使用 NHibernate 时,是否有任何原因应在实体中覆盖 Equals 或 GetHashCode?这些理由在哪些情况下有效?
一些可以在网上找到的原因:
通过默认的 Equals 代理对象
方法可能会导致意外错误。
但这应该通过以下方式解决
身份 map (它确实在
很多情况),不应该吗?当处理来自单个 session 的实体时,即使没有覆盖 Equals/GetHashCode,一切都应该可以正常工作。在那里
身份映射的任何情况
发挥不好它的作用?
session 和分离的实体。是吗
这样做是个好主意吗?
还有其他原因吗?
最佳答案
正如您在问题中提到的,实体实例的身份是覆盖 Equals
的主要要求& GetHashCode
. NHibernate 中的最佳实践是使用数字键值(short、int 或 long,视情况而定),因为它简化了将实例映射到数据库行的过程。在数据库世界中,这个数值成为主键列值。如果表具有所谓的自然键(其中几列一起唯一标识一行),则单个数值可以成为该值组合的代理主键。
如果您确定不想使用或被阻止使用单个数字主键,那么您需要使用 NHibernate CompositeKey functionality 映射身份。 .在这种情况下,您绝对需要实现自定义 GetHashCode
& Equals
覆盖,以便该表的列值检查逻辑可以确定身份。 Here is a good article关于覆盖 GetHashCode
和 Equals
方法。您还应该覆盖 equal 运算符以完成所有用法。
来自评论:在哪些情况下是 Equals
的默认实现(和 GetHashCode
)不够?
默认实现对于 NHibernate 来说不够好,因为它基于 Object.Equals implementation .此方法将引用类型的相等性确定为引用相等性。换句话说,这两个对象是否指向同一个内存位置?对于 NHibernate,相等性应该基于身份映射的值。
如果不这样做,您很可能会遇到将实体的代理与真实实体进行比较的情况,结果是 调试惨不忍睹 .例如:
public class Blog : EntityBase<Blog>
{
public virtual string Name { get; set; }
// This would be configured to lazy-load.
public virtual IList<Post> Posts { get; protected set; }
public Blog()
{
Posts = new List<Post>();
}
public virtual Post AddPost(string title, string body)
{
var post = new Post() { Title = title, Body = body, Blog = this };
Posts.Add(post);
return post;
}
}
public class Post : EntityBase<Post>
{
public virtual string Title { get; set; }
public virtual string Body { get; set; }
public virtual Blog Blog { get; set; }
public virtual bool Remove()
{
return Blog.Posts.Remove(this);
}
}
void Main(string[] args)
{
var post = session.Load<Post>(postId);
// If we didn't override Equals, the comparisons for
// "Blog.Posts.Remove(this)" would all fail because of reference equality.
// We'd end up be comparing "this" typeof(Post) with a collection of
// typeof(PostProxy)!
post.Remove();
// If we *didn't* override Equals and *just* did
// "post.Blog.Posts.Remove(post)", it'd work because we'd be comparing
// typeof(PostProxy) with a collection of typeof(PostProxy) (reference
// equality would pass!).
}
如果您使用
int
,这是一个示例基类作为您的 Id
(也可以抽象为 any identity type ):public abstract class EntityBase<T>
where T : EntityBase<T>
{
public virtual int Id { get; protected set; }
protected bool IsTransient { get { return Id == 0; } }
public override bool Equals(object obj)
{
return EntityEquals(obj as EntityBase<T>);
}
protected bool EntityEquals(EntityBase<T> other)
{
if (other == null)
{
return false;
}
// One entity is transient and the other is not.
else if (IsTransient ^ other.IsTransient)
{
return false;
}
// Both entities are not saved.
else if (IsTransient && other.IsTransient)
{
return ReferenceEquals(this, other);
}
else
{
// Compare transient instances.
return Id == other.Id;
}
}
// The hash code is cached because a requirement of a hash code is that
// it does not change once calculated. For example, if this entity was
// added to a hashed collection when transient and then saved, we need
// the same hash code or else it could get lost because it would no
// longer live in the same bin.
private int? cachedHashCode;
public override int GetHashCode()
{
if (cachedHashCode.HasValue) return cachedHashCode.Value;
cachedHashCode = IsTransient ? base.GetHashCode() : Id.GetHashCode();
return cachedHashCode.Value;
}
// Maintain equality operator semantics for entities.
public static bool operator ==(EntityBase<T> x, EntityBase<T> y)
{
// By default, == and Equals compares references. In order to
// maintain these semantics with entities, we need to compare by
// identity value. The Equals(x, y) override is used to guard
// against null values; it then calls EntityEquals().
return Object.Equals(x, y);
}
// Maintain inequality operator semantics for entities.
public static bool operator !=(EntityBase<T> x, EntityBase<T> y)
{
return !(x == y);
}
}
关于.net - NHibernate:覆盖 Equals 和 GetHashCode 的原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5851398/