Должен ли я возвращать классы POCO из BLL в многоуровневой архитектуре с использованием Entity Framework? (Требуется руководство по архитектуре)

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

Из того, что я собрал, я могу использовать шаблонную штуку VS2010 T4 для создания классов 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 будут взаимодействовать с указанным выше классом BLL и обмениваться экземплярами BookInfo. Все хорошо?

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

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

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

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

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

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

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

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

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

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


person ForeverLearnNeverMaster    schedule 12.04.2011    source источник
comment
Прежде всего, вы должны знать, что на этот вопрос нет правильного ответа - вместо этого существует множество решений, которые работают по-разному. Нелегко выбрать тот, который идеально соответствует вашим потребностям. Вот почему вы получаете информационную перегрузку. Чтобы дать правильный ответ, нам понадобится немного больше информации о приложении, которое вы создаете.   -  person Mark Ewer    schedule 12.04.2011
comment
@Mark: Я вижу, насколько руководство по архитектуре будет субъективным, но как насчет конкретных вопросов, т.е. использование классов POCO на уровне пользовательского интерфейса и реализация IDataErrorInfo в классах POCO? В любом случае постараюсь подробнее рассказать о своем проекте.   -  person ForeverLearnNeverMaster    schedule 12.04.2011
comment
@ForeverLearn: если вы реализуете IDataErrorInfo, ваши классы больше не будут POCO. Вся идея POCO заключается в том, что они не содержат кода, относящегося к сторонним проблемам. Таким образом, если вы используете IDataErrorInfo для облегчения проверки формы, вы больше не имеете дело с POCO. Насколько я понимаю, в MVVM модель - это модель приложения, а не модель домена, поэтому POCO вашего домена не будут привязаны к элементам управления пользовательского интерфейса. Я не знаком с примерами, о которых вы говорите, но я очень надеюсь, что можно добавить свою собственную логику в классы бизнес-логики, иначе это было бы бессмысленно.   -  person Misko    schedule 12.04.2011
comment
@ CodeMonkey1: Ой ... ‹_›. Значит ли это, что мне нужно создать новый набор почти эквивалентных классов в моем BLL? (BookInfoBLL, AuthorBLL и т. Д.)?   -  person ForeverLearnNeverMaster    schedule 12.04.2011
comment
Я не эксперт в этом вопросе, но почти уверен, что с этим не должно быть проблем. Я думаю, что это одна из причин EFCodeFirst (взгляните на это, если вы еще этого не сделали, потому что это должно вам очень помочь)   -  person TBohnen.jnr    schedule 12.04.2011
comment
@ForeverLearn: Написал ответ, потому что у меня заканчивались символы в комментариях.   -  person Misko    schedule 12.04.2011
comment
@ CodeMonkey1: Я не согласен с вашим мнением о IDataErrorInfo. Этот интерфейс представляет собой простой контракт от System.ComponentModel для сообщения о состоянии объекта модели. Он не имеет прямого отношения к пользовательскому интерфейсу, хотя в основном используется в этом контексте. Если вы отказываетесь внедрять его в классы POCO, вам придется заплатить цену дополнительной сложности. Вы не можете оправдать более высокую цену только за счет дизайна и пуризма шаблонов (если, возможно, вы не пишете код как художник).   -  person Slauma    schedule 12.04.2011
comment
@Slauma: Я не говорил, что он не должен этого делать, только то, что это разрушает идею POCO. Проблема с IDataErrorInfo нечеткая, поскольку она реализуется с единственной целью взаимодействия с определенной технологией пользовательского интерфейса, но я могу согласиться с вами, что ее все еще можно назвать POCO. Тем не менее, он по-прежнему смешивает проблемы пользовательского интерфейса с проблемами бизнеса, что не всегда плохо, но не связано с целью создания многоуровневой архитектуры.   -  person Misko    schedule 12.04.2011


Ответы (3)


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

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

person Misko    schedule 12.04.2011
comment
Именно ваш пример привел меня к этому вопросу в первом дворце. Я думал, что мой класс BookSearchResult действительно ничем не отличается от обычного класса BookInfo ... так что в чем смысл создания нового класса, и мне было интересно, могу ли я напрямую использовать класс BookInfo в моей ViewModel (как сгенерированный POCO) . - person ForeverLearnNeverMaster; 12.04.2011
comment
Что ж, я бы предположил, что BookInfo содержит свойства для Title, Author, Publisher, и что последние два из них являются сложными типами со своими собственными наборами свойств. Но действительно ли BookSearchResult заботится обо всех деталях или, возможно, ему просто нужны BookTitle, AuthorName, PublisherName? Это тривиальный пример, но вы можете начать видеть, как BookSearchResult моделирует именно то, что нам нужно для результата поиска, без учета того, что может или не может присутствовать в объекте Book нашего бизнес-уровня. - person Misko; 12.04.2011
comment
Думаю, ты мог быть прав. У меня отображается ВСЕ материалы, связанные с BookInfo, но только имена авторов и имя издателя. И, возможно, также AuthorID, чтобы можно было щелкнуть имя автора и открыть соответствующее представление, отображающее сведения об авторе. НО, например, AddBook (), мне все равно придется передать зеркальное отображение исходного BookInfo ... за исключением реализованного на нем IDataErrorInfo. Хм... - person ForeverLearnNeverMaster; 12.04.2011
comment
Если вы не предвидите в дальнейшем дополнительную сложность, то похоже, что вы могли бы обойтись только с пользовательским интерфейсом и уровнем доступа к данным, а ваши классы сущностей могли бы реализовать любые интерфейсы, которые им нужны для привязки и проверки пользовательского интерфейса. - person Misko; 12.04.2011
comment
Хорошо. Спасибо за дальнейшие мысли. - person ForeverLearnNeverMaster; 12.04.2011

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

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

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

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

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

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

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

person Joel C    schedule 12.04.2011
comment
Спасибо за предложения. Я их немного пережевываю. - person ForeverLearnNeverMaster; 12.04.2011

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

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

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

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

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

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

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

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

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

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

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

person Slauma    schedule 12.04.2011
comment
Спасибо. Много весомых моментов, особенно сценарий отмены. - person ForeverLearnNeverMaster; 12.04.2011