我认为实体应该通过主键比较实现相等作为默认值,但是nhibernate文档建议使用业务标识:
最明显的方法是通过比较两个对象的标识符值来实现Equals()/ GetHashCode()。如果值相同,则两者必须是相同的数据库行,因此它们是相等的(如果两者都添加到ISet,我们在ISet中只有一个元素)。不幸的是,我们不能使用这种方法。 NHibernate只会将标识符值分配给持久化的对象,新创建的实例将不具有任何标识符值!我们建议使用Business key equality实现Equals()和GetHashCode()。
业务密钥相等意味着Equals()方法仅比较构成业务密钥的属性,这是一个在现实世界中识别我们实例的密钥(自然候选密钥)
示例(也来自doc):
public override bool Equals(object other)
{
if (this == other) return true;
Cat cat = other as Cat;
if (cat == null) return false; // null or not a cat
if (Name != cat.Name) return false;
if (!Birthday.Equals(cat.Birthday)) return false;
return true;
}
这引起了我的关注,因为业务标识的概念(根据示例)与通过语法进行比较相同,这基本上是我与ValueObjects关联的语义类型。不使用数据库主键作为比较值的原因是,如果未在客户端生成主键(对于ex incremental),并且您使用某种哈希表集合(例如ISet),这将更改对象的哈希码用于存储您的实体。
如何创建一个良好的相等实现,它不会违反相等/哈希码(http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx)的一般规则并且也符合nhibernate规则?
答案 0 :(得分:13)
这是ORM的已知问题。在这里,我概述了我所知道的解决方案并给出了一些指示。
1代理/主键:自动生成
正如您所提到的,如果对象尚未保存,则无效。
2代理/主键:分配值
您可以决定在代码中分配PK的值,这样对象始终具有ID并可用于比较。见Don't let hibernate steal your identity。
3自然键
如果对象具有另一个自然键,而不是主键,则可以使用此键。对于客户端实体来说就是这种情况,客户端实体具有数字主键和字符串客户端编号。客户编号标识现实世界中的客户端,是一个不会改变的自然密钥。
4个对象值
可以使用对象值进行相等。但是你有没有提到其他缺点。如果值更改且对象位于集合中,则可能会出现问题。例如,如果你有一个Set
,其中两个对象在开始时是不同的,但是当它们在集合中引用时它们会更改它们以使它们变得相等。然后你违反了Set
的合同。请参阅Hibernate equals and hashcode。
5混合:值+自动生成主要/代理键
如果要比较的对象已经有ID,请使用它。否则,使用对象值进行比较。
所有人都有一些利弊。恕我直言,如果可以使用您的域模型,最好的是3。否则,我已经使用了5并且它有效,尽管在使用集合时仍然存在一些陷阱。我从来没用过2但是如果你找到一种在代码中生成PK的方法,这听起来也是一个明智的解决方案。也许其他人有这个指针。