Должен ли я возвращать классы POCO из BLL в многоуровневой архитектуре, использующей Entity Framework?(Необходимо руководство по архитектуре) - PullRequest
8 голосов
/ 12 апреля 2011

Я, наверное, слишком много читаю и страдаю от информационной перегрузки. Поэтому я был бы признателен за некоторые четкие указания.

Из того, что я собрал, я могу использовать шаблон T4 VS2010 для генерации классов POCO, которые не связаны напрямую с EF. Я поместил бы их в их собственный проект, в то время как мой DAL имел бы класс, производный от ObjectContext, верно?

Как только у меня появятся эти классы, допустимо ли использовать их на уровне пользовательского интерфейса? То есть, скажем, один из сгенерированных классов - BookInfo, который содержит информацию о книгах для публичной библиотеки (название, издание, страницы, резюме и т. Д.).

Мой BLL будет содержать класс BooksBLL, например, так:

public class BooksBLL
{
    ObjectContext _context;

    public void AddBook(BookInfo book) { ... }
    public void DeleteBook(int bookID) { ... }
    public void UpdateBook(int bookID, BookInfo newBook) { ... }

    //Advanced search taking possibly all fields into consideration
    public List<BookInfo> ResolveSearch(Func<BookInfo, bool> filter) { ... }

    //etc...
}

Итак, мои ViewModels в моем приложении MVVM UI будут взаимодействовать с вышеуказанным классом BLL и обмениваться экземплярами BookInfo. Это нормально?

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

Если это имеет какое-либо значение, в настоящее время мое приложение не использует WCF (или какие-либо другие сетевые компоненты).

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

Обновление (дополнительная информация по запросу):

Я пытаюсь создать приложение для автоматизации библиотек. В настоящее время это , а не сеть.

Я думаю о следующих слоях:

  • Проект, состоящий из сгенерированных классов POCO (BookInfo, Автор, Член, Издатель, Контакт и т. Д.)
  • Проект с классом, производным от ObjectContext (DAL?)
  • Уровень бизнес-логики с классами, подобными тем, которые я упоминал выше (BooksBLL, AuthorsBLL и т. Д.)
  • Уровень пользовательского интерфейса WPF с использованием шаблона MVVM. (Отсюда мой подвопрос о реализации IDataErrorInfo).

Итак, я задаюсь вопросом о таких вещах, как использование экземпляра BooksBLL в классе ViewModel, вызов ResolveSearch() для него для получения List<BookInfo> и представление его… то есть использование классов POCO везде.

Или у меня должны быть дополнительные классы, которые отражают классы POCO, выставленные из моего BLL?

Если вам нужна дополнительная информация, пожалуйста, спросите.

Ответы [ 3 ]

4 голосов
/ 12 апреля 2011

То, что вы делаете, - это, в основном, шаблон Repository, для которого отлично подходят Entity Framework и POCO.

Итак, мои ViewModels в моем приложении MVVM UI будут взаимодействовать с вышеуказанным BLLкласс и обмен экземплярами BookInfo.Это нормально?

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

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

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

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

Точно так же подумайте о замене вашего List<BookInfo> интерфейсом, например, IList<BookInfo> или IBindingList<BookInfo> или наименьшим общим знаменателем IEnumerable<BookInfo>.Таким образом, вы не привязаны напрямую к определенному классу List<T>, и если ваши потребности со временем изменятся, что обычно происходит, это уменьшит рефакторинг, необходимый для замены вашего List<BookInfo> чем-то другим, предполагая, что вы заменяетеон реализует выбранный вами интерфейс.

3 голосов
/ 12 апреля 2011

Вам не нужно , чтобы делать что-то конкретное ... как сказал Марк, «правильного» ответа нет.Однако, если ваше приложение достаточно простое, чтобы вы просто дублировали свои классы (например, BookInfoUI & BookInfoBLL), то я бы рекомендовал просто использовать бизнес-классы.Дополнительный слой не будет служить цели, и поэтому он не должен существовать.Эрик Эванс из DDD даже рекомендует поместить всю свою логику на уровень пользовательского интерфейса, если ваше приложение простое и имеет очень мало бизнес-логики.

Чтобы провести различие, уровень приложения должен иметь классы, которые моделируют то, что происходит внутри приложения.и уровень домена должен иметь классы, которые моделируют то, что происходит в домене.Например, если у вас есть страница поиска, ваш уровень пользовательского интерфейса может получить список объектов BookSearchResult из BookSearchService на прикладном уровне, который будет использовать домен для получения списка BookInfo.

2 голосов
/ 12 апреля 2011

Ответы на ваши вопросы могут зависеть от размера и сложности вашего приложения. Поэтому я боюсь, что будут веские аргументы, чтобы ответить на ваши вопросы, а также Да и Нет.

Лично я отвечу на два ваших главных вопроса, оба с Да:

Является ли приемлемой практика использования классов POCO (Domain) на уровне пользовательского интерфейса?

Я предполагаю, что под "слоем пользовательского интерфейса" вы на самом деле имеете в виду не часть View шаблона MVVM, а ViewModels. (Я полагаю, что большинство специалистов MVVM вообще не допустят, чтобы View напрямую ссылался на модель).

Нет ничего необычного в том, чтобы обернуть POCO из вашего проекта Domain в качестве свойства в ViewModel и привязать этот обернутый POCO непосредственно к View. Большой профессионал: это легко. Вам не нужны дополнительные классы ViewModel или реплицированные свойства в ViewModel, а затем скопируйте эти свойства между объектами.

Однако, если вы используете WPF, вы должны принять во внимание, что механизм привязки будет напрямую записывать ваши свойства POCO, если вы связываете их с View. Это может не всегда быть тем, что вам нужно, особенно если вы работаете с присоединенными и отслеживаемыми изменениями сущностями в форме WPF. Вам нужно подумать о сценариях отмены или о том, как восстановить свойства после отмены, которые были изменены механизмом привязки.

В моем текущем проекте я работаю с отсоединенными сущностями: я загружаю POCO из слоя данных, отсоединяю его от контекста, располагаю контекст, а затем работаю с этой копией в ViewModel и связываю ее с View. Обновление на уровне данных происходит путем создания нового контекста, загрузки исходного объекта из БД по ИД и последующего обновления свойств из измененного POCO, который был привязан к представлению. Таким образом, проблема нежелательных изменений прикрепленной сущности исчезает при таком подходе. Но есть и недостатки в работе с отдельными объектами (например, обновление более сложное).

Можно ли реализовать интерфейс IDataErrorInfo в сгенерированном классе POCO?

Если вы связываете свои сущности POCO с View (через упаковочную ViewModel), это не только нормально, но вы даже должны реализовать IDataErrorInfo в классе POCO, если вы хотите использовать встроенный проверка свойств механизма связывания WPF. Хотя этот интерфейс в основном используется вместе с технологиями пользовательского интерфейса, он является частью пространства имен System.ComponentModel и поэтому не связан напрямую с какими-либо пространствами имен пользовательского интерфейса. В основном IDataErrorInfo - это всего лишь простой контракт, который поддерживает сообщение о состоянии объекта, что также может быть полезно вне контекста пользовательского интерфейса.

То же самое относится к интерфейсу INotifyPropertyChanged, который вам также необходимо реализовать в ваших классах POCO, если вы связываете их непосредственно с View.

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

...