如何实现 IEquatable当可变字段是等式的一部分时 - GetHashCode 的问题

发布于 2024-08-29 04:09:01 字数 1479 浏览 4 评论 0原文

我在我的应用程序中使用实体框架。

我使用实体的分部类实现了 IEquatable 接口:

Partial Class Address : Implements IEquatable(Of Address) 'Other part generated
  Public Overloads Function Equals(ByVal other As Address) As Boolean _
      Implements System.IEquatable(Of Address).Equals
    If ReferenceEquals(Me, other) Then Return True
    Return AddressId = other.AddressId
  End Function

  Public Overrides Function Equals(ByVal obj As Object) As Boolean
    If obj Is Nothing Then Return MyBase.Equals(obj)
    If TypeOf obj Is Address Then 
      Return Equals(DirectCast(obj, Address)) 
  Else
    Return False
  End Function

  Public Overrides Function GetHashCode() As Integer
    Return AddressId.GetHashCode
  End Function
End Class

现在在我的代码中,我以这种方式使用它:

Sub Main()
  Using e As New CompleteKitchenEntities
    Dim job = e.Job.FirstOrDefault
    Dim address As New Address()

    job.Addresses.Add(address)
    Dim contains1 = job.Addresses.Contains(address) 'True
    e.SaveChanges()
    Dim contains2 = job.Addresses.Contains(address) 'False

    'The problem is that I can't remove it:
    Dim removed = job.Addresses.Remoeve(address) 'False

  End Using
End Sub

注意(我在调试器可视化工具中检查)EntityCollection 类将其实体存储在HashSet 因此它与 GetHashCode 函数有关,我希望它依赖于 ID,以便实体通过 ID 进行比较。

问题是当我点击保存时,ID 从 0 更改为其 db 值。 所以问题是我怎样才能有一个可相等的对象,并被正确地散列。

请帮助我找出 GetHashCode 函数(按 ID)中的问题以及我可以更改哪些内容以使其正常工作。

多谢。

I am using Entity Framework in my application.

I implemented with the partial class of an entity the IEquatable<T> interface:

Partial Class Address : Implements IEquatable(Of Address) 'Other part generated
  Public Overloads Function Equals(ByVal other As Address) As Boolean _
      Implements System.IEquatable(Of Address).Equals
    If ReferenceEquals(Me, other) Then Return True
    Return AddressId = other.AddressId
  End Function

  Public Overrides Function Equals(ByVal obj As Object) As Boolean
    If obj Is Nothing Then Return MyBase.Equals(obj)
    If TypeOf obj Is Address Then 
      Return Equals(DirectCast(obj, Address)) 
  Else
    Return False
  End Function

  Public Overrides Function GetHashCode() As Integer
    Return AddressId.GetHashCode
  End Function
End Class

Now in my code I use it this way:

Sub Main()
  Using e As New CompleteKitchenEntities
    Dim job = e.Job.FirstOrDefault
    Dim address As New Address()

    job.Addresses.Add(address)
    Dim contains1 = job.Addresses.Contains(address) 'True
    e.SaveChanges()
    Dim contains2 = job.Addresses.Contains(address) 'False

    'The problem is that I can't remove it:
    Dim removed = job.Addresses.Remoeve(address) 'False

  End Using
End Sub

Note (I checked in the debugger visualizer) that the EntityCollection class stores its entities in HashSet so it has to do with the GetHashCode function, I want it to depend on the ID so entities are compared by their IDs.

The problem is that when I hit save, the ID changes from 0 to its db value.
So the question is how can I have an equatable object, being properly hashed.

Please help me find what's wrong in the GetHashCode function (by ID) and what can I change to make it work.

Thanks a lot.

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

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

发布评论

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

评论(2

笑脸一如从前 2024-09-05 04:09:02

您使用了可变字段 (AddressId) 作为哈希的一部分 - 不幸的是,这是注定的。我的意思是:当你添加它时,AddressId 是 0? -1?具体是什么并不重要,但它不是最终的 id - 它是与此密钥/哈希一起存储的。保存时,实际 id(数据库中的IDENTITY 列)将更新到对象中。

很简单,如果该值作为字典的一部分可以更改,则您无法可靠地对该值进行哈希处理。一种可能的解决方法是考虑工作单元,即插入是一个工作单元。含义:如果数据等仅存在于此时间,那么这不是问题,因为您在保存数据后永远不会尝试访问数据。随后(在不同的上下文中)加载数据也应该没问题,因为 id 在生命周期内不会改变。

或者:删除这个哈希/等式。

You've used a mutable field (AddressId) as part of the hash - that is unfortunately doomed. By which I mean: when you add it, the AddressId is 0? -1? it doesn't matter what exactly, but it isn't the final id - and it is stored with this key / hash. When you save it, the actual id (the IDENTITY column from the database) is updated into the object.

Quite simply, you cannot reliably hash against this value if it can change when it is part of a dictionary. One possible workaround would be to consider the unit-of-work, i.e. an insert is a unit-of-work. Meaning: if the data etc only lives as long as this, then it is a non-issue as you will never attempt to access the data after saving it. Subsequently (in a different context) loading the data should be fine too, as the id doesn't then change during the lifetime.

Alternatively: drop this hash/equality.

給妳壹絲溫柔 2024-09-05 04:09:02

非密封类不应实现 IEquatable,因为这是确保派生类重写 Object.Equals()Object.GetHashCode 的唯一方法() 将以与 Object.GetHashCode() 一致的方式实现 IEquatable,以便接口实现调用虚拟 Object。 Equals(Object) 方法。由于 IEquatable 的唯一目的是避免调用 Object.Equals(Object) 的开销,以及 IEquatable< 的安全实现/code> 在未密封的类上无法避免调用它,这样的实现没有任何作用。

另外,我强烈建议不要对任何可变类类型覆盖 Object.Equals(或实现 IEquatable)。对于结构体来说,无论是否可变,重写 Object.Equals 和 Object.GetHashCode 以及实现 IEquatable都是有好处的,因为即使结构类型公开可变的公共字段,存储为例如字典键的结构的字段也是不可变的。

Non-sealed classes should not implement IEquatable<T>, because the only way to ensure that a derived class which overrides Object.Equals() and Object.GetHashCode() will implement IEquatable<BaseType> in a fashion consistent with Object.GetHashCode() is for the interface implementation to call the virtual Object.Equals(Object) method. Since the only purpose of IEquatable<T> is to avoid the overhead of calling Object.Equals(Object), and a safe implementation of IEquatable<T> on an unsealed class cannot avoid calling it, such an implementation would serve no purpose.

Also, I would strongly counsel against any override of Object.Equals (or implementation of IEquatable<T>) for any mutable class type. It is good for structs, whether mutable or not, to override Object.Equals and Object.GetHashCode, and to implement IEquatable<theirOwnType>, since the fields of a struct stored as e.g. a Dictionary key will be immutable even if the struct type exposes mutable public fields.

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