我想知道一种在没有Model
或类似内容的情况下将ViewModel
转换为ViewModel
并将Model
转换为AutoMapper
的好方法,因为我想了解背后的原因并自己学习如何做。当然,我所说的Model是指EF生成的类。
到目前为止,我做了类似的事情,但是当涉及到嵌套类时会遇到一些问题:
// to VM
public static Author ToViewModel(EntityAuthor author)
{
if (author == null)
return null;
Author result = new Author();
result.Id = author.ATH_ID;
result.FirstName = author.ATH_FirstName;
result.LastName = author.ATH_LastName;
return result;
}
public static BlogPost ToViewModel(EntityBlogPost post)
{
if (post == null)
return null;
Experiment result = new Experiment();
result.Id = post.BP_ID;
result.Title = post.BP_Title;
result.Url = post.BP_Url;
result.Description = post.BP_Description;
result.Author = ToViewModel(post.Author);
return result;
}
// from VM
public static EntityAuthor ToModel(Author author)
{
if (author == null)
return null;
EntityAuthor result = new EntityAuthor();
result.ATH_ID= author.Id;
result.ATH_FirstName = author.FirstName;
result.ATH_LastName = author.LastName;
return result;
}
public static EntityBlogPost ToModel(BlogPost post)
{
if (post == null)
return null;
EntityBlogPost result = new EntityBlogPost();
result.BP_ID = post.Id;
result.BP_Title = post.Title;
result.BP_Url = post.Url;
result.BP_Description = post.Description;
result.Author = ToModel(post.Author);
return result;
}
注意:
EntityBlogPost
将外键保留为EntityAuthor
。我现在面临的一个问题是,当我要编辑BlogPost时,其对应的实体需要设置作者的外键:“ BP_ATH_ID”,但是由于编辑帖子的作者为“ null”,所以它为“ 0”,因为我不想http-post作者。尽管如此,作者仍需要处于视图模型中,因为我想显示它(在http-get期间)。这是我的控制器,可以帮助您更好地理解(这种观点并不重要): // GET: I make use of Author for this
public ActionResult Edit(int id)
{
return View(VMConverter.ToViewModel(new BlogPostService().GetByID(id)));
}
//
// POST: I don't make use of Author for this
[HttpPost]
public ActionResult Edit(BlogPost input)
{
if (ModelState.IsValid)
{
new BlogPostService().Update(VMConverter.ToModel(input));
return RedirectToAction("List");
}
return View(input);
}
目前,我的控制器后面有一些服务,这些服务只能在
Model
上运行(如您在代码中所见)。目的是也将该“服务层”重用于其他应用程序。 public void Update(EntityBlogPost post)
{
// let's keep it simple for now
this.dbContext.Entry(post).State = EntityState.Modified;
this.dbContext.SaveChanges();
}
好的,回到我的问题。什么是处理此过渡Model-> ViewModel并返回的好方法?
最佳答案
我认为该方法在两个方向上都是有问题的。
建模到ViewModel(获取请求)
如果您使用的是这样的方法...
public static Author ToViewModel(EntityAuthor author)
...问题是:您从哪里获得
EntityAuthor author
?当然,您可以使用Find
或Single
之类的东西从数据库中加载它。这体现了具有所有属性的整个EntityAuthor
实体。您是否需要所有这些视图?在这个简单的示例中,也许可以。但是,想象一个大型的Order
实体,该实体具有对其他实体的大量引用-客户,交货地址,订单项,联系人,发票地址等,并且-您只想显示仅具有某些属性的视图:日期,客户名称,联系人电子邮件地址。要应用
ToViewModel
方法,您必须为EntityOrder
加载一整套视图不需要的属性,甚至必须为相关实体应用Include
。这将再次加载这些实体的所有属性,但是您只需要在视图中选择它们。仅加载视图所需属性的通常方法是投影,例如:
var dto = context.Orders.Where(o => o.Id == someOrderId)
.Select(o => new MyViewDto
{
DueDate = o.DueDate,
CustomerName = o.Customer.Name,
ContactPersonEmailAddress = o.ContactPerson.EmailAddress
})
.Single();
如您所见,我引入了一个新的帮助器类
MyViewDto
。现在您可以创建特定的ToViewModel
方法:public static OrderViewModel ToMyViewModel(MyViewDto dto)
dto和viewModel之间的映射是AutoMapper的理想选择。 (您不能在上面的投影步骤中使用AutoMapper。)
另一种选择是直接投影到ViewModel中,即将上面的
MyViewDto
替换为OrderViewModel
。尽管ViewModel所在的位置,您必须将IQueryable<Order>
暴露给视图层。有些人不喜欢它,我个人就是使用这种方法。缺点是您需要使用许多不同类型
ToMyViewModel
的方法,基本上对于每种视图而言,其他方法都是如此。ViewModel进行建模(POST请求)
正如您在示例中已经注意到的那样,这是一个更大的问题:许多视图未显示完整的实体或显示应为“仅视图”的实体数据,并且没有被发回到服务器。
如果您使用此方法(是否使用AutoMapper)...
public static EntityAuthor ToModel(Author author)
...显然,在大多数情况下,您不会创建完整的
EntityAuthor
对象,因为由视图模型Author author
表示的视图不会显示所有属性,至少不会将其全部发布回去。使用这样的Update
方法:this.dbContext.Entry(post).State = EntityState.Modified;
...将部分破坏数据库中的实体(或在最佳情况下引发异常,因为某些必需的FK或属性未正确设置)。要实现正确的更新,您实际上必须合并存储在数据库中的值,这些值保持不变,并且更改后的值从视图中回发。
您可以使用为视图量身定制的特定
Update
方法:public void UpdateForMyView1(EntityBlogPost post)
{
this.dbContext.EntityBlogPosts.Attach(post);
this.dbContext.Entry(post).Property(p => p.Title).IsModified = true;
this.dbContext.Entry(post).Property(p => p.Description).IsModified = true;
this.dbContext.SaveChanges();
}
这将是仅允许编辑
Title
的Description
和EntityBlogPost
的视图的方法。通过将特定属性标记为Modified
,EF只会更新数据库中的那些列。另一种选择是再次引入DTO,以及在视图模型和那些DTO之间映射方法:
public static MyUpdateAuthorDto ToMyUpdateAuthorDto(Author author)
这仅是属性复制或AutoMapper。更新可以通过以下方式完成:
public void UpdateForMyView1(MyUpdateAuthorDto dto)
{
var entityAuthor = this.dbContext.EntityAuthors.Find(dto.AuthorId);
this.dbContext.Entry(entityAuthor).CurrentValues.SetValues(dto);
this.dbContext.SaveChanges();
}
这只会更新
EntityAuthor
和dto
中匹配的属性,如果确实发生更改,则将它们标记为Modified
。这将解决您的外键丢失的问题,因为它不是dto的一部分,并且不会被更新。数据库中的原始值保持不变。请注意,
SetValues
将object
作为参数。因此,您可以使用某种可重用的Update方法:public void UpdateScalarAuthorProperties(int authorId, object dto)
{
var entityAuthor = this.dbContext.EntityAuthors.Find(authorId);
this.dbContext.Entry(entityAuthor).CurrentValues.SetValues(dto);
this.dbContext.SaveChanges();
}
此方法仅适用于标量和复杂属性的更新。如果允许您的视图更改相关实体或实体之间的关系,该过程就不那么容易了。对于这种情况,除了为每种Update编写特定方法之外,我不知道其他方法。
关于c# - 如何使用嵌套类从特定的Model转换为ViewModel,反之亦然?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11042304/