我正在设计一个跟踪销售的新系统。我的数据模型的一个简单版本是:
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);
这是工作正常,给我预期的结果。我觉得一旦涉及到大型数据集,这可能会给我带来进一步的问题。我认为必须将所有Sale条目加载到一个列表中会占用大量资源,再加上我的实际实现中有与每个salesitem相关的税收、成本等,因此再次变得更加复杂。
下面将允许我在数据库上做所有的处理,但是这是不可能做到的,因为DB没有Total的表示,所以EF抛出一个错误:
Decimal total = db.Sales.Sum(x=>x.Total);
这就引出了我的问题。我可以设置我的模型如下,每次我添加一个salesitem,确保我更新Total:
public class Sale
{
...
public decimal Total { get; set; }
}
这将允许我根据需要查询数据库,我认为这将减少资源密集。另一方面,我减少了数据库的冗余。后一种方法是更好的处理方法还是有一种我甚至没有考虑过的更好的方法?
这取决于许多因素。例如,您多久需要提供一次"总"金额?一次销售中通常有多少个Sale item ?
如果我们在谈论,比如说,一个超市的销售,你有……说…最多200项。在飞行中快速计算它是很好的。然后,如果它被映射到一个RDBMS,如果你在一个表中有所有的salesitem,在外键上有一个索引(它将每个单独的salesitem链接到它的Sale)是必须的,否则一旦你开始有数百万个事务要筛选,性能将会受到巨大的打击。
回答你问题的后半部分,有冗余并不总是坏事…您只需要确保,如果每个Sale都需要修改其List,则在其结束时重新计算Total。这有点危险(冗余总是附带这个负担),但您只需要确保无论有什么可能改变Sale,都以某种方式(甚至可能在RDBMS中使用触发器)自动重新计算总数。
希望有帮助!
你是对的,在数据库端计算总数比加载整个列表并在应用程序上计算要有效得多。
我想你错过了你可以做一个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时,它不是很明显如何得到你想要的,但在大多数情况下你可以做到。