简体   繁体   English

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

[英]Concurrent access of records in Entity Framework

I am working on finding an elegant solution to handle concurrency conflict in Entity Framework. 我正在努力寻找一种优雅的解决方案来处理Entity Framework中的并发冲突。

My table for administered drugs has a few columns like drug name, dose, frequency, administer method, notes and few more. 我的药物管理表格中有几列,例如药物名称,剂量,频率,给药方法,注意事项等等。 An operator can modify fields like dose, frequency, administer method etc after reviewing records OR they can just add notes. 操作员可以在查看记录后修改剂量,频率,管理方法等字段,也可以只添加注释。

SQL Server database is being used by us. 我们正在使用SQL Server数据库。

I have added a column LastUpdated of type timestamp . 我添加了timestamp类型的LastUpdated列。 I'm using this column to raise DbUpdateConcurrencyException . 我正在使用此列引发DbUpdateConcurrencyException The ConcurrencyMode for the LastUpdated column has been set to Fixed in the entity designer. 在实体设计器中, LastUpdated列的ConcurrencyMode已设置为Fixed

The code snippet from my controller class : 我的控制器类的代码片段:

// 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();
}

I'm worried that concurrency conflict can again occur when SaveChanges() is called from catch block. 我担心从catch块调用SaveChanges()时会再次发生并发冲突。 I think it is possible that another user can modify and save the same record between refresh and save in catch block. 我认为其他用户可以在刷新和保存在catch块之间修改和保存相同的记录。

Is my worried valid? 我的担心有效吗? I could not find any concrete answer by searching on net. 通过网络搜索,我找不到任何具体答案。

I was thinking to create this scenario in my test environment and verify behavior but then thought to check in forum here first. 我当时想在我的测试环境中创建这种情况并验证行为,但随后想到先在这里签入论坛。

Thanks for help. 感谢帮助。

As requested DrugDetail class is added here: 根据要求在此处添加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; }
}

Concurrency conflict may occur at any time. 并发冲突可能随时发生。

Use a loop to repeat the process until you can save the changes without a concurrency conflict. 使用循环重复该过程,直到您可以保存更改而不会发生并发冲突为止。

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);

    }
}

It sounds like you are attempting to homegrow your own optimistic concurrency mechanism. 听起来您正在尝试自行发展自己的乐观并发机制。 This probably won't work so well because there is always a chance the data has changed, even in the tiny instant between when you call SaveChanges and when SQL Server writes the data to the table, while the bits are still going over the wire. 这可能无法很好地工作,因为总是有数据更改的机会,即使在您调用SaveChanges和SQL Server将数据写入表之间的那SaveChanges之间,而这些位仍在传递。

You need special setup in order to make this work correctly. 您需要特殊的设置才能使其正常工作。 If you are using the Entity Framework, you need to set up the LastUpdated column as the concurrency token, with something like this: 如果使用的是实体框架,则需要使用以下内容将LastUpdated列设置为并发令牌:

modelBuilder.Entity<Department>().Property(p => p.LastUpdated).IsConcurrencyToken();

Then you'd need to handle any DbUpdateConcurrencyException thrown during the save operation. 然后,您需要处理在保存操作期间引发的任何DbUpdateConcurrencyException

bool saveFailed; 
do 
{ 
    saveFailed = false; 
    try 
    { 
        context.SaveChanges(); 
    } 
    catch (DbUpdateConcurrencyException ex) 
    { 
        saveFailed = true; 

        // Get the current entity values and the values in the database 
        var entry = ex.Entries.Single(); 
        var currentValues = entry.CurrentValues; 
        var databaseValues = entry.GetDatabaseValues(); 

        // Choose an initial set of resolved values. In this case we 
        // make the default be the values currently in the database. 
        var resolvedValues = databaseValues.Clone(); 

        // Have the user choose what the resolved values should be 
        HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues); 

        // Update the original values with the database values and 
        // the current values with whatever the user choose. 
        entry.OriginalValues.SetValues(databaseValues); 
        entry.CurrentValues.SetValues(resolvedValues); 
    } 
} while (saveFailed); 

Doing it this way allows the EF to construct the SQL in such a way that the time stamp is checked and the data are updated within the same, atomic transaction. 通过这种方式,EF可以通过检查时间戳和在同一原子事务内更新数据的方式来构造SQL。

If you insist on doing it yourself, you could wrap the read and the save in a transaction of your own (be sure to choose an appropriate transaction isolation level ). 如果您坚持要自己做,则可以将读取的内容和保存的内容包装在自己的事务中(一定要选择一个适当的事务隔离级别 )。 However this would be far less efficient and lengthen the time that the row/page/table is locked, which will hurt performance if you have a lot of users. 但是,这将大大降低效率,并延长行/页/表被锁定的时间,如果用户很多,则会损害性能。

More information can be found here . 可以在此处找到更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM