2016-02-19 5 views
3

Я попытаюсь описать проблему, которую я вижу очень часто на своем рабочем месте, но я не мог найти способ или разумное решение. Я много искал об этом, и все, что я смог найти, это то, что я уже реализовал. Сценарий таков:ASP.NET MVC + Entity Framework - Как работать с DTO и ViewModel's

У меня есть приложение ASP.NET MVC, использующее Entity Framework, которое следует за шаблоном репозитория. Я буду использовать простой студент/структуру базы данных Преподаватель иллюстрировать

Entities

public class Student : BaseEntity 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public DateTime DateOfBirth { get; set; } 
    public Teacher Tutor { get; set; } 
} 

public class Teacher : BaseEntity 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public ICollection<Student> Students { get; set; } 
} 

public class BaseEntity 
{ 
    public byte State { get; set; } 
    public Datetime CreateDate { get; set; } 
    public Datetime UpdateDate { get; set; } 
} 

Теперь, скажем, мне нужно выставить метод возвращает список имен студентов вместе с именем своего учителя:

var students = context.Students.Include(x => x.Teacher).ToList(); 

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

var students = context.Students.Include(x => x.Teacher) 
           .Select(x => new 
           { 
            Name = x.Name, 
            TeacherName = x.Teacher.Name 
           }).ToList(); 

У меня будет запрос на выполнение, который выбирает только те поля, которые я хочу. Это хорошо. Но теперь мне нужно сделать выбор. Чтобы вернуть этот список моему контроллеру, я могу либо: создать StudentDTO, либо заполнить экземпляр Student только столбцами, которые я выбрал в своем запросе.

DTO подход

Если я создаю DTO и передать его на мой контроллер все будет «отлично». Не беспокойтесь о лишних ненужных полях.

Вернуться EF Entity Student

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

Проблема:

Смотрите рисунок? В любом случае, я закончил создание класса для отображения моего возвращения. Я в порядке с этим большую часть времени, но в проекте, над которым я сейчас работаю, у нас есть несколько способов разоблачения, и каждый из них имеет другую структуру возврата. Например, что, если я хочу вернуть список студентов, который содержит только идентификатор и имя ученика? Создать еще один DTO или ViewModel?

В проекте есть команда из 5 разработчиков, а папка ViewModel становится наполненной классами. Я начал инструктировать, чтобы они возвращали анонимный объект внутри контроллера, когда возвращаемый метод очень специфичен. Я только создаю Модели для просмотра сейчас, когда я уверен, что их можно повторно использовать в другом месте.

Но этот анонимный подход стал также плохим, потому что теперь я должен делать это в нескольких контроллерах, и я чувствую, что повторяюсь.

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

+1

Почему подход DTO с пустыми полями не в порядке? –

+0

С DTO у меня не будет пустых полей. Если я верну свой объект базы данных, я получу пустые поля. См. Базовый класс? Мне не нужно государство и другие вещи на моей стороне клиента. Проблема в том, что я считаю очень неэффективным продолжать создавать новые классы (DTO или ViewModel) каждый раз, когда мне нужно возвращать данные по-другому. – jpgrassi

ответ

0

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

+0

Это имеет смысл, но не совсем отвечает на мой вопрос. Реальная проблема заключается в том, что я создаю много ViewModels и считаю это очень неэффективным. То, как это происходит, у меня будет 100 файлов менее чем за месяц. Не очень элегантный. – jpgrassi

+0

Вы упомянули, что вы делаете много небольших ViewModels, таких как Id, Some value. Что в этих случаях возвращает IDictionary как модель вместо определения конкретной модели для каждого сценария. Это устранит все те классы Key, Value, которые вы создаете. – Fran

+0

Это был только пример. Мой реальный проект, как я уже говорил, в большинстве случаев представляет собой сложные запросы и результаты. У меня нет моделей просмотра с менее чем 5 полями. – jpgrassi

1

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

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

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

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

используя свой первый пример:

var students = context.Students.Include(x => x.Teacher).ToList(); 

Вы бы затем скопировать студентов в DTO объект (возможно, StudentDTO), а затем передать это до вашего UX и перевести его в объект ViewModel. Если Student и StudentDTO идентичны, то они могут наследовать один и тот же базовый класс, а StudentDTO создается примерно с одной строкой кода.

Использование второго примера

var students = context.Students.Include(x => x.Teacher) 
           .Select(x => new 
           { 
            Name = x.Name, 
            TeacherName = x.Teacher.Name 
           }).ToList(); 

Если вы не сделаете этого, и вместо того, чтобы перевести все в ViewModel, то вы будете в конечном итоге с бесконечным ViewModels, как у вас есть.

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

Если вы перемещаете свои модели в UX, вы обнаружите, что вы не сможете переместить объекты EF в свои модели просмотра, и вместо этого вы использовали бы подход архитектуры DTO.

Так почему же вы просто не проходите модели EF до UX?

Ну, вы можете. Хорошо работает в простых сценариях, когда вы просто редактируете одного ученика, например. Это заманчиво, потому что его так легко сделать, и он работает так, почему бы не сделать это?

Для очень простых небольших систем, пожалуйста, сделайте это. Думаю, все в порядке. вы можете передать модель EF до UX, перевести ее в viewmodel, а затем в viewmodel обратно в EF. Для этого ваши модели EF должны сидеть вне ваших данных и ux-слоев в собственном слое. Обычно я использую dll, называемую «MySoftwaremName.Common». Я размещаю все интерфейсы, модели данных и dtos в этом слое и ссылаюсь на этот слой из уровней UX, Service и Data, чтобы DTO и интерфейсы могли легко передаваться вверх и вниз по архитектуре.

Мой основной опыт в реальном мире показал, что основной причиной, по которой вы не проходите EF-модели непосредственно до UX, является кеширование.

В небольших системах разработчики могут не иметь дело с этим, так что это может работать нормально. Но в больших системах, где мы получаем от 50 000 до 100 000 раз, второе ключевое ключевое значение имеет кеширование.

Что делать, если вы хотите кэшировать объект-ученик, чтобы его можно было найти в течение следующих 5 минут, если вы не нажмете db снова и снова.

Вы не можете кэшировать EF-модели в ASP.NET, иначе вы столкнетесь с серьезными проблемами, которые происходят случайно в вашей системе, которые трудно отслеживать.

Вы можете вставлять объекты EF в кеш и даже извлекать их из кеша. Однако объекты EF привязаны к объектам контекста базы данных, и этот контекст в конечном итоге выходит за пределы области видимости и получает мусор, собранный .NET. Если у вас все еще есть объект Student в кеше после этого, и вы его извлечете и попытаетесь использовать его (используя свой собственный пример снова), чтобы получить подкласс Учителя «Student.Teacher»

вы получите желтый экран смерти если этот объект Учителя не загружен и ленивый загружен EF. Ef попытается использовать контекст, чтобы получить этого Учителя, а его нет, и он взорвется.

Чтобы предотвратить все это, лучше переместить объекты EF в DTO, а затем вы можете безопасно переместить эти данные, кешировать их и т. Д., Не беспокоясь.

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

+0

Вот что я сейчас делаю сейчас ... но я отошел от EF к MongoDb. Хороший ответ. – jpgrassi

 Смежные вопросы

  • Нет связанных вопросов^_^