我有一个带有两个子项目的网络解决方案(在VS2010中):
Domain
包含Model
类(通过实体框架映射到数据库表)和Services
(除了其他东西)负责CRUD操作
WebUI
引用了域项目
对于我创建的第一个页面,我在我的强类型视图中直接使用Domain项目中的Model类作为Model,因为类很小,我想显示和修改所有属性
现在我有一个页面,它只适用于相应域模型的所有属性的一小部分。我通过在Service类中使用查询结果的投影来检索这些属性。但我需要投射到一个类型 - 这里有我能想到的解决方案的问题:
我介绍ViewModels
项目中存在的WebUI
,并将IQueryables
和EF data context
从服务公开给WebUI项目。然后我可以直接投射到那些ViewModels。
如果我不想公开IQueryables和EF数据上下文,我将ViewModel
类放在Domain
项目中,那么我可以直接返回ViewModels作为查询的结果和服务类的预测。
除了ViewModels
项目中的WebUI
之外,我还介绍了Data transfer objects
,它将数据从服务类中的查询移动到ViewModels
。< / p>
解决方案1和2看起来像是相同的工作量,我更倾向于选择解决方案2来将所有数据库问题保存在单独的项目中。但不知何故,在域项目中使用 View -Models听起来是错误的。
解决方案3听起来更像是工作,因为我有更多的类可以创建并关心Model-DTO-ViewModel映射。我也不明白DTO和ViewModels之间会有什么区别。 ViewModels不是我想要显示的Model类的所选属性的集合吗?他们不会包含与DTO相同的成员吗?为什么我要区分ViewModels和DTO?
这三种解决方案中的哪一种更为可取,有哪些优点和缺点?还有其他选择吗?
提前感谢您的反馈!
修改(因为我的文字可能太长了,并且被要求提供代码)
示例:我有一个Customer
实体...
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public City { get; set; }
// ... and many more properties
}
...并希望创建一个仅显示(并且可能允许编辑)列表中Name
个客户的视图。在Service类中,我通过投影提取View所需的数据:
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。这应该怎么样?
这种方式(a)......
public ActionResult Index()
{
List<SomeClass1> list = _service.GetCustomerNameList();
return View(list);
}
...或更好的方式(b):
public ActionResult Index()
{
List<SomeClass1> list = _service.GetCustomerNameList();
List<SomeClass2> newList = CreateNewList(list);
return View(newList);
}
关于上面的选项3,我要说:SomeClass1
(住在Domain
项目中)是 DTO 和SomeClass2
(住在{{} 1}} project)是 ViewModel 。
我想知道区分这两个类是否合理。为什么我不总是为控制器动作选择选项(a)(因为它更容易)?是否有理由在 DTO (WebUI
)之外引入 ViewModel (SomeClass2
)?
答案 0 :(得分:9)
我会通过使用自动映射工具(如AutoMapper)为您完成映射来解决您的问题。在映射很容易的情况下(例如,如果一个类的所有属性都应映射到另一个类上具有相同名称的属性),AutoMapper将能够为您完成所有连接工作,并且您必须给出几行代码,注意两者之间应该有一个映射。
这样,您可以将Domain
中的实体和WebUI
中的几个视图模型类放在某个位置(最好放在WebUI
中,或者放在同一个子命名空间中) )定义它们之间的映射。您的视图模型实际上是 DTO,但您不必担心域和DTO类之间的转换过程。
注意:我强烈建议强烈建议直接将您的域实体提供给MVC Web UI的视图。你不希望EF一直“粘贴”到前端层,以防你以后想要使用EF以外的东西。
答案 1 :(得分:6)
介绍住在里面的ViewModels WebUI项目并公开IQueryables 来自的EF数据上下文 服务到WebUI项目。然后我 可以直接投射到那些 的ViewModels。
问题是你很快就会遇到使用EF试图“压扁”模型的问题。当我有一个CommentViewModel
类时,我遇到了类似的东西:
public class CommentViewModel
{
public string Content { get; set; }
public string DateCreated { get; set; }
}
以下针对CommentViewModel
的EF4查询预测无法用作couldn't translate the ToString() method into 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();
}
答案 2 :(得分:1)
谈论模型,ViewModels和DTO令人困惑,我个人不喜欢使用这些术语。我更喜欢谈论域实体,域名服务,操作输入/结果(又称DTO)。所有这些类型都存在于Domain层中。操作是实体和服务的行为。除非您正在构建纯CRUD应用程序,否则表示层仅处理输入/结果类型,而不是实体。您不需要其他ViewModel类型,这些是ViewModel(换句话说,View的模型)。 View用于将“操作结果”转换为HTML,但相同的Result可以序列化为XML或JSON。您用作ViewModel的是域的一部分,而不是表示层。