Упрощённый пример:
asp.net mvc приложение
Допустим, пользователь делает заказ
У нас есть класс OrderService - для размещения заказа и UserService для операций над денежным балансом
Размещение заказа делаем осуществляя ajax post запрос с клиента к контроллеру (HomeController) из которого уже дёргается OrderService
public class OrderService
{
private readonly UserService _userService;
private readonly DbContext _dbContext;
public OrderService(DbContext dbContext, UserService userService)
{
_userService = userService;
_dbContext = dbContext;
}
public int AddOrder(decimal totalSum)
{
var order = new OrderEntity
{
TotalSum = totalSum
};
var balance = _userService.GetBalance();
// проверяем баланс
if (balance < order.TotalSum)
{
throw new Exception("Not enough money");
}
// списываем деньги
_userService.OperateBalance(-order.TotalSum);
_dbContext.Set<OrderEntity>().Add(order);
_dbContext.SaveChanges();
return order.Id;
}
}
public class UserService
{
private readonly DbContext _dbContext;
public UserService(DbContext dbContext)
{
_dbContext = dbContext;
}
public decimal GetBalance()
{
return _dbContext.Set<UserEntity>().FirstOrDefault(x => x.Id == userId)?.Balance;
}
public void OperateBalance(decimal amount)
{
var user = _dbContext.Set<UserEntity>().FirstOrDefault(x => x.Id == userId);
if (user != null)
{
user.Balance += amount;
_dbContext.SaveChanges();
}
}
}
public class HomeController : Controller
{
private readonly IOrderService _service;
public HomeController(IOrderService service)
{
_service = service;
}
[HttpPost]
public JsonResult PostOrder(decimal totalSum)
{
var result = _service.AddOrder(totalSum);
return Json(result);
}
}
$.post('/Home/PostOrder', { totalSum: 150 })
.success(function(res) {
console.info('res1', res);
});
Проблема: если сделать два одновременных запроса, то два потока войдут одновременно в метод AddOrder и оба дадут верную проверку на баланс пользователя
Я вижу только одно решение - statick lock на весь метод
Это вообще нормальная практика? Есть какие-то ещё варианты?
p.s. DbContext управляется Unity через PerRequestLifetimeManager
UserService и OrderService - стандартно (PerResolveLifetimeManager)