在阅读了许多关于如何处理EF 6中的序列的文章之后(见下文),我找到了一种适合我的方法。我首先使用代码,希望从头到尾包含序列,所以不使用迁移(告诉我是否弄错了)。
任务是为我的实体车获得一个唯一的和不累加的额外ID,这个ID没有被用作主键。
我现在正在做的事情是:
public class Car
{
public int Id { get; set; }
public int CarNr { get; set; }
public string Name { get; set; }
}public class Context : DbContext
{
public Context() : base()
{
SetDefaultConfiguration();
}
public Context(string connectionString) : base(connectionString)
{
SetDefaultConfiguration();
}
public Context(DbConnection existingConnection) : base(existingConnection, true)
{
SetDefaultConfiguration();
}
public override int SaveChanges()
{
SetCarNr();
return base.SaveChanges();
}
public override Task<int> SaveChangesAsync()
{
SetCarNr();
return base.SaveChangesAsync();
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
SetCarNr();
return base.SaveChangesAsync(cancellationToken);
}
private void SetDefaultConfiguration()
{
Database.SetInitializer(new CreateSequenceInitializer());
}
public class CreateSequenceInitializer : DropCreateDatabaseIfModelChanges<Context>
{
protected override void Seed(Context context)
{
context.Database.ExecuteSqlCommand("CREATE SEQUENCE CarNrSequence AS INT START WITH 200000 NO CACHE;");
base.Seed(context);
}
}
private void SetCarNr()
{
var carsToSave = ChangeTracker.Entries().Select(e => e.Entity).OfType<Car>();
foreach (var c in carsToSave)
{
c.CarNr = GetNextSequenceValue();
}
}
public int GetNextSequenceValue()
{
var rawQuery = Database.SqlQuery<int>("SELECT NEXT VALUE FOR CarNrSequence;");
var task = rawQuery.SingleAsync();
int nextVal = task.Result;
return nextVal;
}
public DbSet<Car> Cars { get; set; }
}因此,我使用一个DatabaseInitializer为我创建序列,并拦截SaveChanges以插入从DB查询的额外ID。
问:有没有更好的方法?这是“拯救”吗?你看到什么问题了?
谢谢!
PS:这里有一些链接对我很有帮助
发布于 2017-04-05 13:56:01
您的C#代码看起来很好,但是使用非缓存的序列而不是标识(在实体框架中实现起来要容易得多)并不能确保无障碍地递增标识符。
根据微软的这篇文章,序列可以在一些情景中产生空白:
序列对象根据其定义生成数字,但序列对象不控制数字的使用方式。当事务回滚时,当多个表共享序列对象时,或者在没有在表中使用序列号时,插入到表中的序列号可能会有空白。
因此,保存更改的错误将消耗序列号,从而获得空白。
在数据库方面,这条线接近于生成无障碍增量标识符,但我也在考虑在应用层中生成标识符和恢复保存失败时的值:
private int PrevSequenceId;
private int SequenceId;
private void SetCarNr()
{
var carsToSave = ChangeTracker.Entries().Select(e => e.Entity).OfType<Car>();
foreach (var c in carsToSave)
{
c.CarNr = Interlocked.Increment(ref SequenceId);
}
}
public override int SaveChanges()
{
try
{
SetCarNr();
return base.SaveChanges();
// locking might be required here for thread safety
PrevSequenceId = SequenceId;
}
catch (Exception exc)
{
// log stuff if needed
// locking might be required here for thread safety
SequenceId = PrevSequenceId;
}
}但是,只有在仅由应用层生成标识符时,此解决方案才能工作。
https://codereview.stackexchange.com/questions/150060
复制相似问题