对于小型MVC应用程序,如何填充DTO和ViewModel?

时间:2017-04-04 22:34:30

标签: c# asp.net-mvc entity-framework

目前我知道,对于MVC应用程序,View应该填充ViewModels,控制器应该保持苗条,并且你不应该直接将你的Entity Framework实体暴露给控制器。

我遇到困难的地方,就是放置模型的函数(来自数据库)并将其转换为ViewModel数据。

目前我有一个代表数据库中一系列任务的实体。这是班级:

 public class Task
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public Contact Contact { get; set; }
        public string Description { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateModified { get; set; }
        public int? QuotedHours { get; set; }
        public int? UsedHours { get; set; }
        public virtual ICollection<Attachment> Attachments { get; set; }
        public virtual ICollection<Comment> Comments { get; set; }
        public virtual Status Status { get; set; }
        public int RecoveryStatusID { get; set; }
        public virtual RecoveryStatus RecoveryStatus { get; set; }
    }

我有一个看起来完全相同的DTO类,但使用列表而不是虚拟ICollections。它看起来像这样:

  public class Task
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public Contact Contact { get; set; }
        public string Description { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateModified { get; set; }
        public int? QuotedHours { get; set; }
        public int? UsedHours { get; set; }
        public List<Attachment> Attachments { get; set; }
        public List<Comment> Comments { get; set; }
        public virtual Status Status { get; set; }
        public int RecoveryStatusID { get; set; }
        public RecoveryStatus RecoveryStatus { get; set; }
    }

然后我有一个我想填充的视图模型。

  public class TaskIndexViewModel
    {
        public string CategoryName { get; set; }
        public List<DTO.Task> Tasks { get; set; }
    }

所以:

  • 如何将EF实体映射到DTO?我认为它涉及使用Linq Select语句。我非常喜欢使用像AutoMapper这样的东西,但我无法理解它是如何工作的(很多文档已经过时,似乎主要关注旧的静态实现,显然它已经更新为基于实例的实现?)

  • 一旦我在DTO中获得了数据,那么翻译从DTO转移到TaskIndexViewModel类?我知道这将涉及Linq GroupBy语句,还有Select语句将它放入一个新的List中,但这种逻辑应该放在哪里?我想创建一个新的Service文件夹并在其中放置这种功能,但我觉得在控制器中实例化新对象,然后从该新类调用函数。除非这不是一件坏事吗?

非常感谢任何帮助:)

3 个答案:

答案 0 :(得分:2)

查看我发布的这篇博客文章

http://krow.tech/posts/Essentials-of-AutoMapper

(我将在这里模拟一些代码)

var db = new MyEfContext(); // Or however you create or get your ef context IoC maybe?

IEnumerable<Task> tasks = db.Tasks
    .Select(e => Mapper.Map<DTO.Task>(e));

var vm = new TaskIndexViewModel
{
    Tasks = tasks,
    CategoryName = "This Cool Cat"
};

使用此功能,您可以将任务实体映射到任务DTO。您的映射可能看起来像......

config.CreateMap<Task, DTO.Task>()
    .ForMember(dest => dest.Attachments.ToList(), opt => opt.MapFrom(src => src.Attachments))

但是,鉴于您的实体和DTO上的名称相同,您可能最好先写一些TypeResolvers(请参阅上面的链接),以便在将ICollection转换为IList时使用AutoMapper只需告诉AutoMapper:

config.CreateMap<Task, DTO.Task>();

我希望能回答你的问题。正如我们在评论中谈到的那样,我仍然认为映射到指定的DTO是过度的,即使在这种情况下你将实体直接传递给VM(任何人都可以随意纠正我)。我只想这样:

var vm = new TaskIndexViewModel
{
    Tasks = db.Tasks.ToList(),
    CategoryName = "This Cool Cat"
};

如果需要,请参阅上面链接的帖子以获取更多背景信息。

答案 1 :(得分:0)

我不同意@ raykrow的答案的最后一部分。这种结构:

[temp['logs'].append(log) for log in errors['logs']]

...导致var vm = new TaskIndexViewModel { Tasks = db.Tasks.ToList(), CategoryName = "This Cool Cat" }; viewmodel属性与它正在进行Tasks的EF实体紧密绑定。如果您决定从EF切换到另一个ORM,会发生什么?

拥有与域类(甚至)相同的视图模型仍然是有益的。至于如何轻松地将模型中的结果映射到viewmodel,这就是为什么像AutoMapper这样的库存在的原因。没有AutoMapper:

.ToList()

答案 2 :(得分:0)

我使用AutoMapper,这是将很多类映射到一起的一种非常简单方便的方法。

Mapper.CreateMap<Task, DTO.Task>()
  .ForMember(dest => dest.Attachments, opt => opt.MapFrom(src => src.ToList<Attachment>())
  .ForMember(dest => dest.Comments, opt => opt.MapFrom(src => src.ToList<Comment>())
  .ReverseMap()
    .ForMember(dest => dest.Attachments, opt => opt.MapFrom(src => src as ICollection<Attachment>)
    .ForMember(dest => dest.Comments, opt => opt.MapFrom(src => src as ICollection<Comment>);

现在你可以说

var taskDto = Mapper.Map<Task, DTO.Task>(task);

但是反向映射部分也可以让你反过来。

var task = Mapper.Map<DTO.Task, Task>(taskDto);

理想情况下,您需要将映射放入配置文件中。

此外,不创建DTO也没有坏处。如果您决定不创建DTO,那么您将控制器绑定到您的实体,并且在小型应用程序中这可能永远不会成为问题,但如果应用程序不断增长,您可能会发现实现不同的逻辑可能会变得困难,您可能会被迫在控制器中放置一些技术上并不属于那里的东西。因此,如果您决定不使用DTO,则应确保应用程序仍然很小。