Модели, ViewModels, DTO в приложении MVC 3 - PullRequest
16 голосов
/ 13 мая 2011

У меня есть веб-решение (в VS2010) с двумя подпроектами:

  1. Domain, которое содержит классы Model (сопоставленные с таблицами базы данных через Entity Framework) иServices, который (помимо прочего) отвечает за операции CRUD

  2. WebUI, который ссылается на проект домена

Для первых страницЯ создал, что использовал классы Model из проекта Domain непосредственно в качестве Model в моих строго типизированных представлениях, потому что классы были небольшими, и я хотел отобразить и изменить все свойства.

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

  1. Я представляю ViewModels, которые живут в WebUI проецируйте и выставьте IQueryables и EF data context из сервиса в проект WebUI.Затем я могу напрямую проецировать в эти ViewModels.

  2. Если я не хочу показывать IQueryables и контекст данных EF, я помещаю классы ViewModel в проект Domain, затемЯ могу вернуть ViewModels напрямую в результате запросов и проекций классов Service.

  3. В дополнение к ViewModels в проекте WebUI я представляю Data transfer objects, которые перемещаютсяданные из запросов в классах обслуживания в ViewModels.

решения 1 и 2 выглядят одинаково, и я склонен предпочесть решение 2, чтобы сохранить всеБаза данных относится к отдельному проекту.Но почему-то звучит неправильно иметь View -Models в проекте Domain.

Решение 3 звучит как гораздо больше работы, так как у меня есть больше классов для создания и заботы о Model-DTO-Модель ViewView.Я также не понимаю, в чем разница между DTO и ViewModels.Разве ViewModels - это не коллекция выбранных свойств моего класса Model, которые я хочу отобразить?Разве они не будут содержать тех же членов, что и DTO?Почему я хотел бы проводить различие между ViewModels и DTO?

Какое из этих трех решений предпочтительнее и каковы преимущества и недостатки?Есть ли другие варианты?

Спасибо за отзыв заранее!

Редактировать (потому что у меня, возможно, была слишком длинная стена текста и меня попросили ввести код)

Пример: у меня есть Customer Entity ...

public class Customer
{
    public int ID { get; set; }
    public string Name { get; set; }
    public City { get; set; }
    // ... and many more properties
}

... и я хочу создать вид, который показывает только (и, возможно, позволяет редактировать) Name изклиенты в списке.В классе Service я извлекаю данные, необходимые для просмотра, с помощью проекции:

public class CustomerService
{
    public List<SomeClass1> GetCustomerNameList()
    {
        using (var dbContext = new MyDbContext())
        {
            return dbContext.Customers
                .Select(c => new SomeClass1
                             {
                                 ID = c.ID,
                                 Name = c.Name
                             })
                .ToList();
        }
    }
}

Затем существует CustomerController с методом действия.Как это должно выглядеть?

Либо так (а) ...

public ActionResult Index()
{
    List<SomeClass1> list = _service.GetCustomerNameList();
    return View(list);
}

... или лучше так (б):

public ActionResult Index()
{
    List<SomeClass1> list = _service.GetCustomerNameList();

    List<SomeClass2> newList = CreateNewList(list);

    return View(newList);
}

Что касается варианта 3 выше, я бы сказал: SomeClass1 (живет в Domain проекте) - это DTO , а SomeClass2 (живет в WebUI проекте) - ViewModel .

Мне интересно, имеет ли смысл когда-нибудь различать два класса.Почему бы мне не всегда выбирать опцию (а) для действия контроллера (потому что это проще)?Есть ли причины для введения ViewModel (SomeClass2) в дополнение к DTO (SomeClass1)?

Ответы [ 3 ]

9 голосов
/ 13 мая 2011

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

Таким образом, вы можете иметь свои сущности в Domain, и пару классов моделей представлений в вашем WebUI, и где-то (предпочтительно в WebUI или подпространстве имен одного и того же) определять карты между ними. Ваши модели представлений в действительности будут быть DTO, но вам не придется сильно беспокоиться о процессе преобразования между доменом и вашими классами DTO.

Примечание: я бы настоятельно рекомендовал не предоставлять объектам вашего домена право на просмотр вашего веб-интерфейса MVC. Вы не хотите, чтобы EF «держался» на всем протяжении до входного слоя, на случай, если позже вы захотите использовать что-то отличное от EF.

6 голосов
/ 14 мая 2011

представляет ViewModels, которые живут в проекте WebUI и предоставляют IQueryables и контекст данных EF из сервиса в проект WebUI.Тогда я мог бы непосредственно проецировать на эти ViewModels.

Проблема в том, что вы скоро столкнетесь с проблемами при использовании EF, пытаясь «сгладить» модели.Я столкнулся с чем-то похожим, когда у меня был класс CommentViewModel, который выглядел так:

public class CommentViewModel
{
    public string Content { get; set; }
    public string DateCreated { get; set; }
}

Следующая проекция запроса EF4 на CommentViewModel не будет работать, так как не может перевестиМетод ToString () в SQL :

var comments = from c in DbSet where c.PostId == postId 
               select new CommentViewModel() 
               { 
                   Content = c.Content,
                   DateCreated = c.DateCreated.ToShortTimeString() 
               };

Использование чего-то вроде Automapper - хороший выбор, особенно если вам нужно сделать много преобразований.Однако вы также можете создавать свои собственные конвертеры, которые в основном преобразуют модель вашего домена в вашу модель представления.В моем случае я создал свои собственные методы расширения для преобразования моей модели домена Comment в мою CommentViewModel следующим образом:

public static class ViewModelConverters
{
    public static CommentViewModel ToCommentViewModel(this Comment comment)
    {
        return new CommentViewModel() 
        { 
            Content = comment.Content,
            DateCreated = comment.DateCreated.ToShortDateString() 
        };
    }

    public static IEnumerable<CommentViewModel> ToCommentViewModelList(this IEnumerable<Comment> comments)
    {
        List<CommentViewModel> commentModels = new List<CommentViewModel>(comments.Count());

        foreach (var c in comments)
        {
            commentModels.Add(c.ToCommentViewModel());
        }

        return commentModels;
    }
}

По сути, я выполняю стандартный запрос EF, чтобы вернуть модель доменаа затем использовать методы расширения для преобразования результатов в модель представления.Например, следующие методы иллюстрируют использование:

public Comment GetComment(int commentId)
{
    return CommentRepository.GetById(commentId);
}

public CommentViewModel GetCommentViewModel(int commentId)
{
    return CommentRepository.GetById(commentId).ToCommentViewModel();
}

public IEnumerable<Comment> GetCommentsForPost(int postId)
{
    return CommentRepository.GetCommentsForPost(postId);
}

public IEnumerable<CommentViewModel> GetCommentViewModelsForPost(int postId)
{
    return CommentRepository.GetCommentsForPost(postId).ToCommentViewModelList();
}
1 голос
/ 13 мая 2011

Разговоры о моделях, ViewModels и DTO вводят в заблуждение, лично я не люблю использовать эти термины.Я предпочитаю говорить о доменных объектах , доменных службах , Операции ввода / результата (он же DTO).Все эти типы живут в доменном слое.Операции - это поведение сущностей и услуг.Если вы не создаете чистое приложение CRUD, уровень представления имеет дело только с типами ввода / результата, а не с сущностями.Вам не нужны дополнительные типы ViewModel, это ViewModels (другими словами, модель представления).Представление предназначено для перевода результатов операции в HTML, но тот же результат может быть сериализован как XML или JSON.То, что вы используете в качестве ViewModel, является частью домена, а не уровня представления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...