Skip to content

Unable to cast "System.Linq.Expressions.UnaryExpression" to "System.Linq.Expressions.LambdaExpression". #2537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
emejibka opened this issue Sep 14, 2020 · 5 comments · Fixed by #2542

Comments

@emejibka
Copy link

emejibka commented Sep 14, 2020

Hello.
After update from 5.2.7 to 5.3.* we have some issue with custom QueryProvider. I make test application -
https://github.com/emejibka/nhibernate-specification-subquery-bug

System.NotSupportedException : Could not parse expression 'value(NHibernate.Linq.NhQueryable`1[TestApp.Client]).Where(Convert(value(TestApp.Specification`1[TestApp.Client])))': The given arguments did not match the expected arguments: Невозможно преобразовать объект типа "System.Linq.Expressions.UnaryExpression" к типу "System.Linq.Expressions.LambdaExpression".
   в Remotion.Linq.Parsing.Structure.MethodCallExpressionParser.CreateExpressionNode(Type nodeType, MethodCallExpressionParseInfo parseInfo, Object[] additionalConstructorParameters)
   в Remotion.Linq.Parsing.Structure.MethodCallExpressionParser.Parse(String associatedIdentifier, IExpressionNode source, IEnumerable`1 arguments, MethodCallExpression expressionToParse)
   в Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseMethodCallExpression(MethodCallExpression methodCallExpression, String associatedIdentifier)
   в Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseMethodCallExpression(MethodCallExpression methodCallExpression, String associatedIdentifier)
   в Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseTree(Expression expressionTree)
   в Remotion.Linq.Parsing.Structure.QueryParser.GetParsedQuery(Expression expressionTreeRoot)
   в Remotion.Linq.Parsing.ExpressionVisitors.SubQueryFindingExpressionVisitor.Visit(Expression expression)
   в System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   в Remotion.Linq.Parsing.ExpressionVisitors.SubQueryFindingExpressionVisitor.Visit(Expression expression)
   в Remotion.Linq.Parsing.Structure.MethodCallExpressionParser.ProcessArgumentExpression(Expression argumentExpression)
   в System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   в System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   в System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   в Remotion.Linq.Parsing.Structure.MethodCallExpressionParser.Parse(String associatedIdentifier, IExpressionNode source, IEnumerable`1 arguments, MethodCallExpression expressionToParse)
   в Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseMethodCallExpression(MethodCallExpression methodCallExpression, String associatedIdentifier)
   в Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseTree(Expression expressionTree)
   в Remotion.Linq.Parsing.Structure.QueryParser.GetParsedQuery(Expression expressionTreeRoot)
   в NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory, Boolean filter)
   в NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
   в NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
   в NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
   в NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
   в NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query)
   в TestApp.CustomQueryProvider.PrepareQuery(Expression expression, IQuery& query) в C:\temp\nhibernate-specification-subquery-bug\CustomQueryProvider.cs:строка 17
   в NHibernate.Linq.DefaultQueryProvider.ExecuteList[TResult](Expression expression)
   в NHibernate.Linq.NhQueryable`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   в System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   в System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   в TestApp.TestClass.test() в C:\temp\nhibernate-specification-subquery-bug\TestClass.cs:строка 48
@bahusoid

This comment has been minimized.

@bahusoid
Copy link
Member

bahusoid commented Sep 14, 2020

Yes it's reproducible on 5.3.3 and has nothing to do with custom provider. It's something related to implicit conversion to expression.
Can be reproduced by adding the following code to LinqQuerySamples:

public class Specification<T>
{
	private Expression<Func<T, bool>> _expression;

	public Specification(Expression<Func<T, bool>> expression)
	{
		_expression = expression;
	}

	public static implicit operator Expression<Func<T, bool>>(Specification<T> specification)
	{
		return specification._expression;
	}
}

[Test]
public void ImplicitConversionInsideWhereSubqueryExpression()
{
	var spec = new Specification<Order>(x => x.Freight > 1000);

	db.Orders.Where(o => db.Orders.Where(spec).Any(x => x.OrderId == o.OrderId)).ToList();
}

@bahusoid
Copy link
Member

As a workaround you can try to add the following code to SpecificationsProcessor skipping implicit conversion from parsing:

protected override Expression VisitUnary(UnaryExpression node)
{
    if (node.NodeType == ExpressionType.Convert && typeof(BaseSpecification).IsAssignableFrom(node.Operand.Type))
    {
        return ((BaseSpecification)FindValue(node.Operand)).ToExpression();
    }
    return base.VisitUnary(node);
}

private static object FindValue(Expression expression)
{
    if (expression.NodeType == ExpressionType.MemberAccess)
    {
        var memberAccess = (MemberExpression) expression;
        if (memberAccess.Expression == null || memberAccess.Expression.NodeType == ExpressionType.Constant)
        {
            var constantValue = ((ConstantExpression) memberAccess.Expression)?.Value;
            var member = memberAccess.Member;
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    return ((FieldInfo) member).GetValue(constantValue);
                case MemberTypes.Property:
                    return ((PropertyInfo) member).GetValue(constantValue);
            }
        }
    }
    
    throw new NotSupportedException();
}

Also in 5.3 you can drop all IQueryProviderWithOptions related code from CustomQueryProvider and instead override CreateWithOptions:

protected override IQueryProvider CreateWithOptions(NhQueryableOptions options)
{
    return new CustomQueryProvider(Session, Collection, options);
}
private CustomQueryProvider(ISessionImplementor session, object collection, NhQueryableOptions options): base(session, collection, options)
{
}

@hazzik
Copy link
Member

hazzik commented Sep 15, 2020

What is the type of unary expression? Is it Quote?

@bahusoid
Copy link
Member

I suspect it's Convert

@hazzik hazzik closed this as completed Sep 17, 2020
@bahusoid bahusoid linked a pull request Dec 29, 2020 that will close this issue
bahusoid pushed a commit to bahusoid/nhibernate-core that referenced this issue Aug 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants