0

在 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" });

复合键(此处为 ProductLanguageCompositeKey),甚至将其隔离为自己的类(具有可序列化属性、Equals 和 GetHashCode)都没有被缓存?

我们如何使通过复合键访问的实体可缓存?

4

2 回答 2

0

对于那些怀疑 NHibernate 的二级缓存在使用复合主键时是否不起作用(缓存有效)的人,请检查您的复合主键的值是否处于原始形式。我的缓存问题的解决方案:

SQL Server 从 nvarchar 到 varbinary,然后从 varbinary 到 nvarchar 的转换保真度

于 2013-06-16T04:23:17.187 回答
0

为缓存生成一个唯一的缓存键ProductLanguage。这个缓存键是由组合键构建的,组合键取决于Product实体的哈希码。如果您使用跨会话查询,NHibernate 可能会返回代理或未代理版本的Product,这将导致不同的哈希码并导致缓存查找错过缓存的ProductLanguage实体。

解决方案是覆盖EqualsandGetHashCode方法以返回一致的值。EntityBase最简单的方法是为所有具有代理Id键的实体继承流行的类。

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

于 2016-08-19T11:11:17.637 回答