使用实体框架解决乐观并发更新问题

发布于 2024-10-11 20:39:35 字数 1430 浏览 3 评论 0原文

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

第一个用户请求“类别”实体对象,第二个用户执行相同操作。
第二个用户更新此对象,第一个用户更新。

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

这就是我更新的方式:

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

异常永远不会跳转...
我应该如何处理这个问题?

How should I solve simulation update, when one user updates already updated entery by another user?

First user request 'Category' entityobject, second user does the same.
Second user updates this object and first user updates.

I have field timestamp field in database that wa set to Concurrency Mode - Fixed.

This is how I update:

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

Exception never jumps...
How should I handle this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

得不到的就毁灭 2024-10-18 20:39:35

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

  • 用户请求更新数据 - 所有字段和时间戳均从数据库接收
  • 时间戳存储在客户端上(Web 应用程序中的隐藏字段)
  • 用户修改数据并按下“保存”按钮 - 包括旧时间戳在内的所有数据都发送到处理
  • 更新方法加载当前实体
  • 所有更新的字段都与旧实体合并。实体的时间戳设置为第一步中收到的时间戳。
  • 调用 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)     
    {               
        ...
    }     
} 

Are you sure that sequence of calls is performed as you described? Anyway the main problem here is that you should not use the timestamp returned by the query in Upate method. You should use timestamp received when user get initial data for update. It should be:

  • User requests data from update - all fields and timestamp are received from DB
  • Timestamp is stored on client (hidden field in web app)
  • User modifies data and pushes Save button - all data including old timestamp are send to processin
  • Update method loads current entity
  • All updated fields are merged with old entity. Timestamp of entity is set to timestamp received in the first step.
  • SaveChanges is called

The reason for this is that it can pass a lot of time between first call and update method call so there can be several changes already processed. You have to use initial timestamp to avoid silent overwritting other changes.

Edit:

// 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)     
    {               
        ...
    }     
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文