Arnis Lapsa
Arnis Lapsa

Reputation: 47597

LINQ to NHibernate, "get by array of ids" query

Code:

 public IList<T> GetByMultipleIds(int[] ids)
 {
        List<T> result =
            _session.Linq<T>()
                .Where(x => ids.Contains(x.Id)).ToList();

        return result;
 }

Throws:

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.

ids={1}; T is typeof(foo) which has correct mapping.

foo table has expected data.

foo inherits entityBase which has public virtual prop named Id. simple _session.Get(ids[0]) works.

Stack trace:

[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

This one does not work either:

IList<T> result =  
  (_session.Linq<T>().Where(a => new[] {1}.Contains(a.Id))).ToList();

Strange, but this works:

IList<foo> result =  
  (_session.Linq<foo>().Where(a => new[] {1}.Contains(a.Id))).ToList();

This one works too:

IList<T> result =_session.Linq<T>()
  .Where(x => 1==1).ToList();

But i need it to be generic.

Any ideas what might be wrong?

Maybe switching to nhibernate 2.1 beta would help?

At the moment it's like this:

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;
    }

But that's just a lame patch. :/


Actually - my co-worker found a workaround using ICriteria (i'll add code later).
And this allows to sort entities by id array elegantly.

Upvotes: 13

Views: 7641

Answers (4)

PandaWood
PandaWood

Reputation: 8343

The latest answer to this question is that it now works (NHib 3.3 and probably > 3.0)

 var entities = from m in Session.Query<MyEntity>()
                where ids.Contains(m.ID)
                select m;

 return entities.ToList()

Runs the right query, something like

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

Upvotes: 1

Arnis Lapsa
Arnis Lapsa

Reputation: 47597

Damn. I've forgot to add solution that works:

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;
}

Upvotes: 8

Nick Craver
Nick Craver

Reputation: 630409

Remember that NHibernate inherits your classes and doesn't use them directly per it's implementation of IList. Probably not what you want to hear but since your classes are proxied and the current Linq implementation doesn't handle it correctly at all, that's where the problem comes in.

The combination of a generic method using a proxied class in criteria for Linq just blows up in so many ways with the current implementation. Like ShaneC said in his comments, there's good reason they went back and started re-writing it from scratch.

I know your after a fix, but sadly the answer in this case is wait for NHibernate 2.1 to be complete or use a work-around like you are doing for now.

Upvotes: 6

Shane Courtrille
Shane Courtrille

Reputation: 14097

My understanding is that the current Linq to NHibernate implementation is fairly limited.
There is currently an effort to rewrite it with the latest updating being available here:

Linq To NHibernate Update

Upvotes: 1

Related Questions