通过一项交易选择并更新

时间:2018-11-29 10:59:35

标签: c# sql-server entity-framework transactions entity-framework-6

我有一些产品要出售,我想检查商店中产品的存在。 我使用实体框架和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 个答案:

答案 0 :(得分:2)

据我了解,您正在尝试减少每次销售操作中的产品数量。 您需要原子地执行此操作,因为您需要避免出售缺货的产品。

有几种选择,例如在数据上保留版本的乐观锁,或为商品提供一次购买操作的分布式锁。

在sql中,您可以运行具有以下版本控制的更新语句:

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

使用流利的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] 属性:

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

EF会将这些列添加到更新查询中的where条件。如果受影响的行计数为0,则EF引发 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

答案 1 :(得分:1)

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

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

通过这种方式,select语句中不再需要。或者,您可以添加check constraint以确保count始终为> 0,并让SQL引擎应用ACID属性。如果用户(事务)以某种方式尝试将计数减少到0以下,则将引发错误,您可以在应用程序中捕获该错误(例如)。