Ievgen
Ievgen

Reputation: 8086

Linq to Nhibernate - call method inside Select breaks IQueryable

I need IQueryable inside my business logic assembly based on Domain entities. I want to use auto-mapper for this purposes due to big amount of similar entities.

Works:

_repository.GetList<AgentDto>()
        .Select(dto => new Agent{Login = dto.Login, Password = dto.Password})
        .Where(...).ToList();

Does not work (I could not place Where (another assembly) before Select):

_repository.GetList<AgentDto>()
        .Select(dto => ToAgent(dto))
        .Where(...).ToList();

private Agent ToAgent(AgentDto dto)
    {
        return new Agent{Login = dto.Login, Password = dto.Password};
    }

Exception:

System.NotSupportedException was caught
  Message=CustomerInfo.Domain.Support.Agent ToAgent(CustomerInfo.DAL.DTO.AgentDto)
  Source=NHibernate
  StackTrace:
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitMethodCallExpression(MethodCallExpression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitMemberExpression(MemberExpression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
       at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.Visit(Expression expression, VisitorParameters parameters)
       at NHibernate.Linq.Visitors.QueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
       at Remotion.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
       at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
       at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
       at NHibernate.Linq.Visitors.QueryModelVisitor.Visit()
       at NHibernate.Linq.Visitors.QueryModelVisitor.GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, Boolean root)
       at NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory)
       at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(String queryIdentifier, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
       at NHibernate.Engine.Query.HQLExpressionQueryPlan.CreateTranslators(String expressionStr, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
       at NHibernate.Engine.Query.HQLExpressionQueryPlan..ctor(String expressionStr, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
       at NHibernate.Engine.Query.HQLExpressionQueryPlan..ctor(String expressionStr, IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
       at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
       at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
       at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
       at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
       at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
       at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
       at System.Linq.Queryable.Count[TSource](IQueryable`1 source, Expression`1 predicate)
       at CustomerInfo.BLL.Authentication.AgentManagementService.ValidateAgent(String agentLogin, String password) in D:\Projects\CustomerInfo\CustomerInfo.BLL\Authentication\AgentManagementService.cs:line 49
  InnerException: 

Upvotes: 2

Views: 3767

Answers (2)

Michael
Michael

Reputation: 1421

I think if you simply put the Where before the Select you'll be fine.

Upvotes: 0

Miroslav Popovic
Miroslav Popovic

Reputation: 12128

You need to think about how NHibernate Linq provider works. It cannot process everything you throw at it. NHibernate's Linq provider transforms lambda expressions to HQL and eventually to SQL. There is only a limited subset of expressions that it can support. Anything you enter as an expression must be convertible to SQL and execute in the database engine itself.

NHibernate Linq provider is extensible. If you need to use some expressions that aren't supported by NH Linq provider, and you think that they can be represented in SQL, you can write your own extension.

However, your case is pretty simple. You don't need to extend NHibernate to do it. NHibernate Linq provider doesn't support your expression, but Linq to objects does. Just reverse the order in your query, and it should work as expected:

_repository.GetList<AgentDto>()
    .Where(...).ToList()
    .Select(dto => ToAgent(dto)).ToList();

.ToList() after .Where() will execute NHibernate query and return a list of AgentDto objects. .Select method after that is in fact executing as Linq to objects - on a list of AgentDto objects in memory.

Upvotes: 5

Related Questions