目前我知道,对于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文件夹并在其中放置这种功能,但我觉得在控制器中实例化新对象,然后从该新类调用函数。除非这不是一件坏事吗?
非常感谢任何帮助:)
答案 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,则应确保应用程序仍然很小。