技术开发 频道

NHibernate 基本整合:定义领域层


    设置命令集合设值函式到被保护的区域,允许NHibernate 组装 集合 ,而且不需要直接依赖一个私人会员,或者不需要公用地展现设值函式。另外一个选择就是:NHibernate setting access="field"可以用于直接设置一个私人会员,但是应该仔细考虑使用这个,而且只有被批准的时候才能用。在上面的样本代码中,注意AddOrder 和RemoveOrder对Customer 类型所做的。通过增添集合管理到包含类型,它们厚颜无耻地污染了这个类型。如果Customer结束,仍有许多 集合以及管理每一个的方法,想象这样子会导致的麻烦。如果有这样的例子,最好采用定制 集合。

   作为使用定制集合的简单例子,Supplier类型可能有一些Product 条目与它连接在一起。警告人们NHibernate需要映射到IList 集合,因此,就会保持两个集合。第一个是产品集合自己,这是NHibernate 所注意到的,第二个就是产品集合包装纸,此包装纸显示了定制集合。下面来自于Supplier.cs 和 Products.cs的代码就表示了这些。
// Within Supplier.cs... 

public Products Products {
get {
if (productsWrapper == null) {
productsWrapper = new Products(products);
}

return productsWrapper;
}
}

private Products productsWrapper;
// NHibernate binds directly to this member by the access="field" setting
private IList products = new List<Product>();


// Within Products.cs

public Products(IList products) {
this.products = products;
}

public void Add(Product product) {
if (product != null && !products.Contains(product)) {
products.Add(product);
}
}

private IList products;
   尽管很简单,示例代码应该满足你的定制集合的大部分要求。但是在一些情况中,一个成熟的泛型,要求有定制的集合。其中一个最常见的方案包括创建一个定制集合,此定制集合执行BindingList.在这些情况中,有必要综合利用NHibernate's IUser 集合Type.在这里将找到执行这个的好例子。

Generic IDs and Object Comparisons

    BasicSample.Core,中,每一个可持续的领域对象都从DomainObject 继承而来。这个类型处理与比较两个领域对象以求平等有关的大部分工作。(在devlicio.us blog post中,将找到这个对象的详细讨论)。DomainObject 也是一个泛型,接受数据类型,此数据类型宣告领域对象的ID 类型。这个泛型属性提供了拥有一个领域对象的能力,此领域对象使用一个字符串作为ID ,例如:Customer, 且另一个领域对象拥有long 作为ID,例如:Order. 。

    应该适时地注意到:在这篇文章的前面部分,ID 属性有一个公用的取值函式 和设值函式。尽管取值函式 很重要,公用的设值函式污染现存的数据,以此打开了罪恶之源。假设你从数据库重新找到一个顾客,偶然地,把它的ID 设置到了另一个客户的ID 。当客户返回到数据库时,它的数据比另一个客户的数据多。举另一个更微妙的例子,假设Customer 被当作伪造的DTO 使用,此伪造的 DTO 正从一个编辑屏幕返回。有一个公用的ID 取值函式 ,开发者设置了ID 以及可以应用的属性,把它返回到另一个对象,此对象处理将DTO 信息传输到“真”客户(此客户是从数据库得到的)。

    要提醒你的是:DTO 和“真”客户都是作为Customer 实例被传递的。 因此如果这样,在保持状态中,一个开发者没有意识到从现场返回的Customer 应该当作DTO 对待,而不是一个真的客户,继续在它上面直接调用保存 ,然后因为它使用稀有的DTO数据,写得过多,它很可能丢失许多现场客户数据。这一现象后面的主要缺点就是ID 有公用设值函式这一事实。因此,当从数据库下载对象时,领域对象的ID只被NHibernate设置,并且不在公用设配里出现,这样是最好的。有一些这样的情况:领域对象有一个被指定的ID.(我没有找到指派的IDs的空间,尽可能的避免它们,但是应该知道要建立一个变元供它们使用。在BasicSample.Web/AddCustomer.aspx里你能找到进一步的信息。)当要求这个被指定的ID时,包括一个叫做IHasAssignedId的接口。因此即使它为设置ID提供了初步方法,但是它需要更多的想法,此想法与它的使用有关,并且提供一个好场地,以此来包含ID-委派商业逻辑。下面就是snippet 例证了Customer类型里的这个。
public class Customer : DomainObject<string>, IHasAssignedId<string>
{
public void SetAssignedIdTo(string assignedId) {
Check.Require(!string.IsNullOrEmpty(assignedId),
"assignedId may not be null or empty");
// As an alternative to Check.Require,
the Validation Application Block could be used for the following
Check.Require(assignedId.Trim().Length == 5,
"assignedId must be exactly 5 characters");

ID = assignedId.Trim().ToUpper();
}
...
}
    不显示一个公用设值函式的明显缺陷就是:这个属性不能用于单元测试结构,除非把NHibernate用于下载项目。为了绕过这个问题,类型BasicSample.Tests/Domain/DomainObjectIdSetter.cs使你能够建立领域对象ID,即使它们不能执行IHasAssignedId.这一能力在没有给ID属性提供公用设值函式时,也能打开许多测试可能性。在flipside上,使用反射设置私人会员,当绝对必要时,应该被看成是非标准惯例,,对单元测试有好处。因为它建立在字符串的基础上,它能增加复杂性,而且它很脆弱。但是对于设置领域对象得ID性能,它十分适合单元测试层。
0
相关文章