Nhibernate 2-й уровень Cache с преобразователем AliasesToBean
У меня есть сущность:
public class SalesUnit
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
}
И связанные Dto:
public class SalesUnitDto
{
public long Id { get; set; }
public string Name { get; set; }
}
У меня очень простой запрос:
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>();
Это работало, пока я не подключил кэш второго уровня. Так что, если я раскомментирую Cacheable()
В строке я получу исключение:
Сообщение: значение не может быть нулевым. Имя параметра: псевдонимы 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)
Так что с этим не так? Это ошибка NHibernate? Я пробовал разных провайдеров безрезультатно. Также я попытался создать CacheableResultTransformer следующим образом:
CacheableResultTransformer.Create(Transformers.AliasToBean<SalesUnitDto>(), new[] { "Id", "Name" }, new[] { true, true })
Он может возвращать и кэшировать данные, но только в виде кортежей (object[]). Мне не удалось вернуть Дто.
Вот рабочий пример, демонстрирующий проблему: github
2 ответа
Оказывается, это ошибка / ограничение (N)Hibernate. Когда кеширование активировано, оригинал IResultTransformer
начинает получать null
string[] aliases
аргумент, который необходим для AliasToBeanTransformer
реализация, следовательно, вы получаете исключение.
В качестве обходного пути я мог бы предложить следующий пользовательский метод расширения и преобразователь, который хранит текущие псевдонимы при вызове и передает их базовому AliasToBeanTransformer
когда переданный аргумент null
:
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);
}
}
}
и ваш запрос будет:
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>();
Протестировано и работает по этому сценарию. Конечно, я не могу гарантировать, что это работает для всех QueryOver
вариации.