在NHibernate中使用复合主键的正确方法是什么,以便它可以进行缓存?
我隔离了复合主键,类似于这篇文章的最后一部分:http://devlicio.us/blogs/anne_epstein/archive/2009/11/20/nhibernate-and-composite-keys.aspx
但是二级缓存没有缓存它。
如果用于代理密钥,则它是缓存,例如。
var q = from p in session.Query<Product>()
select p;
q.Cacheable().ToList(); // hit database
// this doesn't hit the database, Get get its value from cache(via last query)
var px = secondSession.Get<Product>(1);
但是当使用复合主键时,Get不会从缓存中获取其值:
var q = from pl in session.Query<ProductLanguage>()
select pl;
q.Cacheable().ToList(); // hit the database
// this hits the database, Get didn't get its value from cache(via last query)
var plx = secondSession.Get<ProductLanguage>(
new ProductLanguageCompositeKey { ProductId = 1, LanguageCode = "en" });
复合键(这里是ProductLanguage ageCompositeKey),甚至隔离它自己的类(具有Serializable属性、Equals和GetHashCode)不会被缓存吗?
我们如何使通过复合密钥访问的实体可缓存?
对于那些怀疑NHibernate的二级缓存在使用复合主键时是否不起作用(缓存有效)的人,请检查您的复合主键的值是否为原始形式。我的缓存问题的解决方案:
SQL服务器转换保真度从nvarchar到var二进制,然后从var二进制到nvarchar
为了缓存,会为ProductLanguage
生成一个唯一的缓存键。这个缓存键是由复合键构建的,它取决于Products
实体的哈希码。如果使用跨会话查询,NHibernate可能会返回Products
的代理或未代理版本,这将导致不同的哈希码,并导致缓存查找错过缓存的ProductLanguage
实体。
解决方案是重写Equals
和GetHashCode
方法,以便返回一致的值。最简单的方法是为所有具有代理Id
键的实体继承流行的EntityBase
类。
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);
}
}
并实施:
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!).
}
更多信息https://stackoverflow.com/a/20110265/179494