none
============== 微软数据访问教程中有关业务逻辑执行的问题 ============= RRS feed

  • 问题

  • 原文(实际是翻译后转载的)链接

    https://www.jb51.net/article/83252.htm

    文章比较长,业务规则是:

    如果一个供应商仅仅供应一个产品的话,那么修改产品的信息的时候,是不能将这个产品的状态设置为停用.

    不用太纠结是哪种语言,也不用太纠结细节。。。

    代码如下:

    public bool UpdateProduct(string productName, int? supplierID, int? categoryID, string quantityPerUnit, decimal? unitPrice, short? unitsInStock, short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID) { Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID); if (products.Count == 0) // 根据productID查找时,没有找到匹配项,返回false return false;

    // product 即表示 Product 表中的一条记录 Northwind.ProductsRow product = products[0]; // 业务规则检查 – 不能停用某供应商所提供的唯一一个产品 if (discontinued) { // 获取我们从这个供应商处获得的所有产品 Northwind.ProductsDataTable productsBySupplier = Adapter.GetProductsBySupplierID(product.SupplierID); if (productsBySupplier.Count == 1) // 这是我们从这个供应商处获得的唯一一个产品,如果仅仅供应 1 个产品,则抛出异常 throw new ApplicationException("You cannot mark a product as discontinued if its the only product purchased from a supplier"); } product.ProductName = productName; if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value; if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value; if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit; if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value; if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value; if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value; if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value; product.Discontinued = discontinued; // 更新产品记录 int rowsAffected = Adapter.Update(product); // 如果刚好更新了一条记录,则返回true,否则返回false return rowsAffected == 1; }


    这里的问题是当执行 if (productsBySupplier.Count == 1) 时,此供应商供应了 2 个产品。继续执行 product.ProductName = productName; 时,

    在另一个CPU核心上,或是单核发生线程切换时,另一个用户删除了该供应商的一个产品的代码被完整的执行了。此时该供应商供应的产品数量为 1,

    那么接下来的逻辑将继续执行,但是这个供应商仅仅供应 1个产品了,却执行了 停用 ,也就违反了业务规则 。

    第一个问题,是不是将业务逻辑放到存储过程里来解决而不是应用层来解决更好

    第二个问题,如果根本上解决这个问题?

    多谢。

    2019年5月28日 14:33

全部回复

  • Hi heywap,

    你的问题与ASP.NET业务逻辑层更加有关,所以建议你去ASP.NET Forum进行提问。

    Regards,

    Kyle


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2019年5月30日 2:28
  • Hi heywap,

    要解决此问题我认为最好的方式是使用“锁”,来保证在某一刻只有一个对象访问该数据。你可以尝试使用“lock”或者“Mutex”。

    这里有一篇博客或许你可以参考一下:

    C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)

    Regards,

    Kyle

    Note: This response contains a reference to a third party World Wide Web site. Microsoft is providing this information as a convenience to you. Microsoft does not control these sites and has not tested any software or information found on these sites; Therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there. There are inherent dangers in the use of any software found on the Internet, and Microsoft cautions you to make sure that you completely understand the risk before retrieving any software from the Internet.


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2019年6月4日 1:32
  • 在该表中加一列表示并发性令牌,每次更新的时候检查该令牌与取出时相比是否发生变化。如发生了变化,即意味着在取出数据后和更新数据前这一段时间有其用户更新了记录,则不进行更新,更新失败;反之,创建新的令牌并更新记录。

    在你所提到的这个例子中,另一个用户删除供应商的一个产品或将一个产品停用,不仅更新该商品的并发令牌,还要将该供应商的其他产品的并发令牌一并更新。令牌变化则不更新可以在sql中使用where子句实现。

    那么代码会类似这样:

     Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
    if (products.Count == 0)
     
    // 根据productID查找时,没有找到匹配项,返回false
     
    return false;

    // product 即表示 Product 表中的一条记录
    Northwind.ProductsRow product = products[0];

    // 业务规则检查 – 不能停用某供应商所提供的唯一一个产品
    if (discontinued)
    {
     
    // 获取我们从这个供应商处获得的所有产品
     
    Northwind.ProductsDataTable productsBySupplier = Adapter.GetProductsBySupplierID(product.SupplierID);

     
    if (productsBySupplier.Count == 1)
     
    // 这是我们从这个供应商处获得的唯一一个产品,如果仅仅供应 1 个产品,则抛出异常
     
    throw new ApplicationException("You cannot mark a product as discontinued if its the only product purchased from a supplier");
    }

    product
    .ProductName = productName;
    if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value;
    if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value;
    if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit;
    if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value;
    if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value;
    if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value;
    if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value;
    product
    .Discontinued = discontinued;

    var oldToken = product.Token;product.Token = CreateNewToken();//CreateNewToken方法需要自行实现

    string sql = @"Update Products Set supplierID = @supplierID, categoryID = @categoryID ... 

    Token = @Token Where ID = @ID And Token = @oldToken";//我用...省略了中间的列

    var parameters = new SqlParameter[]()

    {

    new SqlParameter("@supplierID", product.supplierID),

    new SqlParameter("@categoryID ", product.categoryID ),

    ...

    new SqlParameter("@Token ", product.Token ),new SqlParameter("@ID ", product.ID),

    new SqlParameter("@oldToken", oldToken ),

    };

    var cmd = new SqlCommand(sql,Adapter.Connection);

    cmd.Parameters.AddRange(parameters);
    // 更新产品记录
    //int rowsAffected = Adapter.Update(product);

    int rowsAffected = cmd.ExcuteNonQuery();
     
    // 如果刚好更新了一条记录,则返回true,否则返回false
    return rowsAffected == 1;

    注意一下,更新该产品为禁用,同时需要更新其他该供应商其他产品的Token,我把这一步漏掉了,不过这个思路应该是清晰的。


    • 已编辑 anranruye 2019年6月8日 6:12
    • 已建议为答案 anranruye 2019年6月8日 6:14
    • 取消建议作为答案 anranruye 2019年6月8日 6:15
    2019年6月8日 6:06