【问题标题】:how to design ViewModel如何设计 ViewModel
【发布时间】:2012-04-10 12:38:57
【问题描述】:

我在项目中实施了 EF 4。在其中,有表客户和订单。其中有一个(客户)对多个(订单)的关系。

我正在为两者(CustomerViewModel 和 OrderViewModel)创建一个视图模型,以便从我的域层传递到接口层(在本例中为 MVC)。

现在的问题是“我需要同时引用这两个视图模型吗?例如在 customerviewmodel 中有 IEnumerable<OrderViewModel> 而在 orderviewmodel 中有 CustomerViewModel。如果是这样,我该如何设计它(作为最佳实践)以便 IEnumerable<OrderViewModel>并且CustomerViewModel 填充了正确的引用?

【问题讨论】:

标签: entity-framework viewmodel


【解决方案1】:

我总是会在设计 ViewModel 时考虑到特定的视图,而不是从领域模型(=实体)的角度出发。 ViewModel 的外观取决于您要在视图中显示的内容以及要修改的内容。

因此,您没有 THE OrderViewModel 和 THE CustomerViewModel,因为您有不同的视图来显示或编辑订单或客户或其中的一部分。因此,您将这些 ViewModel 用于特定目的和视图,因此可以多次使用不同的变体。

假设您有一个OrderEditView,并且此视图将允许编辑订单信息并显示该订单的客户。你会有这样的OrderEditViewModel

public class OrderEditViewModel
{
    public int OrderId { get; set; }

    public DateTime? ShippingDate { get; set; }

    [StringLength(500)]
    public string Remark { get; set; }
    //...

    public OrderEditCustomerViewModel Customer { get; set; }
}

public class OrderEditCustomerViewModel
{
    [ReadOnly(true)]
    public string Name { get; set; }

    [ReadOnly(true)]
    public string City { get; set; }
    // ...
}

这个OrderEditCustomerViewModel 不需要引用OrderEditViewModel

你可以像这样填充这个 ViewModel:

var orderEditViewModel = context.Orders
    .Where(o => o.OrderId == 5)
    .Select(o => new OrderEditViewModel
    {
        OrderId = o.OrderId,
        ShippingDate = o.ShippingDate,
        Remark = o.Remark,
        Customer = new OrderEditCustomerViewModel
        {
            Name = o.Customer.Name,
            City = o.Customer.City
        }
    })
    .SingleOrDefault();

另一方面,如果您有一个CustomerEditView 允许编辑客户信息并在列表中显示客户的订单,则 ViewModel 可能是:

public class CustomerEditViewModel
{
    public int CustomerId { get; set; }

    [Required, StringLength(50)]
    public string Name { get; set; }

    [Required, StringLength(50)]
    public string City { get; set; }
    //...

    public IEnumerable<CustomerEditOrderViewModel> Orders { get; set; }
}

public class CustomerEditOrderViewModel
{
    [ReadOnly(true)]
    public DateTime? ShippingDate { get; set; }

    [ReadOnly(true)]
    public string Remark { get; set; }
    // ...
}

这里CustomerEditOrderViewModel 不需要引用CustomerEditViewModel,您可以通过这种方式从数据库创建 ViewModel,例如:

var customerEditViewModel = context.Customers
    .Where(c => c.CustomerId == 8)
    .Select(c => new CustomerEditViewModel
    {
        CustomerId = c.CustomerId,
        Name = c.Name,
        City = c.City,
        Orders = c.Orders.Select(o => new CustomerEditOrderViewModel
        {
            ShippingDate = o.ShippingDate,
            Remark = o.Remark
        })
    })
    .SingleOrDefault();

Customer(*)ViewModels 和 Order(*)ViewModels 是不同的 - 关于必要的引用、属性和数据注释,取决于使用它们的视图。

考虑到这些考虑,OrderViewModelCustomerViewModel 之间相互正确引用的问题消失了,因为您通常不需要为您的视图提供这样的双向引用。

【讨论】:

  • Slauma.. 你如何在视图模型和 EF 实体之间进行映射,反之亦然?
  • 还有..你如何填充公共 IEnumerable Orders { get;放;在 CustomerEditViewModel 中?你是懒惰还是渴望?
  • @user384080:从 EF 实体到 ViewModel 的映射是带有Select 的两个代码 sn-ps(它被称为“投影”,既不是惰性加载也不是急切加载,但更接近于急切加载,除了您只从数据库中检索 ViewModel 真正需要的列,而不是不必要的开销的完整实体)。尤其是最后一个 sn-p 还填充了 Orders 集合(请参阅内部 Select)。对于从 ViewModel 回到实体的方式,我使用 DTO,您可以手动将 ViewModel 中的属性映射到 DTO,或者使用 AutoMapper 之类的工具。
  • @user384080:我前段时间问过一个类似的问题,也许有帮助:stackoverflow.com/questions/5995140/…
猜你喜欢
  • 2013-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多