解读Petshop3.2用Nhibernate重构系列(四)

这一节,我们来讲述如何实现一个一对多对象的操作。很显然的,在PetShop中最显著的就是Order和LineItem之间的关系了。
我们应该把Order对象和LineItem对象的保存处理在一个事务中。
在这一节,他利用了通过一个回调函数提供一个事务环境(TransHelper.cs)。
首先我们看看这些配置文件
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
    
<class name="PetShop.BLL.Order, PetShop.BLL" table="Orders">
        
<id name="OrderId" column="OrderId" type="Int32" unsaved-value="0" access="nosetter.camelcase-underscore">
            
<generator class="native"/>
        
</id>
        
<property type="String" length="20" name="UserId" column="UserId" not-null="true"/>
        
<property type="DateTime" not-null="true" column="OrderDate" name="Date"/>
        
<component name="CreditCard" class="PetShop.BLL.CreditCard, PetShop.BLL">
            
<property name="CardNumber" type="String" length="20" column="CreditCard" not-null="true"/>
            
<property name="CardExpiration" column="ExprDate" type="String" length="7" not-null="true"/>
            
<property name="CardType" column="CardType" length="40" not-null="true" type="String"/>
        
</component>
        
<component name="BillingAddress" class="PetShop.BLL.Address, PetShop.BLL">
            
<property name="Address1" column="BillAddr1" type="String" length="80" not-null="true"/>
            
<property name="Address2" column="BillAddr2" type="String" length="80" not-null="false"/>
            
<property name="City" column="BillCity" type="String" length="80" not-null="true"/>
            
<property name="State" column="BillState" type="String" length="80" not-null="true"/>
            
<property name="Zip" column="BillZip" type="String" length="20" not-null="true"/>
            
<property name="Country" column="BillCountry" type="String" length="20" not-null="true"/>
            
<property name="FirstName" column="BillToFirstName" type="String" length="80" not-null="true"/>
            
<property name="LastName" column="BillToLastName" type="String" length="80" not-null="true"/>
        
</component>
        
<component name="ShippingAddress" class="PetShop.BLL.Address, PetShop.BLL">
            
<property name="Address1" column="ShipAddr1" type="String" length="80" not-null="true"/>
            
<property name="Address2" column="ShipAddr2" type="String" length="80" not-null="false"/>
            
<property name="City" column="ShipCity" type="String" length="80" not-null="true"/>
            
<property name="State" column="ShipState" type="String" length="80" not-null="true"/>
            
<property name="Zip" column="ShipZip" type="String" length="20" not-null="true"/>
            
<property name="Country" column="ShipCountry" type="String" length="20" not-null="true"/>
            
<property name="FirstName" column="ShipToFirstName" type="String" length="80" not-null="true"/>
            
<property name="LastName" column="ShipToLastName" type="String" length="80" not-null="true"/>
        
</component>
        
<property name="OrderTotal" type="Decimal" column="TotalPrice" not-null="true"/>
        
<property name="Courier" type="String" column="Courier" not-null="true" length="80" access="field.camelcase-underscore"/>
        
<property name="Locale" type="String" column="Locale" not-null="true" length="20" access="field.camelcase-underscore"/>
        
<bag name="LineItems" access="nosetter.camelcase-underscore" inverse="true" cascade="save-update">
            
<key column="OrderId"/>
            
<one-to-many class="PetShop.BLL.CartItem, PetShop.BLL"/>
        
</bag>
        
<one-to-one name="Status" class="PetShop.BLL.OrderStatus, PetShop.BLL" cascade="save-update"/>
    
</class>
</hibernate-mapping>
包含了属性/组件/bag :)我只是知道大概,但是不知道怎么表达,没什么仔细研究:)
最重要的是bag--它可以用来指定一个一对多的关系。

接着我们来看接口
using System;

//References to PetShop specific libraries
//PetShop busines entity library
using PetShop.BLL;

namespace PetShop.IDAO{
    
/// <summary>
    
/// Interface for the Order DAL
    
/// </summary>

    public interface IOrderDAO{
        
/// <summary>
        
/// Method to insert an order header
        
/// </summary>
        
/// <param name="order">Business entity representing the order</param>

        void Insert(Order order);

        
/// <summary>
        
/// Reads the order information for a given orderId
        
/// </summary>
        
/// <param name="orderId">Unique identifier for an order</param>
        
/// <returns>Business entity representing the order</returns>

        Order GetOrder(int orderId);
    }

}

 
    这里定义了新增订单/根据订单ID获取订单的操作规范。
同时我们还需要看看IInventoryDAO接口

using System;

using PetShop.BLL;

namespace PetShop.IDAO{
    
/// <summary>
    
/// Interface for the Inventory DAL
    
/// </summary>

    public interface IInventoryDAO{
        
/// <summary>
        
///  Reduces the stock level by the given quantity for items in an order
        
/// </summary>
        
/// <param name="inventory"></param>

        void TakeStock(Inventory inventory);
    }

}


这个定义了减少库存的操作。
那么,接下来,我们可以来看看实体类

using System;
using System.Collections;
using PetShop.Helper;
using PetShop.IDAO;

namespace PetShop.BLL{
    
/// <summary>
    
/// Business entity used to model an order
    
/// </summary>

    [Serializable]
    
public class Order{
        
// These variables are used to demonstrate the rollback characterisitic 
        
// of distributed transactions and would not form part of a production application
        private const string ACID_USER_ID = "ACID";
        
private const string ACID_ERROR_MSG = "ACID test exception thrown for distributed transaction!";

        
private int _orderId;
        
private DateTime _date;
        
private string _userId;
        
private CreditCard _creditCard;
        
private Address _billingAddress;
        
private Address _shippingAddress;
        
private decimal _orderTotal;
        
private string _courier = "UPS";
        
private string _locale = "US_en";
        
private IList _lineItems;
        
private OrderStatus _status;

        
public int OrderId{
            
get return _orderId; }
        }


        
public DateTime Date{
            
get return _date; }
            
set { _date = value; }
        }


        
public string UserId{
            
get return _userId; }
            
set { _userId = value; }
        }


        
public CreditCard CreditCard{
            
get return _creditCard; }
            
set { _creditCard = value; }
        }


        
public Address BillingAddress{
            
get return _billingAddress; }
            
set { _billingAddress = value; }
        }


        
public Address ShippingAddress{
            
get return _shippingAddress; }
            
set { _shippingAddress = value; }
        }


        
public decimal OrderTotal{
            
get return _orderTotal; }
            
set { _orderTotal = value; }
        }


        
public IList LineItems {
            
get return _lineItems; }
            
set 
                
int lineNum = 1;
                
foreach(CartItem item in value){
                    item.LineNum 
= lineNum++;
                    item.Order   
= this;
                }

                _lineItems 
= value;
            }

        }

        
        
public OrderStatus Status {
            
get return _status; } 
            
set {
                _status 
= value;
                _status.Order 
= this;
            }

        }


        
public static Order Load(int orderId) {
            
// Validate input
            if (orderId < 1return null;

            
// Return the order from the DAL
            return ((IOrderDAO)ObjectFactory.GetInstance("OrderDAO")).GetOrder(orderId);
        }

        
        
public void Insert(){
            IManagedTransactionContext mtc 
= new TransHelper();
            mtc.DoCallback( 
new ContextCallback(InsertInTransaction) ); 
        }


        
/// <summary>
        
/// A method to insert a new order into the system
        
/// The orderId will be generated within the method and should not be supplied
        
/// As part of the order creation the inventory will be reduced by the quantity ordered
        
/// </summary>
        
/// <param name="order">All the information about the order</param>

        private void InsertInTransaction(){
            
            
// Call the insert method in the DAL to insert the header
            this.Status = new OrderStatus();

            ((IOrderDAO)ObjectFactory.GetInstance(
"OrderDAO")).Insert(this);
            
            
foreach(CartItem item in LineItems){
                item.TakeStock();
            }

        
            
// As part of the sample application we have created a user 
            
// you can tested distributed transactions with
            
// If the order has been created with the user 'Acid', 
            
// then throw an exception which will rollback the entire transaction
            if (this.UserId == ACID_USER_ID) throw new ApplicationException(ACID_ERROR_MSG);
        }

    }

}
我们在这里可以看到对于这种操作,他得通过一个回调函数来显式的控制事务。
这样我们就可以不用考虑那么多了,只需要考虑操作步骤。
   1.给Order对象的Status赋值
   2.通过对象工厂获取一个OrderDAO的借口,并执行新增Order操作。
     在这个步骤,Order LineItem OrderStatus被执行了插入操作。
   3.我们需要扣除已经销售的库存
      我们遍历LineItems属性,调用对应的扣除库存的操作

完成了。其实也不难,只是不知道方法而已:)
posted @ 2005-04-17 02:40  深渊野鱼  阅读(889)  评论(0编辑  收藏  举报