Entity Framework в многоуровневых архитектурах - Описание полученных результатов

ОГЛАВЛЕНИЕ

Описание полученных результатов

Теперь я продемонстрирую приложение и рассмотрю его работу, начиная с верхнего слоя, представлений (находятся в проекте NWUI) и презентаторов (находятся в проекте NWPresentation). Оба проекта доступны в материалах для загрузки, прилагаемых к статье. Приложение загружает представление поиска клиентов, позволяющее выполнять поиск клиентов по названию компании (см. рис. 4). Представление реализуется с помощью платформы WPF, и при взаимодействии пользователя с ним создаются события, прослушиваемые презентатором, который может выполнить соответствующее действие.

 

Рис. 4. Поиск клиентов

При поиске пользователем всех клиентов, начиная с буквы D, как показано на рис. 4, представление создает событие при нажатии пользователем кнопки «Search» (Поиск). Презентатор прослушивает это событие и отвечает на него, вызывая слой служб через платформу WCF для получения списка сущностей клиентов, которые будут отображены в представлении CustomerSearchView. Ниже приведен код в представлении при нажатии пользователем кнопки «Search» (Поиск):

private void btnSearch_Click(object sender,
   RoutedEventArgs e) {
    if (FindCustomerSearchResults != null)
     FindCustomerSearchResults();
  }

Этот код не взаимодействует со списком возвращенных сущностей, оставляя эту работу презентатору. Представление использует привязку данных платформы WPF для ссылки на свойства сущности, поэтому ему известно, как осуществляется привязка списка сущностей к элементам элемента управления представления списком. Взаимодействие представлений с сущностями осуществляется только через привязку данных.

Представление CustomerSearchView создает событие FindCustomerSearchResults, а презентатор CustomerSearchPresenter прослушивает событие и отвечает на него, принимая маркер и выполняя поиск. В следующем коде показано создание классом CustomerSearchPresenter экземпляра класса NWServiceClient, являющегося прокси для службы WCF, предоставляемой на нижнем слое:

public void view_FindCustomerSearchResults()
{
   if (this.view.CompanyNameCriteria.Length > 0)
     using (var svc = new NWServiceClient())
     {
       IList<Customer> customerList = svc.FindCustomerList(
        view.CompanyNameCriteria);
       view.CustomerSearchResultsList = customerList;
     }
}

Обращение к NWServiceClient осуществляется с помощью ServiceReference из проекта NWPresentation, поэтому презентаторам известно о способе вызова служб и о типах возвращаемых данных. Слой презентаций не должен и не ссылается на EDM напрямую. Вместо этого он сообщает ожидаемые типы сущностей с помощью атрибутов DataContract, представленных через платформу WCF. Это позволяет передачу сущностей платформы Entity Framework презентаторам через физические границы сети с помощью платформы WCF.

Обратите внимание, что после возврата списка сущностей Customer для него устанавливается общее свойство представления. После этого данное свойство представления принимает List<Customer> и выполняет его привязку к контексту DataContext представления. Презентатор предоставляет данные, передает их, а представление все специфичные для него привязки (поскольку этот код очень специфичен для конкретной технологии, будь то WPF, Silverlight, Windows Forms или ASP.NET).

Этот прием позволяет использовать тот же самый презентатор для взаимодействия с любыми представлениями, реализующими интерфейс ICustomerSearchView. В данном приложении привязка обрабатывается методами привязки WPF с помощью DataContext.

Контракты предоставляют метод, который затем может быть вызван на уровне службы, так же как и возвращенные сущности. В данном приложении используется только метод для возврата типов сущностей Customer и Order. Это означает, что в контракт будут включены только эти типы сущностей.

Платформа WCF выполняет сериализацию сущностей, применяя к ним при необходимости атрибуты DataContract WCF. Благодаря предоставлению сущностей через DataContracts сущности могут использоваться на уровнях пользовательского интерфейса, не ссылаясь непосредственно на EDM.

Обратите внимание, что для бета-версии 1 пакета обновления 1 (SP1) для .NET Framework 3.5 платформа Entity Framework поддерживает автоматическую сериализацию графа. Например, если у родительской сущности имеются соответствующие дочерние сущности, будут сериализованы как родительские, так и дочерние сущности. Поскольку в примере приложения метод FindOrderList OrderManager использует запрос LINQ к Entities, который выполняет упреждающую загрузку объекта Order Details для всех сущностей Order, каждая сущность Order, возвращенная из промежуточного уровня, будет содержать List<OrderDetail>, доступный через свое свойство переходов.

Хотя сериализованные сущности могут быть переданы между презентаторами и слоем служб через платформу WCF, ObjectContext не сериализуется и не передается презентатору. Это означает, что сущности могут использоваться в слоях пользовательского интерфейса, а ObjectContext остается в нижних слоях, где для него будет доступна модель EDM и все ресурсы платформы Entity Framework.

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

При нажатии пользователем кнопки «Search» (Поиск), показанной на рис. 4, презентатор вызывает слой служб, который затем вызывает бизнес-слой (находится в проекте NWBusinessManagers) для получения List<Customer>. Этот слой имеет две основные функции. Первая заключается в получении данных из модели EDM или размещения данных в ней. Вторая состоит в обработке любой существующей бизнес-логики.

Диспетчер CustomerManager использует контекст ObjectContext для взаимодействия с EDM, поэтому он определяет локальное поле с именем «context» и создает его экземпляр в конструкторе. Контекст ObjectContext может быть создан и удален во всех методах. Однако он оптимизирован для открытия и закрытия ресурсов подключения к базе данных в случае такой необходимости. Кроме того, обеспечивая доступность контекста ObjectContext через его класс, он может поддерживать отслеживание изменений без необходимости передачи ряда частных методов внутри класса:

public CustomerManager()
{
   context = new NWEntities();
}

Однако имейте в виду, что для этого типа приложения контекст ObjectContext не должен удерживаться, а должен при необходимости создаваться/удаляться. Из-за разрешения идентификаторов удерживание одного и того же контекста объекта в конечном итоге приведет к несогласованности и неактуальности данных и (при увеличении объема отслеживаемых данных) снижению производительности при разрешении идентификаторов, кроме того, это может привести к проблемам обновления в многопоточных средах.

В следующем коде показан метод FindCustomerList класса CustomerManager в бизнес-уровне. Этот метод объявляет запрос LINQ к Entities, получающий доступ к контексту для запроса списка начинающихся с критерия сущностей Customer. При выполнении этого запроса он вычисляет сопоставления с концептуального слоя до слоя хранилища и создает соответствующую команду SELECT:

public List<Customer> FindCustomerList(string companyName)
{
    var q = from c in context.Customers
        where c.CompanyName.StartsWith(companyName)
        select c;
    return q.ToList();
}

При необходимости можно просмотреть выполнение запросов с помощью профилировщика SQL Server®.