Как инкрементировать значение поля в БД MSSQL с использованием Linq. Вариант с прочитать-прибавить-записать не подходит ввиду одновременного инкремента разными клиентами и потоками.
По ответам из комментов, попробовал сделать вот так:
public static async Task IncrementDownloadCounterAsync(Int32 id)
{
using (LICENSE_RO3Entities db = new LICENSE_RO3Entities())
{
using (DbContextTransaction transaction = db.Database.BeginTransaction())
{
DB.Update update = await db.Updates.FindAsync(id);
if (update != null)
{
update.download_counter++;
}
await db.SaveChangesAsync();
transaction.Commit();
}
}
}
Ответ
Для обновления одной записи никакая транзакция не нужна: EF умеет использовать оптимистическую блокировку для той же цели.
Если вы используете Code First - можно отметить свойство атрибутом [ConcurrencyCheck]. В таком случае EF при каждом обновлении записи будет дополнительно проверять что значение соответствующего атрибута в базе не изменилось.
Также можно добавить в модель свойство отмеченное атрибутом [Timestamp] типа byte[] если база данных поддерживает тип данных rowversion или его аналог. Отличие Timestamp от ConcurrencyCheck - в том, что ConcurrencyCheck только проверяется, а Timestamp еще и автоматически обновляется при любом изменении записи.
[Timestamp]
public byte[] RowVersion { get; set; }
В любом случае, какой бы из двух способов вы не выбрали - вы получите DbUpdateConcurrencyException если кто-то поменяет значение в базе пока вы собирались его сохранять. После этого можно будет попытаться увеличить счетчик еще раз.
Но самый нормальный способ - конечно же делать это средствами базы. EF не предоставляет способа делать это через Linq - поэтому надо использовать SQL-запрос:
db.Database.ExecuteSqlCommand("UPDATE Updates SET download_counter = download_counter + 1 WHERE Id = @p0", id);
Если есть желание изолировать вышележащие слои от SQL-кода - можно добавить этот запрос как метод контексту:
public void IncrementDownloadCounter(int id)
{
if (Database.ExecuteSqlCommand("UPDATE Updates SET download_counter = download_counter + 1 WHERE Id = @p0", id) == 0)
{
throw что-нибудь;
}
}
Комментариев нет:
Отправить комментарий