【问题标题】:Should I introduce redundancy into model design我应该在模型设计中引入冗余吗
【发布时间】:2014-10-18 10:41:51
【问题描述】:

我正在尝试设计一个用于跟踪销售的新系统。我的数据模型的简化版本是:

public class Sale 
{
    public int SaleId { get; set; }
    public DateTime CompletedDateTime { get; set; }

    public virtual List<SaleItem> SaleItems { get; set; }

    public decimal Total
    {
       get
        {
            return SaleItems.Sum(i => i.Price);
        }
    }
}


public class SaleItem 
{
    public int SaleItemId { get; set; }
    public decimal Price { get; set; }
    public int SaleId { get; set; }
    public virtual Sale Sale { get; set; }

}

我现在正在编写一些报告,其中汇总了指定时期之间的销售价值。我有以下代码可以做到这一点:

List<Sale> dailySales = db.Sales
                          .Where(x => DbFunctions.TruncateTime(x.CompletedDateTime) >= fromParam)
                          .Where(x => DbFunctions.TruncateTime(x.CompletedDateTime) <= toParam)
                          .ToList();

decimal total = dailySales.Sum(x => x.Total);

这一切正常,并给了我预期的结果。我觉得这可能会给我带来更多问题,尽管一旦涉及到大型数据集。我假设必须将所有销售加载到列表中会占用大量资源,而且我的实际实现与每个销售项目相关联的税收、成本等,因此再次变得更加复杂。

以下将允许我对数据库进行所有处理,但由于数据库没有 Total 的表示,因此无法执行此操作,因此 EF 会引发错误:

Decimal total = db.Sales.Sum(x=>x.Total);

这引出了我的问题。我可以将我的模型设置为以下,每次添加 SaleItem 时,请确保更新 Total:

public class Sale 
{
    ...    
    public decimal Total { get; set; }
}

这将允许我根据需要查询数据库,并且我认为资源密集度会降低。但另一方面是我减少了数据库中的冗余。后一种方法是更好的处理方法,还是有一种我什至没有考虑过的替代方法更好?

【问题讨论】:

  • 看你是多读还是多写

标签: c# asp.net-mvc entity-framework database-design


【解决方案1】:

这取决于许多因素。例如,您需要多久提供一次“总”金额?一个销售中通常有多少个销售项目?

如果我们在谈论,比如说,超市的销售,你有......比如说......最多最多 200 件商品。可以在运行中快速计算它。再说一次,如果这曾经被映射到 RDBMS 并且如果您将所有 SaleItems 放在一个表中,则必须在外键上建立索引(将每个单独的 SaleItem 链接到其 Sale),否则性能将需要巨大的一旦你开始有数百万笔交易需要筛选。

回答您问题的后半部分,冗余并不总是一件坏事...您只需要确保如果每个销售都需要修改其列表,则在其末尾重新计算总计。这有点危险(冗余总是有这个附加的负担),但您只需要确保任何有可能改变销售的方式(甚至可能在 RDBMS 中使用触发器)都会自动重新计算总数。

希望对你有帮助!

【讨论】:

  • 这是一个很好的建议。我要补充一点,托管冗余是可以的,但不要从假设您将需要它开始。通过负载测试找出你是否有性能问题,不要假设没有测试就会有问题。
【解决方案2】:

你说得对,在数据库端计算总数比加载整个列表并在应用程序上计算更有效。

我认为您缺少可以进行获取相关子实体总和的 LINQ 查询。

using (var ctx = new MyDbContext())
{
    var totalSales = ctx.Sales
        .Select(s => s.SaleItems.Sum(si => si.Price)) // Total of each Sale
        .Sum(tsi => tsi); // Sum of the total of each sale
}

您当然可以调整查询以带来额外的信息,将结果投射到匿名类或为此目的临时创建的类中。

当然,这个EF查询会被翻译成SQL查询,在服务器端执行。

当您开始使用 LINQ to EF 时,如何获得您想要的并不是很明显,但在大多数情况下您都可以做到。

【讨论】:

    猜你喜欢
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-26
    • 2018-01-01
    相关资源
    最近更新 更多