我是Entity Framework的新手,我在纯t-sql中很容易出问题。假设我们有3列的表。 Id(标识,主键),IdKind(int),Number(int)。列Nubmer依赖于IdKind列。此表中的数据应如下所示:
Id | IdKind |编号
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 2
6 | 2 | 3
如您所见,列号自动递增取决于IdKind。在t-sql中,我发现某些IdKind的max(Number)+ 1。一切都在一个事务中的一个存储过程中,因此Number的值对于IdKind是唯一的。
如何在Entity Framework中完成相同的操作。应在创建新对象或更改IdKind时计算Number的值。
答案 0 :(得分:4)
我会使用数据库触发器并在EF映射中使用Number
配置StoreGeneratedPattern.Computed
属性,以通知EF此属性已在数据库中填充,并且必须在每次更新/插入后重新加载。
我也不会对每个新号码使用max()。相反,我会维护单独的表模拟序列。此表应包含每个序列的记录 - 在每个IdKind
的案例记录中包含当前最大数量。您将选择单个记录并将其更新为包含新的最大数量,而不是选择聚合。使用此表必须与对实体的持久更改处于同一事务中。
答案 1 :(得分:2)
我遇到了同样的问题,但我在c#中解决了这个问题。
我应该有一个映射到表的实体:
public class MyEntity
{
Kind IdKind{get; set;}
int Number{get;set;}
}
我应该有辅助实体存储每种类型使用的最后一个数字。
public class LastNumber
{
Kind IdKind{get;set;}
int LastNumber{get;set;}
}
然后在向表中添加一行(MyEntity对象)之前,我应该使用LastNumberEntity的LastNumber字段计算正确的Number,其中Kind等于我的Kind值。
我们必须注意这个策略有一个并发问题,因为两个不同的线程可能同时添加同一种类的对象,这些线程将检查相同的LastNumberEntity以获取LastNumber值。这个问题在entityframework中有一个已知的解决方案。代码可能如下所示:
public int GetNumber(Kind idKind)
{
using (var db = new MyDataContext())
{
var lastNumber = db.LastNumbers.Single(x=>x.IdKind == idKind);
for (int attemps = 0; attemps < 10;)
{
try
{
lastNumber.LastNumber++;
db.SaveChanges();
return lastNumber.LastNumber;
}
catch (DbUpdateConcurrencyException ex)
{
attemps++;
}
}
}
throw new Exception("Many concurrency calls");
}
此方法获取指定Kind的增量编号并处理并发问题,使用它,我们可以为每个MyEntity对象设置Number值。客户端代码可能如下所示:
var entity = new MyEntity();
var number = GetNumber(entity.IdKind);
entity.Number = number;
db.MyEntities.Add(entity);
db.SaveChanges();
此代码可以重构,使得setter成为私有代码,使工厂确保始终使用GetNumber方法创建实体并使用存储库来管理db。