使用Entity Framework解决乐观并发更新问题

时间:2011-01-15 11:16:09

标签: c# entity-framework optimistic-concurrency

当一个用户更新另一个用户已更新的entery时,我该如何解决模拟更新?

第一个用户请求'Category'entityobject,第二个用户也这样做 第二个用户更新此对象和第一个用户更新。

我在数据库中有字段时间戳字段,设置为并发模式 - 已修复。

这是我更新的方式:

public class CategoriesRepository : BaseCategoryRepository
{

     public  void Update(....)
    {
    try
     {
          Category catToUpdate = (from c in Contentctx.Categories where c.CategoryID == categoryId select c).FirstOrDefault();
          catToUpdate.SectionReference.EntityKey = new System.Data.EntityKey("ContentModel.Sections", "SectionID", sectionId);

          if (catToUpdate != null)
          {
               //Set fields here....
              Contentctx.SaveChanges();
          }

          base.PurgeCacheItems(CacheKey);
    }

    catch (OptimisticConcurrencyException ex)
    {

             }
    }

}
//Contentctx comes from base class: Contentctx:  
private ContentModel _contenttx;
        public ContentModel Contentctx
        {
            get
            {
                if ((_contenttx == null))
                {                   
                    _contenttx = new ContentModel();
                }

                return _contenttx;
            }
            set { _contenttx = value; }
        }



//on Business layer:
using (CategoriesRepository categoriesRepository = new CategoriesRepository())
{
            categoriesRepository.UpdateCategory(.....);
}

异常从未跳过......
我该怎么办呢?

1 个答案:

答案 0 :(得分:1)

您确定按照描述执行了一系列调用吗?无论如何,这里的主要问题是你不应该在Upate方法中使用查询返回的时间戳。您应该使用用户获取初始数据进行更新时收到的时间戳。它应该是:

  • 用户请求更新数据 - 从DB
  • 收到所有字段和时间戳
  • 时间戳存储在客户端(Web应用程序中的隐藏字段)
  • 用户修改数据并按“保存”按钮 - 包括旧时间戳在内的所有数据都将发送到processin
  • 更新方法加载当前实体
  • 所有更新的字段都与旧实体合并。实体的时间戳设置为第一步中收到的时间戳。
  • 称为SaveChanges

原因是它可以在第一次调用和更新方法调用之间传递大量时间,因此可能已经处理了多个更改。您必须使用初始时间戳来避免静默覆盖其他更改。

编辑:

// You are updating category - update probably means that you had to load category
// first from database to show actual values. When you loaded the category it had some 
// timestamp value. If you don't use that value, any change between that load and c
// calling this method will be silently overwritten.
public void Update(Category category)     
{     
    try      
    {           
        Category catToUpdate = (from c in Contentctx.Categories where c.CategoryID == categoryId select c).FirstOrDefault();           
        ...           

        // Now categoryToUpdate contains actual timestamp. But it is timestamp of
        // actual values loaded now. So if you use this timestamp to deal with 
        // concurrency, it will only fire exception if somebody modifies data 
        // before you call SaveChanges few lines of code bellow.

        if (catToUpdate != null)           
        {                
            //Set fields here....  

            // To ensure concurrency check from your initial load, you must 
            // use the initial timestamp.           
            catToUpdate.Timestamp = category.Timestamp;
            Contentctx.SaveChanges();           
        }

        ...
    }      
    catch (OptimisticConcurrencyException ex)     
    {               
        ...
    }     
}