LINQ to NHibernate, запрос "получить по массиву идентификаторов"
код:
public IList<T> GetByMultipleIds(int[] ids)
{
List<T> result =
_session.Linq<T>()
.Where(x => ids.Contains(x.Id)).ToList();
return result;
}
Броски:
An exception of type 'System.NullReferenceException' occurred in
NHibernate.DLL but was not handled in user code
Additional information: Object reference not set to an instance of an object.
Идентификаторы = {1}; T - типof (foo), который имеет правильное отображение.
Таблица foo ожидаемых данных.
foo наследует entityBase, который имеет общедоступную виртуальную подсказку с именем Id. просто _session.Get(ids [0]) работает.
Трассировка стека:
[NullReferenceException: Object reference not set to an instance of an object.]
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetEntityName(ICriteria
subcriteria, String propertyName) +13
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetType(ICriteria
subcriteria, String propertyName) +19
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetTypeUsingProjection
(ICriteria subcriteria, String
propertyName) +94
NHibernate.Criterion.InExpression.AssertPropertyIsNotCollection(ICriteriaQuery
criteriaQuery, ICriteria
criteria) +19
NHibernate.Criterion.InExpression.ToSqlString(ICriteria criteria, ICriteriaQuery
criteriaQuery, IDictionary`2 enabledFilters) +38
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetWhereCondition
(IDictionary`2 enabledFilters) +223
NHibernate.Loader.Criteria.CriteriaJoinWalker..ctor(IOuterJoinLoadable
persister, CriteriaQueryTranslator
translator, ISessionFactoryImplementor factory, CriteriaImpl criteria, String
rootEntityName, IDictionary`2 enabledFilters) +296
NHibernate.Loader.Criteria.CriteriaLoader..ctor(IOuterJoinLoadable persister,
ISessionFactoryImplementor
factory, CriteriaImpl rootCriteria, String rootEntityName, IDictionary`2
enabledFilters) +131
NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results) +173
NHibernate.Impl.CriteriaImpl.List(IList results) +41
NHibernate.Impl.CriteriaImpl.List() +35
Это тоже не работает:
IList<T> result =
(_session.Linq<T>().Where(a => new[] {1}.Contains(a.Id))).ToList();
Странно, но этот работает:
IList<foo> result =
(_session.Linq<foo>().Where(a => new[] {1}.Contains(a.Id))).ToList();
Этот работает:
IList<T> result =_session.Linq<T>()
.Where(x => 1==1).ToList();
Но мне нужно, чтобы оно было общим.
Любые идеи, что может быть неправильным?
Может быть, переход на nhibernate 2.1 beta поможет?
В настоящий момент это так:
public IList<TEntity> GetByMultipleIds(int[] ids)
{
//TODO: somehow query whole list at once
List<TEntity> result = new List<TEntity>();
foreach (var id in ids) {
int tempId = id;
result.Add(_session.Get<TEntity>(tempId));
}
return result;
}
Но это просто хромой патч.:/
Собственно, мой сотрудник нашел обходное решение с использованием ICriteria (я добавлю код позже).
И this > позволяет элегантно сортировать объекты по массиву id.
Ответы
Ответ 1
Помните, что NHibernate наследует ваши классы и не использует их непосредственно для реализации IList. Вероятно, это не то, что вы хотите услышать, но так как ваши классы проксированы, а текущая реализация Linq не справляется с этим правильно, что проблема возникает.
Комбинация общего метода, использующего прокси-класс в критериях для Linq, просто так раздувается с текущей реализацией. Как сказал ShaneC в своих комментариях, есть веская причина, по которой они вернулись и начали переписывать его с нуля.
Я знаю, что после исправления, но, к сожалению, ответ в этом случае ждет завершения NHibernate 2.1 или использования обходного пути, как вы делаете сейчас.
Ответ 2
Блин. Я забыл добавить решение, которое работает:
public virtual IList<TEntity> GetByMultipleIds(int[] ids)
{
var result = Session
.CreateCriteria(typeof (TEntity))
.Add(Restrictions.In("Id", ids))
.List<TEntity>();
result = ids.Join //to order list by passed ids
(result, id => id, r => r.Id, (i, r) => r).ToList();
return result;
}
Ответ 3
Я понимаю, что текущая реализация Linq для NHibernate довольно ограничена.
В настоящее время есть попытка переписать его с последним обновлением, доступным здесь:
Обновление Linq To NHibernate
Ответ 4
Последний ответ на этот вопрос заключается в том, что он теперь работает (NHib 3.3 и, возможно, > 3.0)
var entities = from m in Session.Query<MyEntity>()
where ids.Contains(m.ID)
select m;
return entities.ToList()
Запускает правильный запрос, что-то вроде
exec sp_executesql N'select MyEntity0_.ID as ID47_, MyEntity0_.Name as Name47_ from Groups MyEntity0_ where MyEntity0_.ID in (@p0 , @p1)',N'@p0 int,@p1 int',@p0=175,@p1=176