c# - 如何使用嵌套类从特定的Model转换为ViewModel,反之亦然?

标签 c# asp.net-mvc entity-framework model viewmodel

我想知道一种在没有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?当然,您可以使用FindSingle之类的东西从数据库中加载它。这体现了具有所有属性的整个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();
}


这将是仅允许编辑TitleDescriptionEntityBlogPost的视图的方法。通过将特定属性标记为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();
}


这只会更新EntityAuthordto中匹配的属性,如果确实发生更改,则将它们标记为Modified。这将解决您的外键丢失的问题,因为它不是dto的一部分,并且不会被更新。数据库中的原始值保持不变。

请注意,SetValuesobject作为参数。因此,您可以使用某种可重用的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/

相关文章:

c# - 使用 DataAnnotations 比较两个模型属性

c# - 使外键(字符串字段)可为空

.net - 如何有效地支持生产环境中的 Entity Framework SQL 日志记录?

entity-framework - DbContext 中是否有 GetObjectType?

c# - 动态WPF地理 map 显示用户控件

c# - 单选按钮列表未正确选择新值

c# - 多次调用 DownloadFileAsync

c# - Html.ActionLink() 不适用于传递 C# 对象

entity-framework - EF7 RC2 LINQ选择不包括新添加的记录

c# - 如何手动添加到 swagger 的 schemas 部分?