我应该为领域和EF使用不同的模型吗?

9

场景:我正在编写一个处理报告生成的程序。

我将报告存储在数据库中,并映射到EF模型。有一些非数据库字段(即某些字段是根据其他在数据库中的字段自动计算的)。是否有意义拥有一个仅映射到DB的类,以及另一个类,该类获取该信息并具有其他计算字段?

例如,与codefirst数据库交互的示例类将是:

public class Report{
    public int CategoryOneSeverity {get; set;}
    public int CategoryTwoSeverity {get;set;}
    public string Title {get;set;}
}

有没有必要再创建一个类,比如:

public class ReportModel{
    public int CategoryOneSeverity;
    public int CategoryTwoSeverity;
    public string Title;

    public int RiskRating{
        get{ return CategoryOneSeverity + CategoryTwoSeverity; }
    }
}    

或者RiskRating属性应该在EF模型中。

我同意被接受的答案,即你应该有单独的类——一个用于建模数据存储,另一个用于建模业务域。然而,你的第二个类违反了许多良好设计原则,其中最明显的是字段不应该公开暴露。 - Jesse C. Slicer
我真的不喜欢添加私有成员,然后使用属性来获取它。使用字段更有意义,特别是对于预计会发生变化的区域,例如用户随时可以更改标题。 - appsecguy
1
不,实际上这并没有意义,正如我所说的那样,它违背了良好设计的原则。该属性允许您添加验证逻辑,使用转换更改后备字段表示,并最终使您能够保持稳定的消费者表面积。让我们的好朋友Jon Skeet更好地解释:http://www.csharpindepth.com/Articles/Chapter8/PropertiesMatter.aspx - Jesse C. Slicer
3个回答

10

是的,我绝对相信你应该有不同的类来模拟你的领域和数据库。除非你的应用程序非常简单,否则如果你尝试直接映射你的领域对象,你不可避免地需要修改它们以匹配你需要的数据结构,并可能暴露一些你不想暴露的东西。把它看作是单一职责原则的违反;如果你让它成为你的领域对象并直接映射它,你的类有两个改变的原因。一个是响应于业务需求的变化,另一个是响应于数据存储架构的变化。


1
如果您将其作为域对象并直接映射,那么您的类有两个更改原因。一个是响应于不断变化的业务需求,另一个是响应于不断变化的数据存储模式。- 这帮助我完美地形象化了它。谢谢! - appsecguy

7
"是否有一个仅映射到数据库的类和另一个类,该类接收该信息并具有其他计算字段?" 很可能是的。通常我会创建一个新类,后缀为“ViewModel”,例如HumanResourcesReportViewModel,如果我的实体类是HumanResourcesReport。 ViewModel使用方法有很多变化,我们可以就术语进行琐碎的辩论,但概念上,取出实体并创建一个新类,其中包含您需要处理报告所需的任何其他信息。在这种情况下,报告生成在某种程度上是MVC模型的视图,因此我认为将保存数据的类称为ViewModel不会引起反感。"

我认为你在谈论架构的另一端,即用户界面(UI)。ViewModels被用于UI中,这就是为什么它们被称为“View”模型的原因。 - Robert Harvey
1
@Robert 报告生成在视图端。您从数据库获取数据,将其塑造成ViewModel,然后生成报告,这是您呈现数据的方式。无论是通过报告引擎还是HTML引擎生成,它都是一个视图。您可以根据自己的喜好来称呼它,但从概念上讲,它是相同的东西。您需要一些不属于实体的额外数据。 - AaronLS
啊,我明白你的意思了。计算RiskRating是一个显示函数。 - Robert Harvey

4

您是使用Code First还是DB First?

您可以在您的模型中拥有自动计算字段,这些字段不会映射到数据库中的字段。

这也取决于您的架构。如果您使用的是DB First,则刷新EF模型将更新EF类,从而丢失映射的字段。在DB-First情况下,一个替代方案是使用EF模型类作为基类,并从中继承您的报告类。

public class ReportModel
{
    public int CategoryOneSeverity;
    public int CategoryTwoSeverity;
    public string Title;
}   

public class ReportClass : ReportModel
{
    public int RiskRating
    { 
        get { return CategoryOneSeverity + CategoryTwoSeverity; }
    }
}

这在除了最琐碎的应用程序之外,效果都非常差。 - Andy
+1 你也可以直接向ReportModel类添加属性,并使用[NotMapped]属性进行标记。这样可以省去一个额外的类,但另一方面,它会在实体类中添加你可能不经常使用的属性,所以这是一个判断性的选择。 - AaronLS
Andy:提供的信息不多,旨在展示在提供的信息下可以实现什么,而不是一个完全可行的解决方案。 - Karl Gjertsen
@KarlGjertsen 已经理解,但我认为最有可能的假设是该应用程序将是非平凡的,基于这个假设,我们应该提供最好的答案。 - Andy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接