C# 从数据库填充域实体

C# 从数据库填充域实体,c#,entity-framework,domain-driven-design,lazy-loading,eager-loading,C#,Entity Framework,Domain Driven Design,Lazy Loading,Eager Loading,我的代码中有很多类似的东西(这只是一个简单的示例): 而Invoice将字段UnpaidAmount计算为 public double UnpaidAmount { get { return OrderLines.Sum(ol => ol.Amount) - Payments.Sum(p => p.Amount); } } 现在,项目中经常发生的情况是,如果有人需要修改UnpaidAmount逻辑,例如: ret

我的代码中有很多类似的东西(这只是一个简单的示例):

Invoice
将字段
UnpaidAmount
计算为

public double UnpaidAmount
{
    get
    {
        return OrderLines.Sum(ol => ol.Amount) -
               Payments.Sum(p => p.Amount);
    }
}
现在,项目中经常发生的情况是,如果有人需要修改
UnpaidAmount
逻辑,例如:

return OrderLines.Sum(ol => ol.Amount) -
       Payments.Sum(p => p.Amount) -
       CreditNotes.Sum(cn => cn.Amount);
然后,他们需要在项目中使用
UnpaidAmount
的任何地方找到,并在获取
发票时将
CreditNotes
添加到
Include
。人们经常忘记这一点,在这种情况下,
CreditNotes
上的
Sum
实际上会在一个空集合上被调用,而不是在从数据库获取的集合上

这会变得很麻烦,很难通过项目来维护

另一种选择是要么丢失
懒散加载
,这样我们就不必考虑包含在任何地方,但这可能会导致性能问题,这些问题可能在开发过程中检测不到,但在以后的生产中,当获取的记录数变大时会检测到

或者使用一种方法获取
Invoice
对象及其所有导航属性+递归地获取对象图中更深的导航属性。但这太过分了,因为很多不需要的东西每次都会被拿走


我想这是我必须做出的权衡,但我需要的是那些在大型项目中遇到此类问题的人的建议,你们认为什么样的解决方案最适合长期维护

可以提供引入某种类型的商店,例如发票。它将在构造函数中获取DbContext,并提供获取和保存发票所需的所有方法。所以,您只有一个地方可以编写和修改查询

class Invoices
{
    public Invoices(DbContext dbContext){....}

    public Invoice GetInvoiceById(int invoiceId)
    {
        return this.dbContext.Invoices
            .ForId(invoiceId)
            .Include(i => i.Payments)
            .Include(i => i.OrderLines)
            .FirstOrDefault();
    }
...
然后,他们需要在项目中找到使用UnpaidAmount的所有地方,并在获取发票时添加CreditNotes

为什么这种责任存在于不止一个地方

另一种方法是要么丢失懒散加载,这样我们就不必考虑包含在任何地方,但这可能会导致性能问题,这些问题可能在开发过程中检测不到,但在以后的生产过程中,当获取的记录数变大时会检测到

或者使用一个方法获取Invoice对象及其所有导航属性+递归地获取对象图中更深的导航属性。但这太过分了,因为很多不需要的东西每次都会被拿走

我认为您正在寻找的是存储库模式

基本思想是:

您有一个或多个用户可以用来描述他们将如何使用发票(或者,发票的哪个视图满足他们的需求)

您提供了这些角色的实现,这些角色对发票数据的存储方式有共同的理解。这意味着,当您将CreditNotes引入模型时,只有一个地方需要更改

您可以使用管道(依赖项注入或其他方式)来确保为每个角色提供正确的实现

简而言之,您在消费者和供应商之间创建了一个明确的合同;消费者描述他们需要什么,供应商可以自由选择如何满足需求

乌迪·达汉(Udi Dahan)在当天写了几篇与这个想法相关的帖子

class Invoices
{
    public Invoices(DbContext dbContext){....}

    public Invoice GetInvoiceById(int invoiceId)
    {
        return this.dbContext.Invoices
            .ForId(invoiceId)
            .Include(i => i.Payments)
            .Include(i => i.OrderLines)
            .FirstOrDefault();
    }
...