Выбрать и обновить одной транзакцией - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть продукт на продажу, и я хочу проверить наличие товара в магазине.Я использую Entity Framework и SQL-сервер.Я хочу, чтобы некоторые думали так.

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 голосов
/ 29 ноября 2018

Вы можете попробовать другие способы реализации этого.Например, выполнять обновление только при наличии количества:

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

Таким образом, нет необходимости в операторе select.Или вы можете добавить check constraint, чтобы count всегда было > 0, и оставить SQL Engine для применения свойств ACID.Если каким-либо образом пользователь (транзакция) попытается уменьшить число ниже 0, будет выдано сообщение об ошибке, и вы можете отловить ошибку в приложении (например).

0 голосов
/ 29 ноября 2018

Как я понимаю, вы пытаетесь уменьшить количество товара при каждой операции продажи.Вы должны сделать это атомарно, потому что вам нужно избегать продажи товара, которого нет в наличии.

Есть несколько вариантов, таких как оптимистическая блокировка с сохранением версии на ваших данных или распределенная блокировка для обеспечения одной операции покупки на предмете..

В sql вы можете запустить оператор обновления с контролем версий, как показано ниже:

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

Каждое обновление блокирует строку, поэтому, если в действие вступает другой запрос на обновление, оно ожидает текущее обновление и при выполнении оно влияет на0 строка.

Вы также можете использовать распределенную блокировку с Redis и заблокировать сам процесс.

Это зависит от требований, вариантов использования и ваших ресурсов.

Обновление

Для того, чтобы сделать это с помощью Entity Framework, если вы сначала используете код, вы можете сделать следующий подход:

// 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 throws DbUpdateConcurrencyException , вам нужно его перехватить.

Ознакомьтесь с дополнительной информацией:

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

Оптимистичный параллелизм: IsConcurrencyToken и 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

...