阳光的学长 發表於 2025-9-17 23:58:00

EF Core 与 MySQL:事务和并发处理详解

<div class="_7d763a7">
<div class="_48edb25">
<div class="e1675d8b ds-think-content">
<div class="ds-markdown">
<p class="ds-markdown-paragraph">本文将详细讲解EF Core与MySQL的事务和并发处理,分为三个部分:使用事务、处理并发冲突(乐观并发)以及悲观并发(MySQL中使用锁)。</p>
<ul>
<li>
<p class="ds-markdown-paragraph">使用事务<br>在EF Core中,可以使用事务来确保一系列操作要么全部成功,要么全部失败。EF Core支持多种事务管理方式,包括自动事务(SaveChanges自动包装事务)和显式事务。</p>






</li>
<li>
<p class="ds-markdown-paragraph">处理并发冲突(乐观并发)<br>乐观并发假设多个事务很少冲突,因此不会立即锁定资源。而是在更新时检查数据是否被其他事务修改过。EF Core通过配置并发令牌(Concurrency Token)来实现乐观并发。</p>



</li>
<li>
<p class="ds-markdown-paragraph">悲观并发(MySQL中使用锁)<br>悲观并发假设冲突经常发生,因此在读取数据时就会锁定资源,防止其他事务修改。在MySQL中,可以通过使用事务和SELECT ... FOR UPDATE语句来实现悲观并发。</p>



</li>



</ul>
<h2>1. 使用事务</h2>
<h3>基本事务用法</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用DbContext的Database.BeginTransaction()</span>
<span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> transaction =<span style="color: rgba(0, 0, 0, 1)"> context.Database.BeginTransaction())
{
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行多个数据库操作</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> product = <span style="color: rgba(0, 0, 255, 1)">new</span> Product { Name = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">New Product</span><span style="color: rgba(128, 0, 0, 1)">"</span>, Price = <span style="color: rgba(128, 0, 128, 1)">99.99m</span>, Stock = <span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)"> };
      context.Products.Add(product);
      
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> order = <span style="color: rgba(0, 0, 255, 1)">new</span> Order { CustomerId = <span style="color: rgba(128, 0, 128, 1)">1</span>, OrderDate =<span style="color: rgba(0, 0, 0, 1)"> DateTime.Now };
      context.Orders.Add(order);
      
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存更改</span>
<span style="color: rgba(0, 0, 0, 1)">      context.SaveChanges();
      
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交事务</span>
<span style="color: rgba(0, 0, 0, 1)">      transaction.Commit();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
    {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 回滚事务</span>
<span style="color: rgba(0, 0, 0, 1)">      transaction.Rollback();
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<h3>使用TransactionScope(分布式事务)</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 需要安装System.Transactions包</span>
<span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> scope = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 多个数据库操作</span>
      <span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> context1 = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationDbContext())
      </span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> context2 = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationDbContext())
      {
            context1.Products.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span> Product { Name = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Product 1</span><span style="color: rgba(128, 0, 0, 1)">"</span>, Price = <span style="color: rgba(128, 0, 128, 1)">10.99m</span><span style="color: rgba(0, 0, 0, 1)"> });
            context2.Orders.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span> Order { CustomerId = <span style="color: rgba(128, 0, 128, 1)">1</span>, OrderDate =<span style="color: rgba(0, 0, 0, 1)"> DateTime.Now });
            
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context1.SaveChangesAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context2.SaveChangesAsync();
      }
      
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 完成事务</span>
<span style="color: rgba(0, 0, 0, 1)">      scope.Complete();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
    {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 事务会自动回滚</span>
      <span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<h3>设置事务隔离级别</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> transaction =<span style="color: rgba(0, 0, 0, 1)"> context.Database.BeginTransaction(IsolationLevel.ReadCommitted))
{
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行操作</span>
      context.Products.Add(<span style="color: rgba(0, 0, 255, 1)">new</span> Product { Name = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">New Product</span><span style="color: rgba(128, 0, 0, 1)">"</span>, Price = <span style="color: rgba(128, 0, 128, 1)">99.99m</span><span style="color: rgba(0, 0, 0, 1)"> });
      context.SaveChanges();
      
      transaction.Commit();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
    {
      transaction.Rollback();
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<h3>异步事务处理</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt; ProcessOrderAsync(<span style="color: rgba(0, 0, 255, 1)">int</span> productId, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> quantity)
{
    </span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> transaction = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Database.BeginTransactionAsync())
    {
      </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查库存</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> product = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Products
                .FirstOrDefaultAsync(p </span>=&gt; p.Id ==<span style="color: rgba(0, 0, 0, 1)"> productId);
               
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (product == <span style="color: rgba(0, 0, 255, 1)">null</span> || product.Stock &lt;<span style="color: rgba(0, 0, 0, 1)"> quantity)
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 减少库存</span>
            product.Stock -=<span style="color: rgba(0, 0, 0, 1)"> quantity;
            context.Products.Update(product);
            
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建订单</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> order = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Order
            {
                ProductId </span>=<span style="color: rgba(0, 0, 0, 1)"> productId,
                Quantity </span>=<span style="color: rgba(0, 0, 0, 1)"> quantity,
                OrderDate </span>=<span style="color: rgba(0, 0, 0, 1)"> DateTime.Now
            };
            context.Orders.Add(order);
            
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.SaveChangesAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.CommitAsync();
            
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.RollbackAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    }
}</span></pre>
</div>
<h2>2. 处理并发冲突(乐观并发)</h2>
<h3>配置并发令牌</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在实体中定义并发令牌属性</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Product
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Id { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Name { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">decimal</span> Price { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Stock { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
   
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用时间戳作为并发令牌</span>
<span style="color: rgba(0, 0, 0, 1)">    1777992846
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">byte</span>[] Version { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
   
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或者使用自定义的并发令牌
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> public uint RowVersion { get; set; }</span>
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在DbContext中配置并发令牌</span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity</span>&lt;Product&gt;<span style="color: rgba(0, 0, 0, 1)">()
      .Property(p </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> p.Version)
      .IsRowVersion()
      .IsConcurrencyToken();
      
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或者使用非时间戳字段作为并发令牌</span>
    modelBuilder.Entity&lt;Product&gt;<span style="color: rgba(0, 0, 0, 1)">()
      .Property(p </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> p.Name)
      .IsConcurrencyToken();
}</span></pre>
</div>
<h3>处理并发异常</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;<span style="color: rgba(0, 0, 0, 1)"> UpdateProductAsync(Product updatedProduct)
{
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
    {
      context.Products.Update(updatedProduct);
      </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.SaveChangesAsync();
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (DbUpdateConcurrencyException ex)
    {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理并发冲突</span>
      <span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> entry <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> ex.Entries)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (entry.Entity <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> Product)
            {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取数据库中的当前值</span>
                <span style="color: rgba(0, 0, 255, 1)">var</span> databaseValues = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> entry.GetDatabaseValuesAsync();
               
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (databaseValues == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
                {
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实体已被删除</span>
                  <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
                }
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 转换为实体</span>
                <span style="color: rgba(0, 0, 255, 1)">var</span> databaseProduct =<span style="color: rgba(0, 0, 0, 1)"> (Product)databaseValues.ToObject();
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 决定如何解决冲突
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选项1: 使用客户端值
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> entry.OriginalValues.SetValues(databaseValues);
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> await context.SaveChangesAsync();
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选项2: 使用数据库值
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> entry.CurrentValues.SetValues(databaseValues);
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选项3: 合并值</span>
                <span style="color: rgba(0, 0, 255, 1)">var</span> currentValues =<span style="color: rgba(0, 0, 0, 1)"> entry.CurrentValues;
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> resolvedValues =<span style="color: rgba(0, 0, 0, 1)"> currentValues.Clone();
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 合并逻辑示例</span>
                resolvedValues[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = currentValues[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>]; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保留新名称</span>
                resolvedValues[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Price</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = databaseValues[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Price</span><span style="color: rgba(128, 0, 0, 1)">"</span>]; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用数据库价格</span>
                resolvedValues[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Stock</span><span style="color: rgba(128, 0, 0, 1)">"</span>] =<span style="color: rgba(0, 0, 0, 1)"> Math.Max(
                  (</span><span style="color: rgba(0, 0, 255, 1)">int</span>)currentValues[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Stock</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
                  (</span><span style="color: rgba(0, 0, 255, 1)">int</span>)databaseValues[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Stock</span><span style="color: rgba(128, 0, 0, 1)">"</span>]); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用较大库存
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置解析后的值</span>
<span style="color: rgba(0, 0, 0, 1)">                entry.OriginalValues.SetValues(databaseValues);
                entry.CurrentValues.SetValues(resolvedValues);
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 重试保存</span>
                <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.SaveChangesAsync();
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }
      
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<h3>自定义并发冲突解决策略</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ConcurrencyHandler
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;<span style="color: rgba(0, 0, 0, 1)"> ResolveConcurrencyAsync(
      DbUpdateConcurrencyException ex,
      ApplicationDbContext context)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> saved = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
      
      </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> entry <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> ex.Entries)
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取数据库当前值</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> databaseValues = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> entry.GetDatabaseValuesAsync();
            
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (databaseValues == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实体已被删除</span>
                entry.State =<span style="color: rgba(0, 0, 0, 1)"> EntityState.Detached;
                </span><span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取原始值和当前值</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> originalValues =<span style="color: rgba(0, 0, 0, 1)"> entry.OriginalValues;
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> currentValues =<span style="color: rgba(0, 0, 0, 1)"> entry.CurrentValues;
            
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据实体类型应用不同的解决策略</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (entry.Entity <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> Product product)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> ResolveProductConcurrency(entry, databaseValues);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (entry.Entity <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> Order order)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> ResolveOrderConcurrency(entry, databaseValues);
            }
      }
      
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 重试保存</span>
      <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.SaveChangesAsync();
            saved </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (DbUpdateConcurrencyException)
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果再次失败,可能需要递归处理或放弃</span>
<span style="color: rgba(0, 0, 0, 1)">      }
      
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> saved;
    }
   
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> Task ResolveProductConcurrency(
      EntityEntry entry,
      PropertyValues databaseValues)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> databaseProduct =<span style="color: rgba(0, 0, 0, 1)"> (Product)databaseValues.ToObject();
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> currentProduct =<span style="color: rgba(0, 0, 0, 1)"> (Product)entry.Entity;
      
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解决策略:保留较高的库存值,其他字段使用最新值</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> resolvedStock =<span style="color: rgba(0, 0, 0, 1)"> Math.Max(currentProduct.Stock, databaseProduct.Stock);
      
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更新实体</span>
<span style="color: rgba(0, 0, 0, 1)">      entry.OriginalValues.SetValues(databaseValues);
      currentProduct.Stock </span>=<span style="color: rgba(0, 0, 0, 1)"> resolvedStock;
    }
}</span></pre>
</div>
<h2>3. 悲观并发(MySQL中使用锁)</h2>
<h3>使用SELECT ... FOR UPDATE</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用原始SQL进行行级锁定</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;Product&gt; GetProductWithLockAsync(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> productId)
{
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 开始事务</span>
    <span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> transaction = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Database.BeginTransactionAsync(IsolationLevel.Serializable))
    {
      </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用SELECT ... FOR UPDATE锁定行</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> product = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Products
                .FromSqlRaw(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">SELECT * FROM Products WHERE Id = {0} FOR UPDATE</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, productId)
                .AsNoTracking()
                .FirstOrDefaultAsync();
               
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行需要原子性的操作
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
            
            <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.CommitAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> product;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.RollbackAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    }
}</span></pre>
</div>
<h3>使用EF Core的锁机制</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在EF Core中模拟行级锁</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt; ReserveProductAsync(<span style="color: rgba(0, 0, 255, 1)">int</span> productId, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> quantity)
{
    </span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> transaction = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Database.BeginTransactionAsync(IsolationLevel.Serializable))
    {
      </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取产品并锁定行</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> product = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Products
                .FirstOrDefaultAsync(p </span>=&gt; p.Id ==<span style="color: rgba(0, 0, 0, 1)"> productId);
               
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (product == <span style="color: rgba(0, 0, 255, 1)">null</span> || product.Stock &lt;<span style="color: rgba(0, 0, 0, 1)"> quantity)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.RollbackAsync();
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更新库存</span>
            product.Stock -=<span style="color: rgba(0, 0, 0, 1)"> quantity;
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.SaveChangesAsync();
            
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.CommitAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.RollbackAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    }
}</span></pre>
</div>
<h3>处理锁超时</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在MySQL连接字符串中设置锁等待超时</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> connectionString = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">server=localhost;database=efcoredb;user=root;password=yourpassword;</span><span style="color: rgba(128, 0, 0, 1)">"</span> +
    <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Connection Timeout=30;Default Command Timeout=30;</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或者在DbContext中配置</span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString),
      options </span>=&gt; options.EnableRetryOnFailure().CommandTimeout(<span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">));
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理锁超时异常</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt; TryUpdateWithLockAsync(<span style="color: rgba(0, 0, 255, 1)">int</span> productId, Action&lt;Product&gt;<span style="color: rgba(0, 0, 0, 1)"> updateAction)
{
    </span><span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">int</span> maxRetries = <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">;
   
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> attempt = <span style="color: rgba(128, 0, 128, 1)">0</span>; attempt &lt; maxRetries; attempt++<span style="color: rgba(0, 0, 0, 1)">)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> transaction = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Database.BeginTransactionAsync(IsolationLevel.Serializable))
      {
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取并锁定产品</span>
                <span style="color: rgba(0, 0, 255, 1)">var</span> product = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Products
                  .FirstOrDefaultAsync(p </span>=&gt; p.Id ==<span style="color: rgba(0, 0, 0, 1)"> productId);
                  
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (product == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
                  
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行更新操作</span>
<span style="color: rgba(0, 0, 0, 1)">                updateAction(product);
               
                </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.SaveChangesAsync();
                </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.CommitAsync();
               
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span> (MySqlException ex) when (ex.Number == <span style="color: rgba(128, 0, 128, 1)">1205</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Lock wait timeout</span>
<span style="color: rgba(0, 0, 0, 1)">            {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 锁等待超时,重试</span>
                <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.RollbackAsync();
               
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (attempt == maxRetries - <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)
                  </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
                  
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指数退避策略</span>
                <span style="color: rgba(0, 0, 255, 1)">await</span> Task.Delay(TimeSpan.FromMilliseconds(<span style="color: rgba(128, 0, 128, 1)">100</span> * Math.Pow(<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">, attempt)));
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.RollbackAsync();
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }
    }
   
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<h3>表级锁定</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用表级锁定(谨慎使用,影响性能)</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;<span style="color: rgba(0, 0, 0, 1)"> PerformMaintenanceAsync()
{
    </span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> transaction = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Database.BeginTransactionAsync(IsolationLevel.Serializable))
    {
      </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 锁定整个表</span>
            <span style="color: rgba(0, 0, 255, 1)">await</span> context.Database.ExecuteSqlRawAsync(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">LOCK TABLES Products WRITE</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行维护操作</span>
            <span style="color: rgba(0, 0, 255, 1)">await</span> context.Database.ExecuteSqlRawAsync(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">UPDATE Products SET Price = Price * 1.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解锁表</span>
            <span style="color: rgba(0, 0, 255, 1)">await</span> context.Database.ExecuteSqlRawAsync(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">UNLOCK TABLES</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.CommitAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">await</span> context.Database.ExecuteSqlRawAsync(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">UNLOCK TABLES</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> transaction.RollbackAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    }
}</span></pre>
</div>
<h2>4. 高级并发控制模式</h2>
<h3>使用版本号实现乐观并发</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> VersionedEntity
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Id { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">uint</span> Version { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用无符号整数作为版本号</span>
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置版本号并发令牌</span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity</span>&lt;Product&gt;<span style="color: rgba(0, 0, 0, 1)">()
      .Property(p </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> p.Version)
      .IsConcurrencyToken()
      .ValueGeneratedOnAddOrUpdate();
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在更新时自动增加版本号</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> SaveChanges()
{
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> entries =<span style="color: rgba(0, 0, 0, 1)"> ChangeTracker.Entries()
      .Where(e </span>=&gt; e.State == EntityState.Modified || e.State ==<span style="color: rgba(0, 0, 0, 1)"> EntityState.Added);
      
    </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> entry <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> entries)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (entry.Entity <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> VersionedEntity entity)
      {
            entity.Version</span>++<span style="color: rgba(0, 0, 0, 1)">;
      }
    }
   
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">.SaveChanges();
}</span></pre>
</div>
<h3>使用自定义锁机制</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实现简单的应用级锁机制</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationLockService
{
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ConcurrentDictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, SemaphoreSlim&gt; Locks =
      <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentDictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, SemaphoreSlim&gt;<span style="color: rgba(0, 0, 0, 1)">();
   
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;T&gt; ExecuteWithLockAsync&lt;T&gt;(<span style="color: rgba(0, 0, 255, 1)">string</span> lockKey, Func&lt;Task&lt;T&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> operation)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> lockObject = Locks.GetOrAdd(lockKey, key =&gt; <span style="color: rgba(0, 0, 255, 1)">new</span> SemaphoreSlim(<span style="color: rgba(128, 0, 128, 1)">1</span>, <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">));
      
      </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> lockObject.WaitAsync();
      
      </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> operation();
      }
      </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
      {
            lockObject.Release();
      }
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用应用级锁</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt; UpdateProductSafelyAsync(<span style="color: rgba(0, 0, 255, 1)">int</span> productId, Action&lt;Product&gt;<span style="color: rgba(0, 0, 0, 1)"> updateAction)
{
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> lockService = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationLockService();
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> lockKey = $<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">product_{productId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
   
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> lockService.ExecuteWithLockAsync(lockKey, <span style="color: rgba(0, 0, 255, 1)">async</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> context = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationDbContext())
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> product = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.Products.FindAsync(productId);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (product == <span style="color: rgba(0, 0, 255, 1)">null</span>) <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            
            updateAction(product);
            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> context.SaveChangesAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    });
}</span></pre>
</div>
<h2>5. 监控和诊断并发问题</h2>
<h3>记录并发冲突</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ConcurrencyLogger
{
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;ConcurrencyLogger&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;
   
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> ConcurrencyLogger(ILogger&lt;ConcurrencyLogger&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
    {
      _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
    }
   
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> LogConcurrencyConflict(DbUpdateConcurrencyException ex, <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> operation)
    {
      _logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">并发冲突发生在操作: {Operation}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, operation);
      
      </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> entry <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> ex.Entries)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> databaseValues =<span style="color: rgba(0, 0, 0, 1)"> entry.GetDatabaseValues();
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> currentValues =<span style="color: rgba(0, 0, 0, 1)"> entry.CurrentValues;
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> originalValues =<span style="color: rgba(0, 0, 0, 1)"> entry.OriginalValues;
            
            _logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">实体: {EntityType}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, entry.Metadata.Name);
            _logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">数据库值: {DatabaseValues}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, databaseValues?<span style="color: rgba(0, 0, 0, 1)">.Properties);
            _logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">当前值: {CurrentValues}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, currentValues?<span style="color: rgba(0, 0, 0, 1)">.Properties);
            _logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">原始值: {OriginalValues}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, originalValues?<span style="color: rgba(0, 0, 0, 1)">.Properties);
      }
    }
}</span></pre>
</div>
<h3>性能计数器监控</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 监控并发冲突率</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ConcurrencyMonitor
{
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> _totalOperations;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> _concurrencyExceptions;
   
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> RecordOperation(<span style="color: rgba(0, 0, 255, 1)">bool</span> hadConcurrencyException = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
    {
      Interlocked.Increment(</span><span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> _totalOperations);
      
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (hadConcurrencyException)
      {
            Interlocked.Increment(</span><span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> _concurrencyExceptions);
      }
    }
   
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">double</span><span style="color: rgba(0, 0, 0, 1)"> GetConcurrencyExceptionRate()
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> total = Interlocked.Read(<span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> _totalOperations);
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> exceptions = Interlocked.Read(<span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> _concurrencyExceptions);
      
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> total &gt; <span style="color: rgba(128, 0, 128, 1)">0</span> ? (<span style="color: rgba(0, 0, 255, 1)">double</span>)exceptions / total : <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<h2>总结</h2>
<p class="ds-markdown-paragraph">本文详细介绍了EF Core与MySQL中的事务和并发处理,包括:</p>
<ol>
<li class="ds-markdown-paragraph"><strong>事务管理:</strong></li>
</ol><ol start="1">
<li style="list-style-type: none">
<ul>
<li>
<p class="ds-markdown-paragraph">基本事务用法和TransactionScope</p>
</li>
<li>
<p class="ds-markdown-paragraph">设置事务隔离级别</p>
</li>
<li>
<p class="ds-markdown-paragraph">异步事务处理</p>
</li>
</ul>
</li>
<li>
<p class="ds-markdown-paragraph"><strong>乐观并发控制:</strong></p>
<ul>
<li>
<p class="ds-markdown-paragraph">配置并发令牌(时间戳或自定义字段)</p>
</li>
<li>
<p class="ds-markdown-paragraph">处理DbUpdateConcurrencyException</p>
</li>
<li>
<p class="ds-markdown-paragraph">实现自定义冲突解决策略</p>
</li>
</ul>
</li>
<li>
<p class="ds-markdown-paragraph"><strong>悲观并发控制:</strong></p>
<ul>
<li>
<p class="ds-markdown-paragraph">使用SELECT ... FOR UPDATE进行行级锁定</p>
</li>
<li>
<p class="ds-markdown-paragraph">处理锁超时和重试机制</p>
</li>
<li>
<p class="ds-markdown-paragraph">表级锁定(谨慎使用)</p>
</li>
</ul>
</li>
<li>
<p class="ds-markdown-paragraph"><strong>高级并发模式:</strong></p>
<ul>
<li>
<p class="ds-markdown-paragraph">版本号实现乐观并发</p>
</li>
<li>
<p class="ds-markdown-paragraph">应用级锁机制</p>
</li>
<li>
<p class="ds-markdown-paragraph">监控和诊断并发问题</p>
</li>
</ul>
</li>
</ol>
<p class="ds-markdown-paragraph">在实际应用中,应根据具体场景选择合适的并发控制策略:</p>
<ul>
<li>
<p class="ds-markdown-paragraph">对于读多写少的场景,乐观并发通常更高效</p>
</li>
<li>
<p class="ds-markdown-paragraph">对于写密集或需要强一致性的场景,悲观并发可能更合适</p>
</li>
<li>
<p class="ds-markdown-paragraph">总是要考虑超时和重试机制,以提高系统的健壮性</p>
</li>
<li>
<p class="ds-markdown-paragraph">监控并发冲突率,及时发现和解决性能瓶颈</p>
</li>
</ul>
<p class="ds-markdown-paragraph">正确实施事务和并发控制是构建高并发、高可用应用程序的关键。建议在生产环境中进行充分的压力测试,确保并发控制策略能够满足实际需求。</p>
</div>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/jixingsuiyuan/p/19097867
頁: [1]
查看完整版本: EF Core 与 MySQL:事务和并发处理详解