DDD:GetHashCode 和主 ID

发布于 2024-12-09 02:15:43 字数 290 浏览 2 评论 0原文

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

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

我的问题:如果没有可用的业务相关主键并且所有成员都是可变的,对于 GetHashCode 应该使用什么模式?

先感谢您。

I have seen DDD Domain implementations where entities rely on the primary key ID when building Equals/GetHashCode methods.
I understand why it is a good idea, as the primary key might be the only member which is not mutable. But there might also be situations where it is no good idea, though.

Think about a Dictionary, holding entities which where just instantiated. The primary key (assume it is an auto-incrementing value, not a "business-related" value) is not assigned yet, it might be 0 on each entity.
Now, the entities within the Dictionary will be saved. This implies a change of the primary key resulting in a different hash code.

My question: what pattern should be used regarding GetHashCode if no business-related primary key is available and all members are mutable?

Thank you in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

提笔落墨 2024-12-16 02:15:43

在这种情况下,如果已分配 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) 创建此方法的类型化版本
当两个实体属于同一类型时,我检查其中之一是否是瞬态的。我只是通过检查它们的 Id 属性来做到这一点:如果它包含默认值,则意味着它们尚未保存在数据库中。
如果它们都是瞬态的,我会根据它们的参考来比较两个实体。否则,我可以根据他们的 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 ();
        }

In such cases, I only rely on the Id in the Equals and GetHashcode methods, if the Id has been assigned.
In other cases, I compare for equality using the reference.
The same goes for the GetHashCode implementation; I only create a hashcode based on the 'Id', if the Id has been assigned. (That is, if the entity is not transient).

The code below, is what I use. This is somewhat based on the entities in Sharp architecture:

        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);

        }

First, I check whether both entities are of the same type. (Actually, you could create a typed version of this method as well, by implementing the correct interface (IEquality<T>)
When both entities are of the same type, I check whether one of them is transient. I just do it by checking their Id property: if it contains the default value, it means that they haven't been saved in the DB yet.
If both of them are transient, I compare both entities on their reference. Otherwise, I can compare them on their ID.

The GetHashCode method that I use, makes sure that the value that's being returned, never changes:

        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 ();
        }
荒人说梦 2024-12-16 02:15:43

您可以检查,假设它是一个自动递增的标识,并且仅在 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);
        }

You can check, assuming it's an auto-incremented identity, and only compare on Id if they are not 0 or default(int). Something like:

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);
        }
金橙橙 2024-12-16 02:15:43

好吧,如果你不能判断一个对象等于另一个对象,你就不应该这样做:)

如果 equals 始终返回 false 也没关系(给定另一个对象而不是它自己)。如果不同的本地对象(没有 id)在插入数据库/存储时获得不同的 id,这是完全正常的。

如果您可以看出两个对象相等,并且它们都将映射到同一表行,因此在存储上获得相同的 id,直观上,Equals 应该返回 true。但你是对的,使用可变值来确定相等性(尤其是哈希码)可能很危险。当您这样做时,集合通常会感到困惑。
在这种情况下,最好使用 Equals 和 GetHashCode 之外的其他方法来实现这种相等性,因为它确实会随着时间的推移而变化。

因此,本质上你需要确定出于什么目的你真正需要哪种平等。

Well, if you can't tell that an object equals another you should not do it :)

It is ok if equals returns false all the time (given another object than itself). If different local objects (without id) get different ids at the time inserted into the db/storage this is perfectly sane.

If you can tell that two objects are equal and they would both be mapped into the same table line and thus would get the same id on store Equals should return true, intuitively. But you are right that using mutable values to determine equality (and esp. hash code) can be dangerous. Collections usually get confused when you do this.
In this case it is probably better to use other methods than Equals and GetHashCode to implement this kind of equality, as it really changes over time.

So essentially you need to determine which kind of equality you really need for what purpose.

听风吹 2024-12-16 02:15:43

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

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

I expect that the systems you have seen use NHibernate for ORM. The guidelines for NHibernate (which supposedly allows you entities to be POCOs, and enforces no requirement on them) recommends this practice. The problem is that NH sometimes has difficulty recognising an entity as being the same as another instance, especially if the session has been closed and reopened, or if two proxies represent the same object. You can end up with the same entity twice in a collection if you do not do this. Follow this link for more information:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文