我正在尝试设计一个跟踪销售的新系统。我的数据模型的简单版本是:
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);
这工作正常并给我预期的结果。虽然一旦涉及到大型数据集,我觉得这可能会给我带来更多问题。我假设必须将所有销售加载到列表中将变得资源密集,而且我的实际实现具有与每个SaleItem相关的税,成本等,因此再次变得更加复杂。
以下将允许我对数据库进行所有处理,但由于数据库没有Total的表示,因此无法执行此操作,因此EF会抛出错误:
Decimal total = db.Sales.Sum(x=>x.Total);
这引出了我的问题。我可以将模型设置为以下,每次添加SaleItem时,请确保我更新Total:
public class Sale
{
...
public decimal Total { get; set; }
}
这将允许我根据需要查询数据库,并且我假设资源密集程度较低。另一方面,我减少了数据库的冗余。是后一种方式,处理这种方法的更好方法还是我还没有考虑过更好的替代方法?
答案 0 :(得分:1)
这取决于很多因素。例如,您需要多长时间才能获得“总计”金额?销售中通常有多少SaleItems?
如果我们谈论的是超市类型的销售,那么......最多可以说200件商品。在飞行中快速计算它是完全可以的。再说一次,如果它被映射到RDBMS并且如果你在一个单独的表中有所有SaleItems,那么在外键上有一个索引(将每个SaleItem链接到它的销售)是必须的,否则性能将会很大一旦你开始有数以百万计的交易筛选,就会被击中。
回答你问题的后半部分,拥有冗余并不总是坏事......你只需要确保如果每个Sale都需要修改其List,最后会重新计算Total。它有点危险(冗余总是有这种附加的负担),但你只需要确保任何有可能改变销售的东西,在某种程度上(甚至在RDBMS中有一个触发器)这样做会自动重新计算总数。
希望它有所帮助!
答案 1 :(得分:0)
你说得对,在数据库端计算总数而不是加载整个列表并在应用程序上计算它是更有效的。
我认为您错过了您可以创建一个获取相关子实体的SUM的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时,如何获得你想要的东西并不是很明显,但在大多数时候你都可以做到。