我有这个" 1到N"模型:
class Reception
{
public int ReceptionId { get; set; }
public string Code { get; set; }
public virtual List<Item> Items { get; set; }
}
class Item
{
public int ItemId { get; set; }
public string Code { get; set; }
public int Quantity { get; set; }
public int ReceptionId { get; set; }
public virtual Reception Reception { get; set; }
}
这个动作,api/receptions/list
public JsonResult List()
{
return dbContext.Receptions
.Select(e => new
{
code = e.Code,
itemsCount = e.Items.Count,
quantity = e.Items.Sum(i => i.Quantity)
}).ToList();
}
返回接收列表及其项目数:
[
{code:"1231",itemsCount:10,quantity:30},
{code:"1232",itemsCount:5,quantity:70},
{code:"1234",itemsCount:30,quantity:600},
...
]
这工作正常,但我有太多Reception
和Item
因此查询花了太长时间......
所以我想通过向Reception
添加一些持久字段来加快速度:
class Reception
{
public int ReceptionId { get; set; }
public string Code { get; set; }
public virtual List<Item> Items { get; set; }
public int ItemsCount { get; set; } // Persisted
public int Quantity { get; set; } // Persisted
}
通过此更改,查询最终成为:
public JsonResult List()
{
return dbContext.Receptions
.Select(e => new
{
code = e.Code,
itemsCount = e.ItemsCount,
quantity = e.Quantity
}).ToList();
}
我的问题是:
维持这两个领域的最佳方法是什么?
我会获得表现,但现在我需要更加谨慎地创建Item
今天可以创建,编辑和删除Item
:
api/items/create?receptionId=...
api/items/edit?itemId=...
api/items/delete?itemId=...
我还有一个通过Excel导入接收的工具:
api/items/createBulk?...
也许明天我会有更多方法来创建Item
,所以问题是如何确保这两个新字段ItemsCount
和Quantity
将会总是最新的?
我应该像这样在Reception
内创建方法吗?
class Reception
{
...
public void UpdateMaintainedFields()
{
this.Quantity = this.Items.Sum(e => e.Quantity);
this.ItemsCount = this.Items.Count();
}
}
然后记住从之前的所有网址中调用它? (items/create
,items/edit
,...)
或者我应该在数据库中有一个存储过程吗?
通常的做法是什么?我知道有calculated columns
但这些引用了同一类的字段。还有indexed views
,但我不确定它们是否适用于这样的场景。
答案 0 :(得分:1)
在您的代码中,我觉得您没有业务逻辑层,并且所有内容都在控制器中实现,这会导致问题,当您有不同的方式时(似乎,您意味着一个不同的控制器)你必须再次实现这个逻辑,很容易忘记,如果你不忘记,你可能忘记以后维护。
所以我建议有一个业务逻辑层(比如添加新项目),并从你想要创建项目的控制器中使用它。
我还建议您按照要求编写UpdateMaintainedFields函数,但在添加项目之后在业务逻辑层中调用它,而不是在控制器中调用它!
如果您可以接受不能编写单元测试的话,也可以在数据库上编写逻辑(触发器)。
答案 1 :(得分:0)
假设在SQLServer中使用正确的执行计划无法改进原始查询,更新这些字段的方法是通过DB中的触发器。当发生插入时(如果持久化字段根据数据发生更改,则可能更新),那么当该表发生插入时,将运行触发器。它将负责使用新值更新所有行。
显然,您的插入性能会下降,但您的查询性能将是简单索引和单行读取。显然,如果您要返回表的一个子集,则无法使用此技巧,因为所有数量都将被修复。
另一种方法是将计数和数量总和保存在单独的表中,或保存在一个虚拟行中,该行包含总计数量作为其数量条目。 YMMV。
PS我讨厌在C#代码中解决了什么是SQL问题!学习SQL并直接在数据库中运行您需要的查询,这将向您展示更多关于您所需要的性能和结构的信息,而不是让EF参与其中。 / rant:)
答案 2 :(得分:0)
您希望以双重方式存储相同的信息,这可能会导致不一致。作为灵感,索引也在复制数据。你如何更新它们?你没有。它完全透明。我会在这里推荐相同的方法。
制作总和表,由触发器维护。该表不会包含在任何datacontext模式中,只有通过不可更新的视图或存储过程才能读取它。它的名字应该引起,没有人应该直接触摸这个表。
您现在可以从各种框架访问您的数据,而不必担心更新任何内容。数据库将确保预先计算的总和始终是正确的,只要您不自行写入总和表即可。实际上,您可以随时添加或删除此表,甚至没有应用程序会注意到。