代码之家  ›  专栏  ›  技术社区  ›  Amirhossein Yari

按一个事务选择和更新

  •  1
  • Amirhossein Yari  · 技术社区  · 7 年前

    我有一些产品要出售,我想检查商店里现有的产品。 我使用实体框架和SQL Server。 我想要一些这样的想法。

    IF ((SELECT TOP 1 Amount FROM tblProduct WHERE Id =1)>0)
    UPDATE tblProduct
    SET count = count -1
    WHERE Id = 1
    

    但这不在一个交易中,当用户希望同时购买时,我无法正确检查它。我尝试了以下操作:

    BEGIN TRANSACTION
    IF ((SELECT TOP 1 Amount FROM tblProduct WHERE Id =1)>0)
        UPDATE tblProduct
        SET count = count -1
        WHERE Id = 1
    COMMIT
    

    但事实并非如此,我如何在实体框架中做到这一点呢? 谢谢

    2 回复  |  直到 7 年前
        1
  •  2
  •   Emre Savcı    7 年前

    据我所知,你们正试图减少每一个销售操作的产品数量。 你需要原子地这样做,因为你需要避免销售一个没有库存的产品。

    有几个选项,如乐观锁(optimatic lock)保留数据的版本,或分布式锁(distributed lock)提供项目的一次购买操作。

    在SQL中,可以使用如下版本控制运行update语句:

    update [table]
    set count = count - 1
    where id=1 and count=5 and version=3
    

    每个更新都会锁定该行,因此如果另一个更新请求正在执行,它会等待当前更新,并且在执行时会影响0行。

    您还可以将分布式锁与redis一起使用,并锁定进程本身。

    它取决于需求、用例和您的资源。

    更新

    对于使用实体框架执行此操作,如果首先使用代码,则可以执行以下方法:

    // add the below property to your entity
    [Timestamp]
    public byte[] RowVersion { get; set; }
    

    使用Fluent API:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<YourEntity>().Property(s => s.RowVersion).IsConcurrencyToken();
        //modelBuilder.Entity<YourEntity>().Property(s => s.RowVersion).IsRowVersion(); // in EF6 or EF Core
        base.OnModelCreating(modelBuilder);
    }
    

    或者你可以使用 [并发检查] 属性:

    [ConcurrencyCheck]
    public int Version { get; set; }
    [ConcurrencyCheck]
    public int Count { get; set; }
    

    ef将把这些列添加到更新查询中的where条件中。如果受影响的行数为0,则返回ef throw DBUpdateConcurrencyException 你得抓住它。

    了解更多信息:

    https://docs.microsoft.com/en-us/ef/core/modeling/concurrency

    Optimistic concurrency: IsConcurrencyToken and RowVersion

    https://www.codeproject.com/Articles/817432/Optimistic-Concurrency-in-Entity-Framework-Code-Fi

    http://www.binaryintellect.net/articles/14e67064-634c-4206-9eca-a42d3739594a.aspx

        2
  •  1
  •   gotqn user3521065    7 年前

    您可以尝试其他方法来实现这一点。例如,仅在存在数量时执行更新:

    UPDATE tblProduct
    SET count = count -1
    WHERE Id = 1
        AND Amount > 0;
    

    这样,就不需要 select 语句。或者,您可以添加 check constraint 确保 count 总是 > 0 并让SQL引擎应用ACID属性。如果某个用户(事务)试图将计数减少到0以下,则会引发一个错误,您可以在应用程序中捕获该错误(例如)。