简体   繁体   English

具有AliasesToBean转换器的Nhibernate 2级缓存

[英]Nhibernate 2nd level Cache with AliasesToBean transformer

I have an entity: 我有一个实体:

public class SalesUnit
{
    public virtual long Id { get; set; }
    public virtual string Name { get; set; }

}

And related Dto: 和相关的Dto:

public class SalesUnitDto
{
    public long Id { get; set; }
    public string Name { get; set; }

}

I have a very simple query: 我有一个非常简单的查询:

SalesUnitDto result = null; 
var list = _session.QueryOver<SalesUnit>()
                .SelectList(l => l
                    .Select(x => x.Id).WithAlias(() => result.Id)
                    .Select(x => x.Name).WithAlias(() => result.Name))
                .TransformUsing(Transformers.AliasToBean<SalesUnitDto>())
                //.Cacheable()
                .List<SalesUnitDto>();

It was working until I plugged in the second level cache. 在插入第二级缓存之前,它一直有效。 So if I uncomment Cacheable() line I will get the exception: 因此,如果我取消注释Cacheable()行,我将得到异常:

Message: Value cannot be null. 消息:值不能为空。 Parameter name: aliases StackTrace: 参数名称:别名StackTrace:

   at NHibernate.Transform.AliasedTupleSubsetResultTransformer.IncludeInTransform(String[] aliases, Int32 tupleLength)
   at NHibernate.Transform.CacheableResultTransformer.Create(ITupleSubsetResultTransformer transformer, String[] aliases, Boolean[] includeInTuple)
   at NHibernate.Loader.Loader.GenerateQueryKey(ISessionImplementor session, QueryParameters queryParameters)
   at NHibernate.Loader.Loader.ListUsingQueryCache(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes)
   at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results)

So what's wrong with that? 那怎么了? Is it a bug of NHibernate? 这是NHibernate的错误吗? I have tried different providers with no avail. 我尝试了其他提供商,但无济于事。 Also I tried to create CacheableResultTransformer like this: 我也试图创建CacheableResultTransformer像这样:

CacheableResultTransformer.Create(Transformers.AliasToBean<SalesUnitDto>(), new[] { "Id", "Name" }, new[] { true, true })

It can return and cache data but only as tuples(object[]). 它可以返回和缓存数据,但只能作为元组(object [])。 I did not manage to return Dto. 我没有设法返回Dto。

Here is the working example to demonstrate a problem: github 这是演示问题的工作示例: github

It turns out to be a bug/limitation of (N)Hibernate. 原来是(N)Hibernate的错误/局限性。 When the caching is activated, the original IResultTransformer starts receiving null string[] aliases argument, which is essential for AliasToBeanTransformer implementation, hence the exception you are getting. 激活缓存后,原始IResultTransformer开始接收null string[] aliases参数,这对于AliasToBeanTransformer实现是必不可少的,因此会出现异常。

As a workaround I could suggest the following custom extension method and transformer, which stores the current aliases when called and passes them to the underlying AliasToBeanTransformer when the passed argument is null : 作为一种解决方法,我建议使用以下自定义扩展方法和转换器,它们在调用时存储当前别名,并在传递的参数为null时将其传递给底层的AliasToBeanTransformer

public static class NHExtensions
{
    public static IQueryOver<TRoot, TSubType> TransformUsingAliasToBean<TRoot, TSubType>(this IQueryOver<TRoot, TSubType> query, Type resultType)
    {
        ITupleSubsetResultTransformer resultTransformer = new AliasToBeanResultTransformer(resultType);
        var criteria = query.UnderlyingCriteria as NHibernate.Impl.CriteriaImpl;
        if (criteria != null)
            resultTransformer = new CacheableAliasToBeenResultTransformer(resultTransformer, criteria.Projection.Aliases);
        return query.TransformUsing(resultTransformer);
    }

    class CacheableAliasToBeenResultTransformer : ITupleSubsetResultTransformer
    {
        ITupleSubsetResultTransformer baseTransformer;
        string[] aliases;

        public CacheableAliasToBeenResultTransformer(ITupleSubsetResultTransformer baseTransformer, string[] aliases)
        {
            this.baseTransformer = baseTransformer;
            this.aliases = aliases;
        }

        public bool[] IncludeInTransform(string[] aliases, int tupleLength)
        {
            return baseTransformer.IncludeInTransform(aliases ?? this.aliases, tupleLength);
        }

        public bool IsTransformedValueATupleElement(string[] aliases, int tupleLength)
        {
            return baseTransformer.IsTransformedValueATupleElement(aliases ?? this.aliases, tupleLength);
        }

        public IList TransformList(IList collection)
        {
            return baseTransformer.TransformList(collection);
        }

        public object TransformTuple(object[] tuple, string[] aliases)
        {
            return baseTransformer.TransformTuple(tuple, aliases ?? this.aliases);
        }
    }
}

and you query would be: 您将查询为:

SalesUnitDto result = null; 
var list = _session.QueryOver<SalesUnit>()
    .SelectList(l => l
        .Select(x => x.Id).WithAlias(() => result.Id)
        .Select(x => x.Name).WithAlias(() => result.Name))
    .TransformUsingAliasToBean(typeof(SalesUnitDto))
    .Cacheable()
    .List<SalesUnitDto>();

Tested and working for this scenario. 经过测试并适用于这种情况。 Of course I can't guarantee that it works for all QueryOver variations. 当然,我不能保证它适用于所有QueryOver版本。

NHibernate的这个错误已在v4.1.0.4000修复

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM