C# 两个客户端之间的数据同步(验证)

C# 两个客户端之间的数据同步(验证),c#,asp.net-core,async-await,aspnetboilerplate,abp,C#,Asp.net Core,Async Await,Aspnetboilerplate,Abp,我有一个基于ASP.NET样板(Angular frontend和MSSQL数据库)的网站商店。 webshop包含项目,我想保留这些项目的清单。 每次创建订单时,都会更新库存。所以基本上我有一个包含网店、商品和订单的数据库。 我有这些对象的存储库和管理器 所有这些都可以正常工作,但当两个客户端同时加载webshop时就会出现问题 客户端1打开网页: 网店1 第1项:“10项可用” 第2项:“8项可用” Client2同时打开网页: 网店1 第1项:“10项可用” 第2项:“8项可用

我有一个基于ASP.NET样板(Angular frontend和MSSQL数据库)的网站商店。
webshop包含项目,我想保留这些项目的清单。 每次创建订单时,都会更新库存。所以基本上我有一个包含网店、商品和订单的数据库。
我有这些对象的存储库和管理器

所有这些都可以正常工作,但当两个客户端同时加载webshop时就会出现问题

客户端1打开网页:

  • 网店1
    • 第1项:“10项可用”
    • 第2项:“8项可用”
Client2同时打开网页:

  • 网店1
    • 第1项:“10项可用”
    • 第2项:“8项可用”
第一个购买了所有可用物品的人应该能够创建订单,第二个应该会出错

创建订单时,后端会检查是否有足够的可用项目。 但是,当在创建第一个客户端的订单之前加载webshop时,第二个客户端不知道更新的库存,也可以创建订单。
这意味着Item1的20件商品可以出售

如何在后端的两个会话之间“同步”数据?加载webshop时,数据似乎被缓存在后端

CreateOrder函数

public async Task<CreateOrderResponseDto> Create(CreateOrderDto input, long? userId)
{
    input.OrderItems.ForEach(async o =>
    {
        if (!(await _salesItemManager.ReserveStock(o.SalesItemId, o.Quantity)).IsSuccess)
        {
            throw CodeException.ToAbpValidationException("OrderItem", "OrderItemCreate");
        }
    });


    var salesPage = await _salesPageManager.Get(input.SalesPageId, false);

    if (salesPage.GetState() != StatePage.Published)
    {
        throw CodeException.ToAbpValidationException("Order", "PageNotAvailable");
    }

    if (salesPage.CommentsRequired.HasValue && salesPage.CommentsRequired.Value)
    {
        if (string.IsNullOrWhiteSpace(input.Description))
        {
            throw CodeException.ToAbpValidationException("Order", "CommentsRequired");
        }
    }

    var order = new Order
    {
        Address = input.Address,
        City = input.City,
        LastName = input.LastName,
        Name = input.Name,
        PostalCode = input.PostalCode,
        Email = input.Email,
        PhoneNumber = input.PhoneNumber,
        Description = input.Description,
        SalesPage = salesPage
    };


    try
    {
        order.Price = await _salesItemManager.GetPriceByOrders(input.OrderItems);
        order = await _orderRepository.InsertAsync(order);

        input.OrderItems.ForEach(async o =>
        {
            var orderItem = new OrderItem();
            orderItem.SalesItemId = o.SalesItemId;
            orderItem.OrderId = order.Id;
            orderItem.Quantity = o.Quantity;

            await _orderItemRepository.InsertAsync(orderItem);
        });

        if (input.SelectedSalesPageOptionId.HasValue)
        {
            order.SalesOption = await _salesPageManager.GetOption(input.SelectedSalesPageOptionId.Value);
        }

    }
    catch (Exception e)
    {
        throw CodeException.ToAbpValidationException("OrderItem", "OrderItemCreate");
    }

    if (userId.HasValue && salesPage.User.Id == userId.Value)
    {
        var payment = await _paymentManager.CreateManualPayment(order, input.IsPaid);
        order.Payment = payment;

        return new CreateOrderResponseDto() { IsSuccess = true, PaymentUrl = string.Empty, OrderId = order.Id.ToString() };
    }
    else
    {
        var payment = await _paymentManager.CreatePayment(order);
        order.Payment = payment;
        return new CreateOrderResponseDto() { IsSuccess = true, PaymentUrl = payment.PaymentUrl, OrderId = order.Id.ToString() };
    }
}
公共异步任务创建(CreateOrderDto输入,长?用户ID)
{
input.OrderItems.ForEach(异步o=>
{
如果(!(等待_salesItemManager.ReserveStock(o.SalesItemId,o.Quantity)).IsSuccess)
{
抛出CodeException.ToAbpValidationException(“OrderItem”、“OrderItemCreate”);
}
});
var salesPage=await\u salesPageManager.Get(input.SalesPageId,false);
if(salesPage.GetState()!=StatePage.Published)
{
抛出CodeException.ToAbpValidationException(“订单”,“页面不可用”);
}
if(salesPage.CommentsRequired.HasValue&&salesPage.CommentsRequired.Value)
{
if(string.IsNullOrWhiteSpace(input.Description))
{
抛出CodeException.ToAbpValidationException(“Order”,“CommentsRequired”);
}
}
var订单=新订单
{
地址=输入。地址,
城市=输入。城市,
LastName=input.LastName,
Name=input.Name,
PostalCode=input.PostalCode,
Email=input.Email,
PhoneNumber=input.PhoneNumber,
Description=输入。Description,
SalesPage=SalesPage
};
尝试
{
order.Price=await\u salesItemManager.GetPriceByOrders(input.OrderItems);
订单=等待_orderRepository.InsertAsync(订单);
input.OrderItems.ForEach(异步o=>
{
var orderItem=new orderItem();
orderItem.SalesItemId=o.SalesItemId;
orderItem.OrderId=order.Id;
orderItem.Quantity=o.数量;
wait_orderItemRepository.InsertAsync(orderItem);
});
if(input.SelectedSalesPageOptionId.HasValue)
{
order.salespageoption=wait\u salesPageManager.GetOption(input.SelectedSalesPageOptionId.Value);
}
}
捕获(例外e)
{
抛出CodeException.ToAbpValidationException(“OrderItem”、“OrderItemCreate”);
}
if(userId.HasValue&&salesPage.User.Id==userId.Value)
{
var payment=Wait_paymentManager.CreateManualPayment(订单,输入.IsPaid);
订单.付款=付款;
返回新的CreateOrderResponseDto(){IsSuccess=true,PaymentUrl=string.Empty,OrderId=order.Id.ToString()};
}
其他的
{
var payment=Wait_paymentManager.CreatePayment(订单);
订单.付款=付款;
返回新的CreateOrderResponseDto(){IsSuccess=true,PaymentUrl=payment.PaymentUrl,OrderId=order.Id.ToString()};
}
}
储备股票函数

public async Task<GeneralDto> ReserveStock(Guid itemId, int quantity)
{
    var salesItem = await _salesItemRepository.GetAsync(itemId);

    if (salesItem == null || salesItem.Stock == null || salesItem.ReservedStock == null)
        return new GeneralDto() { IsSuccess = false };

    if (salesItem.Stock < quantity)
    {
        return new GeneralDto() { IsSuccess = false };
    }

    salesItem.Stock -= quantity;
    salesItem.ReservedStock += quantity;

    try
    {
        await _salesItemRepository.UpdateAsync(salesItem);
    }
    catch (Exception e)
    {
        throw CodeException.ToAbpValidationException("SalesItem", "SalesItemUpdate");
    }

    return new GeneralDto() { IsSuccess = true };
}
public异步任务ReserveStock(Guid itemId,int quantity)
{
var salesItem=await\u salesItemRepository.GetAsync(itemId);
如果(salesItem==null | | | salesItem.Stock==null | | | salesItem.ReservedStock==null)
返回新的GeneralTo(){IsSuccess=false};
if(salesItem.Stock<数量)
{
返回新的GeneralTo(){IsSuccess=false};
}
salesItem.Stock-=数量;
salesItem.ReservedStock+=数量;
尝试
{
wait\u salesItemRepository.UpdateAsync(salesItem);
}
捕获(例外e)
{
抛出CodeException.ToAbpValidationException(“SalesItem”、“SalesItemUpdate”);
}
返回新的GeneralTo(){IsSuccess=true};
}

问题不在于数据被缓存

问题在于您的
ReserveStock
检查是调用方不等待的异步任务:

input.OrderItems.ForEach(异步o=>
{
如果(!(等待_salesItemManager.ReserveStock(o.SalesItemId,o.Quantity)).IsSuccess)
{
抛出CodeException.ToAbpValidationException(“OrderItem”、“OrderItemCreate”);
}
});
将异步
ReserveStock
检查分配给调用者等待的任务数组:

var reserveStockChecks=input.OrderItems.Select(异步o=>
{
如果(!(等待_salesItemManager.ReserveStock(o.SalesItemId,o.Quantity)).IsSuccess)
{
抛出CodeException.ToAbpValidationException(“OrderItem”、“OrderItemCreate”);
}
}).ToArray();
等待任务。WhenAll(保留库存检查);

在订单的最后一步,即结账时,应用程序需要在后端再次检查库存项目的可用性。然后向用户显示错误。感谢您的评论。我就是这么做的。在后端,在下订单之前,我检查itemrepository中的可用项。但不知何故,会话的数据被“兑现”,后端不知道同时有人购买了一些项目。如果我在其他用户购买后刷新webshop