• Microsoft .NET
  • ASP.NET
  • Руководство по Catharsis - часть 3: быстрая разработка приложений

Руководство по Catharsis - часть 3: быстрая разработка приложений

ОГЛАВЛЕНИЕ

Пошаговое руководство по созданию надежного корпоративного многоуровневого веб-приложения ASP.NET архитектуры модель-вид-контроллер с использованием Catharsis для автоматического создания инфраструктуры кода.

Catharsis – это мощный инструмент RAD (быстрая разработка приложений), созданный на базе надежной архитектуры ASP.NET MVC (модель-вид-контроллер) и NHibernate. Лучшие шаблоны проектирования и разделение задач между уровнями были ключевыми факторами разработки каркаса приложений Catharsis.

Используя руководство, каркас предлагает функциональные возможности RAD на базе надежной архитектуры корпоративного уровня путем автоматического создания большей части кода, необходимого для объектов вашего веб-приложения. Во многих случаях требуется только лишь заполнить несколько простых диалоговых экранов.

Эта статья объясняет, как можно быстро создать приложение для создания, чтения, обновления и удаления объектов (C.R.U.D.). Руководство Catharsis автоматически создает многоуровневую архитектуру и добавляет основную инфраструктуру классов и интерфейсов, которая будет работать без большого объема дополнительного программирования. Статья основана на предыдущей статье из этой серии, в которой рассматривался пример проекта Catharsis. В этой статье будет добавлен новый объект к тому примеру проекта, который доступен для скачивания. Данная информация поможет быстро создать ваше собственное приложение CRUD (создавать, читать, обновлять, удалять).

Помимо создания простых объектов, в этой статье объясняется, как использовать каркас приложений для кодирования ссылок между объектами, например, когда один объект используется в качестве типа объекта в другом объекте. В конце описывается, как добавлять бизнес-правила в приложение.

Если у вас есть база данных, и вы хотите быстро создать надежное веб-приложение корпоративного уровня для доступа к этой базе данных, Catharsis предлагает лучший способ для достижения этого. В отличие от многих каркасов приложений, Catharsis был написан с использованием открытых и защищенных методов, что делает его полностью расширяемым. Программист может получать контроль над своим приложением и заменять методы, когда нужно добавить новые функциональные возможности. Это не требуется при создании большинства приложений Catharsis, но для приложений корпоративного уровня полезно знать о наличии такой возможности при необходимости.

Перед чтением этой статьи прочитайте первую часть руководства по установке Catharsis, доступную по данной ссылке:

http://www.cyberguru.ru/microsoft-net/asp-net/aspnet-catharsis-tutorial-p01.html

Пример мощного решения на основе каркаса приложений Catharsis доступен для скачивания по ссылке:

http://catharsis.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=28510

Вторая статья в этой серии рассматривает пример решения и объясняет его устройство, ознакомьтесь с ней перед продолжением чтения статья, (Руководство 02 по Catharsis):

http://www.cyberguru.ru/microsoft-net/asp-net/aspnet-catharsis-tutorial-p02.html

Чтобы лучше изучить Catharsis, установите демонстрационное решение и следуйте пошаговому руководству в этой статье для добавления нового объекта в конкретное решение. Решение содержит объекты под названиями Agent (агент) и AgentContract (контракт агента), а также будет добавлена еще одна сущность под названием Client (клиент).   


Автоматическое создание сетевой инфраструктуры

База данных, создающая сценарий для решения Firm.SmartControls, в действительности содержит 2 таблицы, соответствие между которыми еще не установлено, поэтому одна из них будет использоваться в качестве примера того, как добавлять новый объект.



‘Клиент’ будет новым объектом. (Он будет добавлен в пространство имен Страхование).

Перед добавлением нового объекта при помощи Руководства необходимо включить пакет Руководства. Щелкните мышкой по Tools (Инструменты) -> Guidance Package Manager (диспетчер пакета руководства)



Щелкните мышкой по Включить/выключить пакеты в появившемся диалоговом окне, выберите ProjectBase.Guidance (руководство) и нажмите на OK.



Закройте следующие два диалоговых окна, которые появятся, так как пока они не нужны.

Учтите, что если вам нужно добавить полную сетевую инфраструктуру, лучше всего ее добавить с помощью Объекта (или Сетевого проекта). Также можно ее добавить в DataLayer (слой данных), но он не позволяет добавлять элементы графического пользовательского интерфейса, которые требуются в этом экземпляре.

Каталог, в который добавляется новый объект, станет частью пространства имен для этого нового объекта. Если нужно, чтобы объект был в новом пространстве имен, вы должны создать каталог в проекте объекта и добавить объект в этот каталог, или же вы можете добавить объект в существующий каталог, как будет сделано в данном случае, так как нужно, чтобы новый объект был в пространстве имен Страхование.

Щелкните правой кнопкой мыши по папке и выберите "(Вновь) создать полную сетевую инфраструктуру" из меню. ‘(Вновь)’ означает, что если объект уже существует в каталоге, файлы будут перезаписаны новыми пустыми классами скелета, что позволяет отменить код или исправить ошибки.

"Полный сетевой" означает, что файлы классов скелета будут добавлены в каждый проект (даже в тестовые модули).
Если вы выберете "(Вновь) создать проект - (объект, организация, стандартный, тесты)" – никакие файлы в моделях, контроллерах и веб-проектах не будут добавлены (или изменены). Это подходит для случаев, когда не нужны элементы графического пользовательского интерфейса.

Пространство имен нового объекта должно быть Firm.SmartControls.Entity.Insurance.Client, поэтому необходимо щелкнуть по каталогу Страхование, как показано на рисунке, чтобы создать сетевую инфраструктуру при помощи Руководства.



Здесь показано главное диалоговое окно, которое нужно заполнить. Заполнение как можно большего количества информации в нем сократит объем работы, которую придется делать впоследствии.



Введите имя нового объекта в диалоговом окне. Вы можете добавить до трех дополнительных свойств. В SQL Server 2005 были столбцы таблицы InsuranceClient (клиент страхования), поэтому можно добавить первые три, Code (код), Name (имя) и BankCode (код банка). Руководство автоматически выполнит проверки с целью обеспечения того, что код уникален (эту функцию можно удалить, если она не нужна). Добавление свойств здесь сокращает объем работы, которую придется выполнять впоследствии, но можно добавить только типы значений – например, строковое свойство ‘имя’, нельзя добавлять объектные типы – например, внешний ключ, ссылающийся на другую таблицу, такую как Country (страна).

Предоставляется пространство имен, так как оно определяется тем, где вы добавили объект в решение. Тип объекта в этом случае должен быть постоянный. Это создаст скелет для объекта организации, который не имеет предков для типа организации (он является производным непосредственно от постоянного объекта). Другие типы позволяют повторно использовать ранее реализованную функциональность.

Второй и третий варианты для Списков кодов – вы можете выбрать ‘простой’ или ‘отдельный объект’. Сначала необходимо пояснить, что такое Список кодов. Объекты списка кодов часто используются для заполнения комбинированных списков, позволяющих пользователю выбирать один вариант из набора заранее заданных вариантов. Таким образом, к примеру,  можно отобразить все страны в Евросоюзе, другой хороший пример – женский и мужской пол. Еще одно общее свойство объектов списка кодов заключается в том, что данные статические, и поэтому не возникнет необходимости добавлять или удалять объекты данного типа. Например, пол никогда не потребует больше вариантов, чем мужской и женский.

Базовые классы дают объектам Списка кодов код и имя, поэтому для пола имя может быть «мужской» и код может быть M. Это простые объекты, так как не требуется никакая дополнительная информация. Поэтому использование варианта для простого списка кодов подходит для чего-то вроде пола или страны. Если вам нужен простой объект, такой как пол, можно использовать вариант ICodeList. В этом случае вам не нужно ничего реализовывать, ваше новое свойство ICodeList будет работать сразу же, без какого-либо написания дополнительного кода.

Каркас также дает возможность создавать список кодов и позволяет расширять объект за счет дополнительной информации. Например, валюта может иметь объект с именем «доллар» и кодом ‘USD’, но может потребоваться добавить столбец для субъединиц и дать ему значение ‘центы’ или ‘c’. Если нужно расширить базовую функциональность списка кодов, можно использовать отдельный объект. В этом случае столбец в таблице базы данных должен хранить значение для субъединицы.

Отслеживаемый тип объекта такой же, как и постоянный тип, но создается дополнительный код, позволяющий вести контрольный журнал для объекта. Если вам нужно отслеживать, когда объект изменяется, кто изменил его, и в каком состоянии он находится, это лучший вариант.  



Нажмите «Завершить», и спустя некоторое время все файлы будут автоматически созданы, и появится всплывающее окно, которое сообщит о том, что вы должны делать дальше:



1) Следуйте этим указаниям и откройте файл Str.Controller.cs и добавьте выделенную строку:



2) Теперь откроется файл menu.config (меню.настройки)



Нужно добавить выделенный код:

Он добавляется на том же уровне, что и Агент, поэтому он появится в качестве брата или сестры этого узла в дереве Навигации.


 
Попытка использования этого нового элемента в меню навигации вызовет ошибку, так как таблица базы данных еще не была назначена через NHibernate:


Назначение таблицы базы данных объекту

Назначаемая таблица выглядит так:

 

Откройте файл NHibernate, автоматически созданный для этого объекта.

Firm.SmartControls.Data.Insurance.Client.hbm.xml

Данные, указанные вами во время создания объекта, уже добавлены:

<?xml version='1.0' encoding='utf-8' ?>
<hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'
    namespace='Firm.SmartControls.Entity.Insurance' assembly='Firm.SmartControls.Entity'>
  <class name='Client' table='Client' lazy='true' >
      <id name='ID' column='ClientId' >
        <generator class='native'></generator>
      </id>
    <property not-null='true' name='Code' />
    <property not-null='true' name='Name' />
    <property not-null='true' name='BankCode' />
  </class>
</hibernate-mapping>

Нужно изменить следующие разделы:
Имя таблицы – InsuranceClient (клиент страхования), а не клиент.
Столбец идентификатора - InsuranceClientId, а не идентификатор клиента.
CountryId (идентификатор страны) и GenderId (идентификатор пола) – списки кодов, требующие установления соответствий многие к одному.

Ниже приводится законченный вариант:

<?xml version='1.0' encoding='utf-8' ?>
<hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'
    namespace='Firm.SmartControls.Entity.Insurance' assembly='Firm.SmartControls.Entity'>
  <class name='Client' table='InsuranceClient' lazy='true' >
      <id name='ID' column='InsuranceClientId' >
        <generator class='native'></generator>
      </id>
    <property not-null='true' name='Code' />
    <property not-null='true' name='Name' />
    <property not-null='true' name='BankCode' />

    <many-to-one name='Country' column='CountryId' lazy='false' ></many-to-one>
    <many-to-one name='Gender' column='GenderId' lazy='false' ></many-to-one>
   
  </class>
</hibernate-mapping>

Можно увидеть ссылку на Firm.SmartControls.Entity.Insurance в вышеуказанном файле, поэтому ее нужно будет изменить с целью отражения изменений, сделанных в файле соответствий. DAO (объект доступа к данным) также нужно будет изменить, но перед этим необходимо добавить в файл объекта свойства, которые не были автоматически созданы руководством.

Откройте файл объекта Клиент:


 
Имеются три свойства – Code (код), Name (имя) и BankCode (код банка). Будут добавлены Gender (пол) и Country (страна). Это объекты списка кодов, поэтому нужно добавить директиву using (использование) для Firm.SmartControls.Entity.CodeLists, чтобы типы данных Gender и Country были распознаны. Код, который нужно добавить, написан полужирным шрифтом.

using System;                            // ===================================
using System.Collections.Generic;        // Код, созданный Руководством © Catharsis
using System.Linq;                       // ===================================

using ProjectBase.Core;
using ProjectBase.Core.PersistentObjects;
using ProjectBase.Core.Collections;
using Firm.SmartControls.Entity.CodeLists;

namespace Firm.SmartControls.Entity.Insurance
{
    /// <summary>
    /// Объект Клиент.
    /// </summary>
    [Serializable]
    public class Client : Persistent
    {
        public virtual string Code { get; set; }
        public virtual string Name { get; set; }
        public virtual string BankCode { get; set; }

        /// <summary>
        /// список кодов
        /// </summary>
        public virtual Gender Gender { get; set; }
        /// <summary>
        /// список кодов
        /// </summary>
        public virtual Country Country { get; set; }

Теперь эти дополнительные поля добавляются в DAO

(Firm.SmartControls.Data.Insurance.ClientDao)

Только что добавленные Gender и Country должны быть доступны в «интеллектуальном восприятии» после добавления двух новых элементов, так как они теперь являются свойствами объекта Client (клиент).

Выполнение приложения с новым объектом

После выполнения необходимых изменений в файле NHibernate, объекта и Dao элемент меню Клиент будет работать.



Разумеется, в базе данных еще нет клиентов, поэтому придется их добавить. Следующий шаг – увеличение функциональных возможностей кнопки ‘Новый’, позволяющее добавлять новых клиентов.


Добавление нового свойства

Если сейчас нажать на кнопку ‘Новый’, то можно увидеть, что свойства, заданные во время настройки руководства (Code, Name и BankCode), добавляются автоматически.



Теперь будут добавлены свойства Gender и Country.

Откройте файл ClientDetailsWC.ascx (Аббревиатура WC означает сетевой контроль)



Этот файл показывает разметку HTML, использованную для создания показанной выше страницы. Будет уменьшен размер двух столбцов (обозначение и описание), добавлен третий столбец для списков кодов, и будут добавлены списки кодов для Gender и Country.

Каждая строка в HTML содержит некоторое количество наборов полей. На данный момент есть один набор полей для обозначения и один для описания. Ширина в процентах этих двух наборов будет уменьшена до 32%, поэтому в строке будет достаточно места для трех наборов полей.

<div class='newRow mh50'>

    <fieldset class='newDetail w32p'>

w32p обозначает класс CSS для ширины. Изучить эти классы CSS можно в следующем файле:



Стиль css .w32p { width: 32%; } будет применяться в данном случае.

Теперь можно добавить третий набор полей для двух списков кодов, код приведен ниже:

 <fieldset class='newDetail w32p'>  
      <legend><%= GetLocalized(Str.Business.CodeLists.CodeList)%></legend>
      <div class='fieldset'>
      
        <div class='inputWC inputWC60 w100p'>
          <div class='label'><%= GetLocalized(Str.Controllers.Country)%></div>
          <div class='input'><% Country.SetEntityAsDataSource(Model.Item.Country); %>
            <smart:AsyncComboBoxWC ID='Country' runat="'server'" TargetController='Country' /> </div>
        </div>

        <div class='inputWC inputWC60 w100p'>
          <div class='label'><%= GetLocalized(Str.Controllers.Gender)%></div>
          <div class='input'><% Gender.SetEntityAsDataSource(Model.Item.Gender); %>
            <smart:AsyncComboBoxWC ID='Gender' runat="'server'" TargetController='Gender' /> </div>
        </div>
    </div>
</fieldset>

Этот код использует модель для доступа к элементу (объекту) и его свойствам. Теперь можно увидеть, что два новых выпадающих списка были добавлены и заполнены данными, нужными нам.



Попытка по-настоящему добавить нового клиента провалится.



Это происходит из-за того, что при нажатии кнопки добавления контроллер пытается добавить объект, но не может сделать это, так как он еще не может обрабатывать свойства объектного типа. Нужно исследовать контроллер на наличие нового объекта, который был автоматически создан в следующем месте:



Имеется много областей, в которые можно добавить код, большинство из них пустые в только что созданном файле контроллера.



Полезно знать, что удержание нажатой кнопки CTRL и набор на клавиатуре m o развернет все области, аналогично CTRL m l свернет все области.

 

Члены:     Используется для хранения локальных переменных, используемых в этом классе.

OnList:         Эта область содержит переопределенные методы, которые можно использовать для расширения функциональности для создания списка объектов для отображения его в виде списка в приложении и для экспорта этого списка в электронную таблицу Excel.

OnAfter:       Эта область содержит несколько переопределенных методов, используемых для выполнения задач после того, как произойдут конкретные события. OnAfterBindModel и OnAfterBindSearch реализованы в контроллере по умолчанию. Эти два метода используются для обработки свойств объектного типа, поэтому их можно использовать для добавления страны и пола.

OnBefore:     Как и для вышеупомянутой области, можно переопределить несколько методов из базовых классов в этом разделе. Можно увидеть список доступных методов, набрав на клавиатуре ‘переопределить OnBefore’, и после этого «интеллектуальное восприятие» покажет вам доступные методы.



Действия:       Эту область можно использовать для переопределения методов, относящихся к действиям, но если это не нужно, то область можно просто удалить. Важно понимать, что большая часть автоматически создаваемого кода может быть не нужна для ваших конкретных нужд, и поэтому может быть удалена. Проще удалить ненужный код, чем писать недостающий код.

Очистить поиск:  Используется для удаления параметров из объекта поиска после его использования.

Свойства:     Эта область содержит метод, возвращающий имя текущего контроллера, который в данном случае будет ClientController (контроллер клиента). Она также используется для загрузки объектов, которые могут понадобиться, если они являются типами объектов в нашем объекте. Это будет иметь место в нашем примере, так как присутствует два типа объектов. Эти объекты используют метод «ленивой загрузки», то есть они создаются только при наличии потребности в них, что повышает производительность в приложении.

Будут внесены необходимые изменения, позволяющие сохранять нового клиента. Необходимо добавить два метода в область OnAfter для обработки типов объектов

/// <summary>
/// Связывает свойства нечислового типа с элементом
/// </summary>
/// <returns></returns>
protected override bool OnAfterBindModel()
{
    var success = base.OnAfterBindModel();
    int id = 0;
    // Country
    if (Request.Form.AllKeys.Contains(Str.Controllers.Country)
     && int.TryParse(Request.Form[Str.Controllers.Country], out id))
    {
        Model.Item.Country = CountryFacade.GetById(id);
    }
    // Пол
    if (Request.Form.AllKeys.Contains(Str.Controllers.Gender)
     && int.TryParse(Request.Form[Str.Controllers.Gender], out id))
    {
        Model.Item.Gender = GenderFacade.GetById(id);
    }
    return success;}
/// <summary>
/// Связывает свойства нечислового типа с поиском
/// </summary>
/// <returns></returns>
protected override bool OnAfterBindSearch()
{
    var success = base.OnAfterBindSearch();
    int id;
    // Country
    if (Request.Form.AllKeys.Contains(Str.Controllers.Country)) // здесь была некоторая выборка
    {
        Model.SearchParam.Example.Country = null;               // обнуление предыдущих нулю (это может быть заключительный этап)
        if (int.TryParse(Request.Form[Str.Controllers.Country], out id))
        {
            Model.SearchParam.Example.Country = CountryFacade.GetById(id);

        }
    }
    // Пол
    if (Request.Form.AllKeys.Contains(Str.Controllers.Gender)) // здесь была некоторая выборка
    {
        Model.SearchParam.Example.Gender = null;               // обнуление предыдущих нулю (это может быть заключительный этап)
        if (int.TryParse(Request.Form[Str.Controllers.Gender], out id))
        {
            Model.SearchParam.Example.Gender = GenderFacade.GetById(id);

        }
    }
    return success;
}

Как можно видеть из кода, выполняется несколько проверок, чтобы убедиться, что значение для Country было предоставлено на форме (в элементе управления ascx) и для обеспечения того, что переданное значение целочисленное. Затем вызывается CountryFacade, чтобы найти страну, имеющую идентификатор, который был отправлен из формы, и затем возращается объект Country и добавляется в объект Клиент.

Также нужно добавить несколько свойств в область свойств:

 public override string ControllerName { get { return Str.Controllers.Client; } }
/// <summary>
/// Позволяет «лениво» (или при помощи контроллера ввода-вывода) работать со Страной
/// </summary>
public virtual ICountryFacade CountryFacade
{
    protected get
    {
        if (_countryFacade.IsNull())
        {
            _countryFacade = FacadeFactory.CreateFacade<ICountryFacade>(Model.Messages);
        }
        return _countryFacade;
    }
    set
    {
        Check.Require(value.Is(), " ICountryFacade не может быть нулевым");
        _countryFacade = value;
    }
}
/// <summary>
/// Позволяет «лениво» (или при помощи контроллера ввода-вывода) работать с Полом
/// </summary>
public virtual IGenderFacade GenderFacade
{
    protected get
    {
        if (_genderFacade.IsNull())
        {
            _genderFacade = FacadeFactory.CreateFacade<IGenderFacade>(Model.Messages);
        }
        return _genderFacade;
    }
    set
    {
        Check.Require(value.Is(), " IGenderFacade не может быть нулевым");
        _genderFacade = value;
    }
}

Это обеспечивает фасад для двух типов объектов, которые будут использоваться в вышеуказанных методах OnAfter.

Вышеуказанные методы требуют два локальных члена, и они добавляются в область членов, как показано ниже:

         #region members
        IGenderFacade _genderFacade;
        ICountryFacade _countryFacade;
        #endregion members

Теперь был добавлен весь требуемый код, который позволит добавить новый клиент.



Только что добавленный клиент можно увидеть в списке, появляющемся после нажатия пункта меню «Клиент»:



Учтите, что пол и страна не появятся в списке. Появляются только те свойства объекта Клиент, которые были заданы в диалоговых окнах руководства при создании сетевой инфраструктуры. Как сказано выше, для обработки свойств Gender и Country нужно развернуть область OnList в элементе управления.


Список

В этом разделе в OnList будет добавлен метод в ClientController, чтобы показать пол и страну включенных в список объектов клиентов. Здесь приведен код, контролирующий то, что появляется в списке:

protected override void OnListToDisplay()
{
    Model.ListModel.ItemsToDisplay = Facade.GetBySearch(Model.SearchParam)
        .Select(i => new ItemToDisplay()
        {
            ID = i.ID,
            Description = i.ToDisplay(),
            Items = new List<IHeaderDescription>
            {  
              new HeaderDescription { HeaderName = "Code", Value = i.Code},
              new HeaderDescription { HeaderName = "Name" , Value = i.Name },
              new HeaderDescription { HeaderName = "BankCode" , Value = i.BankCode },
              new HeaderDescription { HeaderName = Str.Common.ID ,
                        Value = i.ID.ToDisplay(), Align = Align.right },
            }
        } as IItemToDisplay);
}

Будет добавлена еще одна строка для отображения кода страны

new HeaderDescription { HeaderName = Str.Controllers.Country, Value = i.Country.Code, SortByObject=Str.Controllers.Country, SortByProperty=Str.Common.Code},

Атрибуты для сортировки по заголовкам столбцов также указаны в этой строке. Вы можете выбрать отображение кода страны, например ‘IR’, или отображение страны, например ‘IR (Ирландия)’. Второе свойство объектного типа – пол – добавляется аналогичным образом.

Важно отметить, что при работе с каркасом приложений Catharsis часто придется заново компоновать все приложение, чтобы увидеть изменения в веб-браузере, когда приложение выполняется в режиме отладки. Причина этого – разделение задач между уровнями каркаса приложений Catharsis. Когда вы вносите некоторые изменения в код (как, например, в ClientController в этом случае) и нажимаете F5 или щелкаете мышью по кнопке отладки, будут обновлены только те файлы (DLL), которые Visual Studio считает необходимым обновить. Потому что отсутствуют ссылки между контроллером и веб-проектом. Это подробно объясняется позже, но помните, что если вы ожидаете увидеть изменения, заново скомпонуйте все решение перед тестированием ваших изменений.


Редактирование

Не нужно писать дополнительный код, чтобы сделать объекты доступными для редактирования. При просмотре объекта в подробном представлении щелкните мышью по кнопке «Редактировать», и текстовые поля станут редактируемыми, измените свойство, которое нужно обновить, и нажмите «Обновить», чтобы сохранить объект.

Поиск 

Функция поиска вызывается путем нажатия на кнопку Поиск.



Поиск, созданный Руководством, по умолчанию обрабатывает свойства, заданные входе настройки Руководства для нового объекта.



HTML и CSS можно скорректировать так, чтобы они удовлетворяли вашим требованиям.

Использование идентификатора, кода и кода банка для поиска очевидно. Число строк, отображаемых на странице поиска, можно задать на странице поиска. Также можно выводить результаты поиска в новом окне.

Сейчас будет добавлен код, требуемый для ведения поиска по свойствам объектного типа, таким как страна и пол. Сначала будут добавлены элементы в элемент управления ascx. Будет добавлен набор полей, содержащий комбинированные списки для двух свойств:

<fieldset class='newDetail w30p'>  
      <legend><%= GetLocalized(Str.Business.CodeLists.CodeList)%></legend>
      <div class='fieldset'>
     
        <div class='inputWC inputWC60 w100p'>
          <div class='label'><%= GetLocalized(Str.Controllers.Country)%></div>
          <div class='input'><% Country.SetEntityAsDataSource(Model.SearchParam.Example.Country); %>
            <smart:AsyncComboBoxWC ID='Country' runat="'server'" TargetController='Country' ComboBoxShowEmpty='true' /> </div>
        </div>
        <div class='inputWC inputWC60 w100p'>
          <div class='label'><%= GetLocalized(Str.Controllers.Gender)%></div>
          <div class='input'><% Gender.SetEntityAsDataSource(Model.SearchParam.Example.Gender).SetComboBoxName(Str.Controllers.Gender) ; %>
            <smart:AsyncComboBoxWC ID='Gender' runat="'server'" TargetController='Gender' ComboBoxShowEmpty='true' /> </div>
        </div>
    </div>
</fieldset>

Это создаст нужные элементы графического пользовательского интерфейса, которые будут заполнены ожидаемыми списками.



Это наделит систему возможностью выполнять поиск по стране и полу.  Также можно расширить функциональные возможности поиска с целью ведения поиска, например, по названию страны. Это описывается в следующем разделе.

 


Бизнес-правила

Большинство приложений требует применения некоторых бизнес-правил при работе с объектами. Например, если есть клиент, имеющий ‘Германия’ в качестве Country, нельзя позволять системе удалять страну Германия из списка доступных стран. Это может привести к ситуации, при которой объект использует объект, больше не существующий в системе. Это похоже на целостность данных по внешнему ключу на уровне базы данных. Мы не поручаем базе данных слежение за этим, поскольку более эффективно обрабатывать такие ситуации в коде, и ниже показано, как это делается.

Бизнес-правила применяются на бизнес-фасаде, который можно найти в показанном здесь месте:



Чтобы обеспечить соблюдение бизнес-правила, запрещающего удалять страну, если она используется клиентом, необходимо заставить CountryFacade спрашивать ClientFacade, используют ли какие-либо клиенты страну, которую пытаются удалить.
Это включает четыре шага.

  1. Метод ‘CheckDelete (проверка удаления)’ в CountryFacade должен быть переопределен, и перед удалением страны должна выполняться проверка, допустимо ли ее удаление.
  2. Проверка в переопределенном методе CheckDelete должна вызывать другой метод в ClientFacade, чтобы проверить, используется ли страна клиентом.
  3. Интерфейс фасада клиента (IClientfacade) должен быть расширен при помощи метода ‘IsCountryInUse’ (используется ли страна (Country))
  4. Метод IsCountryInUse должен быть реализован в ClientFacade.

Начинаем с открытия CountryFacade и добавления следующего кода:

/// <summary>
/// Нужно проверить, используется ли данный объект!
/// если любой другой объект использует этот экземпляр, удаление останавливается и добавляется сообщение об ошибке
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
protected override bool CheckDelete(Country entity)
{
var result = base.CheckDelete(entity);
      if (ClientFacade.IsCountryInUse(entity))
      {
Messages.AddError(this, Str.Messages.Keys.CannotDeleteItem, Str.Messages.Templates.CannotDelete1
            , entity.ToDisplay());
            result = false;
       }
       return result;
}

Этот метод использует ClientFacade, поэтому нужно добавить локальный член _фасад клиент.

#region members
IClientFacade _clientFacade;
#endregion members

Также нужно свойство для ClientFacade:

 #region properties
/// <summary>
/// Позволяет задавать агента при помощи регистрации
/// </summary>
public virtual IAgentFacade AgentFacade
{
    protected get
    {
        if (_agentFacade.IsNull())
        {
            _agentFacade = FacadeFactory.CreateFacade<IAgentFacade>(Messages);
        }
        return _agentFacade;
    }
    set
    {
        Check.Require(value.Is(), " IAgentFacade cannot be null");
        _agentFacade = value;
    }
}
#endregion properties

Вышеуказанный метод CheckDelete вызывает метод IsCountryInUse и определяет, исходя из результатов этого вызова, можно ли удалить страну.

 IsCountryInUse должен быть добавлен в IClientFacade:
   
/// <summary>
/// Позволяет выполнять проверку перед удалением.
/// Существует ли какой-либо агент, использующий экземпляр объекта в качестве страны
/// </summary>
/// <param name="entity"></param>
/// <returns>истина, если используется</returns>
bool IsCountryInUse(Country entity);

Учтите, что необходимо добавить директиву using (использовать), чтобы интерфейс имел доступ к пространству имен списка кодов, так как ему нужен доступ к объекту Country:

Вышеуказанная директива using также должна быть добавлена в ClientFacade.

using Firm.SmartControls.Entity.CodeLists;

Теперь будет реализован метод IsCountryInUse в ClientFacade.

#region IClientFacade
/// <summary>
/// Выполняет проверку перед тем, как произойдет удаление.
/// Существуют ли какие-либо клиенты, использующие экземпляр объекта в качестве страны
/// </summary>
/// <param name="entity"></param>
/// <returns>истина, если используется</returns>
public virtual bool IsCountryInUse(Country entity)
{
    var item = Dao.GetBySearch(new ClientSearch()
    {
        MaxRowsPerPage = 1,
        Example = new Client()
        {
            Country = entity
        }
    }).FirstOrDefault();

    if (item.Is())
    {
        Messages.AddWarning(this, Str.Messages.Keys.ItemInUse, Str.Messages.Templates.ItemIsUsedForEntity3
            , entity.ToDisplay(), item.ToDisplay(), Str.Controllers.Country);
        return true;
    }
    return false;
}
#endregion IClientFacade

Теперь можно тестировать код. Запустите приложение и щелкните мышью по клиентам, чтобы увидеть список текущих клиентов.


Можно увидеть, что Ирландия используется в качестве страны, поэтому откройте ветвь списков кодов навигационного дерева и щелкните мышью по стране. Можно использовать красный крестик рядом со страной Ирландия, чтобы попытаться удалить ее. Удаление не выполнится из-за бизнес-правила, которое было добавлено. Учтите, что сообщения об ошибках могут быть отформатированы по-разному, в зависимости от вашего желания.



Бизнес-правила можно использовать для контроля над тем, что разрешено во время добавления или обновления объекта.

Вывод 

Теперь вы должны понимать, как добавлять новые объекты в пример решения, связывать их с другими объектами (списками кодов) и добавлять некоторые элементарные бизнес-правила. Используя информацию и указания в первой статье из этой серии, вы должны уметь создавать новую базу данных в SQL Server и быстро разрабатывать веб-приложение CRUD при помощи каркаса приложений Catharsis.

В будущих руководствах в этой серии будет рассмотрен поиск и устранение некоторых проблем, с которыми сталкивались пользователи каркаса Catharsis, будет более глубоко рассмотрено использование Catharsis и создано больше примеров приложений.