如何实现 IEquatable当可变字段是等式的一部分时 - GetHashCode 的问题
我在我的应用程序中使用实体框架。
我使用实体的分部类实现了 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您使用了可变字段 (
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, theAddressId
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 (theIDENTITY
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.
非密封类不应实现
IEquatable
,因为这是确保派生类重写Object.Equals()
和Object.GetHashCode 的唯一方法()
将以与Object.GetHashCode()
一致的方式实现IEquatable
,以便接口实现调用虚拟Object。 Equals(Object)
方法。由于IEquatable
的唯一目的是避免调用Object.Equals(Object)
的开销,以及IEquatable< 的安全实现/code> 在未密封的类上无法避免调用它,这样的实现没有任何作用。
另外,我强烈建议不要对任何可变类类型覆盖都是有好处的,因为即使结构类型公开可变的公共字段,存储为例如字典键的结构的字段也是不可变的。
Object.Equals
(或实现IEquatable
)。对于结构体来说,无论是否可变,重写 Object.Equals 和 Object.GetHashCode 以及实现 IEquatableNon-sealed classes should not implement
IEquatable<T>
, because the only way to ensure that a derived class which overridesObject.Equals()
andObject.GetHashCode()
will implementIEquatable<BaseType>
in a fashion consistent withObject.GetHashCode()
is for the interface implementation to call the virtualObject.Equals(Object)
method. Since the only purpose ofIEquatable<T>
is to avoid the overhead of callingObject.Equals(Object)
, and a safe implementation ofIEquatable<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 ofIEquatable<T>
) for any mutable class type. It is good for structs, whether mutable or not, to overrideObject.Equals
andObject.GetHashCode
, and to implementIEquatable<theirOwnType>
, since the fields of a struct stored as e.g. aDictionary
key will be immutable even if the struct type exposes mutable public fields.