我正在努力寻找一种优雅的解决方案来处理Entity Framework中的并发冲突。
我的药物管理表有几栏,如药物名称,剂量,频率,给药方法,注释等等。操作员可以在查看记录后修改剂量,频率,管理方法等字段,也可以添加注释。
我们正在使用SQL Server数据库。
我添加了LastUpdated
类型的列timestamp
。我正在使用此列提升DbUpdateConcurrencyException
。 ConcurrencyMode
列的LastUpdated
已在实体设计器中设置为Fixed
。
我的控制器类的代码片段:
// drugId and strNotes are passed from edit screen
DataAccessLayer.Dal dal= new DataAccessLayer.Dal();
// Select record
DrugDetail drugDetail = (from drugRow in dal.DrugDetails
where drugRow.id == drugId
select drugRow).ToList<DrugDetail>()[0];
// Assign notes
drugDetail.Notes = strNotes;
try
{
dal.SaveChanges();
}
catch(DbUpdateConcurrencyException ex)
{
((IObjectContextAdapter)dal).ObjectContext.
Refresh(System.Data.Objects.RefreshMode.StoreWins, drugDetail);
// Set the notes data again
drugDetail.Notes = strNotes;
// Is it possible that DbUpdateConcurrencyException occur again here
dal.SaveChanges();
}
我担心从catch块调用SaveChanges()
时会再次出现并发冲突。我认为另一个用户可以在catch块中修改和保存刷新和保存之间的相同记录。
我担心有效吗?我在网上搜索找不到任何具体的答案。
我正在考虑在我的测试环境中创建这个场景并验证行为,但后来想到先在这里查看论坛。
感谢您的帮助。
根据要求添加DrugDetail类:
public partial class DrugDetail
{
public long id { get; set; }
public string DrugName { get; set; }
public Nullable<decimal> Dose { get; set; }
public Nullable<byte> Frequency { get; set; }
public string Method { get; set; }
public string Notes { get; set; }
public byte[] LastUpdated { get; set; }
}
答案 0 :(得分:1)
并发冲突可能随时发生。
使用循环重复此过程,直到您可以保存更改而不会发生并发冲突。
DataAccessLayer.Dal dal= new DataAccessLayer.Dal();
//Select record
DrugDetail drugDetail = (from drugRow in dal.DrugDetails
where drugRow.id == drugId
select drugRow).Single();
int count = 0;
while ( true )
{
//Assign notes
drugDetail.Notes = strNotes;
try
{
dal.SaveChanges();
break;
}
catch(DbUpdateConcurrencyException)
{
count++;
if ( count > 10 )
throw;
((IObjectContextAdapter)dal).ObjectContext.
Refresh(System.Data.Objects.RefreshMode.StoreWins, drugDetail);
}
}
答案 1 :(得分:0)
听起来你正试图自己构建自己的乐观并发机制。这可能不会很好地工作,因为数据总是有可能发生变化,即使是在您调用i
和SQL Server将数据写入表之间的微小时刻,而这些位仍在继续通过电线。
您需要进行特殊设置才能使其正常工作。如果您使用的是Entity Framework,则需要将LastUpdated列设置为并发令牌,如下所示:
SaveChanges
然后你需要处理在保存操作期间抛出的任何DbUpdateConcurrencyException。
modelBuilder.Entity<Department>().Property(p => p.LastUpdated).IsConcurrencyToken();
这样做允许EF以这样的方式构造SQL,即检查时间戳并在同一原子事务中更新数据。
如果你坚持自己做,你可以将阅读和保存包装在你自己的交易中(一定要选择合适的transaction isolation level)。但是这样效率会低得多,并且延长了行/页面/表被锁定的时间,如果你有很多用户,这将损害性能。
可以找到更多信息here。