基类(域模型)到派生类(视图模型)的转换

本文关键字:模型 视图 转换 基类 派生 | 更新日期: 2023-09-27 17:49:36

我在MVC应用程序中看到以下模式,基本上是一个派生类向域类添加了一些小功能,很多时候只是显示依赖于基类其他属性的只读属性。有什么更好的方法不需要编写非常基本的属性复制代码(between //***** //*****)?

//Can't change this class, it has what is shown (it contains only properties)
public class MyDomainModel
{
    public bool BoolValue { get; set; }
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
    // Several other properties
}
public class MyViewModel : MyDomainModel
{
    // This is the only property that is added to the view model class
    public string DisplayValue
    {
        get { return BoolValue ? "Value1" : "Value2"; }
    }
    public static MyViewModel FromBase(MyDomainModel myDomainModel)
    {
        var myViewModel = new MyViewModel();
        //*****
        myViewModel.BoolValue = myDomainModel.BoolValue;
        myViewModel.Prop1 = myDomainModel.Prop1;
        myViewModel.Prop2 = myDomainModel.Prop2;
        // Several other properties
        //*****
        return myViewModel;
    }
}
// some other place
MyDomainModel myDomainObject = CallService();
//Here MyDomainModel needs to be converted to MyViewModel
MyViewModel myViewObject = MyViewModel.FromBase(myDomainObject);

EDIT:我想用这个问题来解决过多的"属性复制"代码在应用程序中蔓延的问题。不幸的是,错误的示例选择(从DomainModel派生的ViewModel)导致了另一个领域的讨论。

基类(域模型)到派生类(视图模型)的转换

public class MyDomainModel
{
    // .....
}
public class MyViewModel : MyDomainModel
{
    // .....
}

为什么要让视图模型继承域模型?这似乎是一个糟糕的主意。

在这种情况下继承给你带来了什么好处?你在重复使用什么?

我所能看到的是一袋属性,在你的使用场景中,你甚至不能重用那些(因为你得到一个已经实例化的基类,你不能将其实例化为派生类)。

在这里(而且经常)似乎合适的一个公理是"优先组合而不是继承"。除非你有明确的理由并且对继承有好处,否则还是避免继承吧。

最常见的建议是让你的ViewModels完全独立的类,并保持它们不依赖。它们应该是简单的POCO。

然后,您只需将域模型对象映射到视图模型对象。这将涉及一些重复的属性复制代码或使用像AutoMapper这样的工具,但是您希望这里的复制,因为它减少了耦合。

另一方面,

继承引入了一些非常紧密的耦合,几乎肯定会给您带来问题。

这看起来类似于您希望在呈现视图之前将信息附加到所有模型的模式。实际上,你可以通过重写onactionexecute,然后将数据附加到你的模型来做到这一点。

 protected override OnActionExecuted(ActionExecutedContext context)
 {
      ViewResult viewResult = filterContext.Result as ViewResult;
      if(viewResult != null)
      {
           MyDomainModel model = viewResult.ViewData.Model as MyDomainModel;
           if(model != null)
              /* Set Properties on your Model, for any model deriving 
                 from MyDomainModel! */
      }
 } 

在基类中创建复制构造函数。

这将为您在派生类中节省大量代码。

public class MyDomainModel
{
    public bool BoolValue { get; set; }
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
    public MyDomainModel(MyDomainModel myDomainModel)
    {
        var myViewModel = new MyViewModel();
        //*****
        this.BoolValue = myDomainModel.BoolValue;
        this.Prop1 = myDomainModel.Prop1;
        this.Prop2 = myDomainModel.Prop2;
        // Several other properties
        //*****
    }
}
public class MyViewModel : MyDomainModel
{
    // This is the only property that is added to the view model class
    public string DisplayValue
    {
        get { return BoolValue ? "Value1" : "Value2"; }
    }
    public MyViewModel (MyDomainModel other) : base(other) {}
}

然后在代码中使用:

// some other place
MyDomainModel myDomainObject = CallService();
//Here MyDomainModel needs to be converted to MyViewModel
MyViewModel myViewObject = new MyViewModel(myDomainObject);

我认为你违反了单一责任原则,因为对ViewModel的责任只是为视图提供来自域模型的一些数据,而在这个类视图模型类通常表示的类中没有任何业务逻辑,您可以将Domain对象的数据映射到ViewModel对象AutoMapper

看下面的文章示例Automaper NerdDinner

AutoMapper:对象-对象映射器