实体框架中记录的并发访问

时间:2017-06-22 22:19:58

标签: c# sql entity-framework

我正在努力寻找一种优雅的解决方案来处理Entity Framework中的并发冲突。

我的药物管理表有几栏,如药物名称,剂量,频率,给药方法,注释等等。操作员可以在查看记录后修改剂量,频率,管理方法等字段,也可以添加注释。

我们正在使用SQL Server数据库。

我添加了LastUpdated类型的列timestamp。我正在使用此列提升DbUpdateConcurrencyExceptionConcurrencyMode列的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; }
}

2 个答案:

答案 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