Skip to content
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

UseParameterizedNamesInDynamicQuery set to true breaks OfType method call #722

Closed
yonguelink opened this issue Jul 10, 2023 · 5 comments · Fixed by #723
Closed

UseParameterizedNamesInDynamicQuery set to true breaks OfType method call #722

yonguelink opened this issue Jul 10, 2023 · 5 comments · Fixed by #723
Assignees

Comments

@yonguelink
Copy link
Contributor

1. Description

We are working with the lib to generate linq queries from strings and we recently found the setting UseParameterizedNamesInDynamicQuery, which we see as a nice speed optimization. We updated our parameters to set that to true, and now we have a problem with the .OfType("type") method - System.Linq.Dynamic.Core.Exceptions.ParseException: The 'OfType' function requires the argument to be not null and of type ConstantExpression.

Our usage looks like this (abbreviated a whole lot for simplicity):

var queryable = dataContext.GetEntity<EntityType>();
queryable.Where($"Field.OfType(\"{typeof(MyType)}\")", args);

2. Exception

If you are seeing an exception, include the full exception details (message and stack trace).

Exception message: System.Linq.Dynamic.Core.Exceptions.ParseException: The 'OfType' function requires the argument to be not null and of type ConstantExpression.

Stack trace: ExpressionParser.ResolveTypeFromArgumentExpression(String functionName, Expression argumentExpression, Nullable`1 arguments)
ExpressionParser.ParseEnumerable(Expression instance, Type elementType, String methodName, Int32 errorPos, Type type)
ExpressionParser.ParseMemberAccess(Type type, Expression expression)
ExpressionParser.ParsePrimary()
ExpressionParser.ParseUnary()
ExpressionParser.ParseMultiplicative()
ExpressionParser.ParseAdditive()
ExpressionParser.ParseShiftOperator()
ExpressionParser.ParseComparisonOperator()
ExpressionParser.ParseLogicalAndOrOperator()
<21 more frames...>
ExpressionParser.Parse(Type resultType, Boolean createParameterCtor)
DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, Type itType, Type resultType, String expression, Object[] values)
DynamicQueryableExtensions.Where(IQueryable source, ParsingConfig config, String predicate, Object[] args)
DynamicQueryableExtensions.Where[TSource](IQueryable`1 source, ParsingConfig config, String predicate, Object[] args)

3. Fiddle or Project

I am not quite able to setup a reproduction in Fiddle right now, nor provide a (private) project - this is really deep into convoluted code - I'll look further into this if it is required to help you figure out the problem.

Our usage looks like this (abbreviated a whole lot for simplicity):

var parsingConfig = new ParsingConfig { UseParameterizedNamesInDynamicQuery = true };
var queryable = dataContext.GetEntity<MyType>();
// build up a query here which provides arguments, ignored for this base example
queryable.Where(parsingConfig, $"Field.OfType(\"{typeof(MySubType)}\")", args);

4. Any further technical details

I dug around in the code and from what I could understand setting UseParameterizedNamesInDynamicQuery to true makes it so that the Expression that ends up reaching ExpressionParser.ResolveTypeFromArgumentExpression is a MemberExpression instead of the expected ConstantExpression, making the switch (argumentExpression) fall into the default case which throws the above exception.

To confirm my hypothesis I did a dirty check right before the switch:

private Type ResolveTypeFromArgumentExpression(string functionName, Expression argumentExpression, int? arguments = null)
{
// ...
var unwrappedExpression = argumentExpression as ConstantExpression;
if (unwrappedExpression == null)
{
    _expressionHelper.TryUnwrapAsConstantExpression(argumentExpression, out unwrappedExpression);
}

switch (unwrappedExpression)
// ...

and that fixed the problem we were hitting with the OfType method.

@StefH StefH self-assigned this Jul 10, 2023
@StefH
Copy link
Collaborator

StefH commented Jul 10, 2023

@yonguelink
Thanks for the detailed problem description. I think that there is a unit-test which tests the OfType, so I guess adding a new unit test with your scenario would be possible. And then apply the fix.

I keep you informed. Or you can also create a PR.

@yonguelink
Copy link
Contributor Author

I'll get back to this in ~12h, if no PR has been created by then I'll check the unit tests!

@yonguelink
Copy link
Contributor Author

I added a (failing) test in #723 & then added a fix that I think follows the original pattern. Let me know if there's anything to improve!

StefH pushed a commit that referenced this issue Jul 15, 2023
…e function (#723)

* Test UseParameterizedNamesInDynamicQuery TypeOf use-case

This test fails

* Handle case where expression is MemberExpression

This fixes #722

* Check TryUnwrapAsConstantExpression output before calling resolve

---------

Co-authored-by: Isaac Ouellet-Therrien <iotherrien@dns1.sherweb.com>
@pascalmartin
Copy link

Hi @StefH , is the 1.3.4 release coming soon? We'd love to take advantage of this fix

@StefH
Copy link
Collaborator

StefH commented Aug 1, 2023

@pascalmartin / @yonguelink
I will release a new version within days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants