DDD:GetHashCode和主要ID

时间:2011-10-07 12:55:02

标签: c# domain-driven-design

我见过DDD Domain实现,其中实体在构建Equals / GetHashCode方法时依赖主键ID。 我理解为什么这是一个好主意,因为主键可能是唯一不可变的成员。但是,也有可能出现这样的情况。

考虑一个字典,保存刚才实例化的实体。主键(假设它是自动递增值,而不是“业务相关”值)尚未分配,每个实体可能为0。 现在,将保存Dictionary中的实体。这意味着主键的更改会导致不同的哈希码。

我的问题:如果没有与业务相关的主键可用且所有成员都是可变的,那么应该使用哪种模式来使用GetHashCode?

提前谢谢。

4 个答案:

答案 0 :(得分:6)

在这种情况下,如果已分配了ID,我只会依赖EqualsGetHashcode方法中的ID。 在其他情况下,我使用引用比较相等性。 GetHashCode实施也是如此;如果已分配Id,我只根据'Id'创建一个哈希码。 (也就是说,如果实体不是短暂的)。

下面的代码是我使用的。这有点基于Sharp架构中的实体:

        public override bool Equals( object obj )
        {
            Entity<TId> other = obj as Entity<TId>;

            if( other == null || this.GetType() != other.GetType() )
            {
                return false;
            }

            bool otherIsTransient = Equals (other.Id, default(TId));
            bool thisIsTransient = Equals (this.Id, default (TId));

            if( otherIsTransient && thisIsTransient )
            {
                return ReferenceEquals (this, other);
            }

            return Id.Equals (other.Id);

        }

首先,我检查两个实体是否属于同一类型。 (实际上,您也可以通过实现正确的接口(IEquality<T>)来创建此方法的类型化版本 当两个实体属于同一类型时,我会检查其中一个是否是瞬态的。我只是通过检查它们的Id属性来做到这一点:如果它包含默认值,则意味着它们尚未保存在数据库中。 如果它们都是瞬态的,我会在它们的参考上比较两个实体。否则,我可以比较他们的身份证。

我使用的GetHashCode方法确保返回的值永远不会改变:

        private int? _oldHashCode;

        public override int GetHashCode()
        {
            // Once we have a hash code we'll never change it
            if( _oldHashCode.HasValue )
            {
                return _oldHashCode.Value;
            }

            bool thisIsTransient = Equals (Id, default(TId));


            // When this instance is transient, we use the base GetHashCode()
            // and remember it, so an instance can NEVER change its hash code.

            if( thisIsTransient )
            {
                _oldHashCode = base.GetHashCode ();

                return _oldHashCode.Value;
            }

            return Id.GetHashCode ();
        }

答案 1 :(得分:1)

您可以检查,假设它是一个自动递增的标识,并且仅在Id上进行比较,如果它们不是0或default(int)。类似的东西:

public virtual bool Equals(MyClass other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            if (Id != default(int) && other.Id != default(int))
                return Equals(Id, other.Id);
            return Equals(other.ChildProp, ChildProp);
        }

答案 2 :(得分:1)

好吧,如果你不能说一个物体等于另一个物体你就不应该这样做:)

如果equals始终返回false(给定另一个对象而不是自身),则可以。如果不同的本地对象(没有id)在插入db / storage时获得不同的id,这是完全正确的。

如果您可以判断两个对象是相同的并且它们都将映射到同一个表行,因此在商店中获得相同的ID Equals应该直观地返回true。但你是对的,使用可变值来确定相等(以及特别是哈希码)可能是危险的。收集通常会在您执行此操作时感到困惑。 在这种情况下,最好使用除Equals和GetHashCode之外的其他方法来实现这种相等性,因为它实际上会随着时间的推移而变化。

所以基本上你需要确定你真正需要哪种平等目的。

答案 3 :(得分:1)

我希望您看到的系统使用NHibernate进行ORM。 NHibernate的指南(据称允许您将实体作为POCO,并且对它们不强制执行)推荐这种做法。问题是NH有时难以将实体识别为与另一个实例相同,特别是如果会话已经关闭并重新打开,或者两个代理表示同一个对象。如果不这样做,您可以在集合中两次使用同一实体。请点击此链接获取更多信息:

http://community.jboss.org/wiki/EqualsAndHashCode