我遵循了这里的文档,我使用的是SQL Server 2016(https://ef.readthedocs.io/en/latest/modeling/concurrency.html)
我在表中添加了一个名为VersionCol的时间戳列,然后当我运行Scaffold DbContext时,它会在我的DbContext中的表实体上放置以下属性。
entity.Property(e => e.VersionCol)
.IsRequired()
.HasColumnType("timestamp")
.ValueGeneratedOnAddOrUpdate()
它缺少.IsConcurrencyToken()
,所以我自己添加了它,但在应该遇到并发问题的情况下仍然不会抛出异常。相反,它只是简单地重写数据。
我有什么东西不见了吗?
编辑:
我使用数据库优先的方法(因此没有[Timestamp]或任何其他注释),我的DbContext被注入到一个服务中,在Startup.cs 中配置services.AddScoped<IPoRepository, PoRepository>()
它在我的模型中生成了一个public byte[] VersionCol { get; set; }
字段,我相信这是正确的。
在我的PoRepository中,我试图用以下内容更新我的Po:
public void SavePo(PoListing poListing) {
Po po;
try {
po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
} catch ( ArgumentNullException ) {
throw new ArgumentNullException( "The PO does not exist." );
}
po.AssignedUserId = poListing.AssignedUserId;
po.VersionCol = poListing.VersionCol;
_context.Entry( po ).State = EntityState.Modified;
_context.SaveChanges();
}
PoListing本质上只是Po的一部分,所以它只有它的一些列(它不是数据库中的表),并且它在第一次生成时具有Po的VersionCol。如果PoListing的VersionCol比它所基于的Po旧,那么它应该给出一个异常。
第2版:
这是可行的,但我不知道如何在不需要创建第二个上下文的情况下使其工作,只需要使用注入的上下文。
public void SavePo(PoListing poListing) {
DbContextOptionsBuilder<TMS_1000Context> options = new DbContextOptionsBuilder<TMS_1000Context>();
options.UseSqlServer( "Server=DEVSQL16;Database=TMS_1000_Dev;Trusted_Connection=True;MultipleActiveResultSets=true" );
TMS_1000Context context1;
try {
po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
} catch ( ArgumentNullException ) {
throw new ArgumentNullException( "The PO does not exist." );
}
using ( context1 = new TMS_1000Context( options.Options ) ) {
po.AssignedUserId = poListing.AssignedUserId;
po.VersionCol = poListing.VersionCol;
context1.Update( po );
context1.SaveChanges();
}
}
第3版:
这目前正在工作。还有别的办法吗?
public void SavePo(PoListing poListing) {
Po po;
try {
po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
} catch ( ArgumentNullException ) {
throw new ArgumentNullException( "The PO does not exist." );
}
po.AssignedUserId = poListing.AssignedUserId;
_context.Entry( po ).Property( u => u.VersionCol ).OriginalValue = poListing.VersionCol;
_context.Update( po );
_context.SaveChanges();
}
我之所以相信会发生这种情况,是因为EF Core跟踪只关心原始值是否与数据库中当前的值相同,如果它们不相同,则会引发并发异常。
以下是我发现的3个修复程序。
-
更改原始值,使其与数据库中存在的值不同。
public void SavePo(PoListing poListing) { Po po; try { po = _context.Po.Where( p => p.Poid == poListing.PoId ).First(); } catch ( ArgumentNullException ) { throw new ArgumentNullException( "The PO does not exist." ); } po.AssignedUserId = poListing.AssignedUserId; _context.Entry( po ).Property( u => u.VersionCol ).OriginalValue = poListing.VersionCol; _context.Update( po ); _context.SaveChanges(); }
-
使用AsNoTracking()获取实体。现在EF Core将不再只是比较原始值。
public void SavePo(PoListing poListing) { Po po; try { po = _context.Po.AsNoTracking().Where( p => p.Poid == poListing.PoId ).First(); } catch ( ArgumentNullException ) { throw new ArgumentNullException( "The PO does not exist." ); } po.AssignedUserId = poListing.AssignedUserId; po.VersionCol = poListing.VersionCol; _context.Update( po ); _context.SaveChanges(); }
-
从上下文中分离实体。与修复#2 功能相似
public void SavePo(PoListing poListing) { Po po; try { po = _context.Po.Where( p => p.Poid == poListing.PoId ).First(); } catch ( ArgumentNullException ) { throw new ArgumentNullException( "The PO does not exist." ); } po.AssignedUserId = poListing.AssignedUserId; po.VersionCol = poListing.VersionCol; _context.Entry( po ).State = EntityState.Detached; _context.Update( po ); _context.SaveChanges(); }