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

Call generic method failed (eg: EF shadow property "EF.Property<string>(it, \"PropName\")") #908

Open
neilbgr opened this issue Mar 29, 2025 · 2 comments
Labels

Comments

@neilbgr
Copy link
Contributor

neilbgr commented Mar 29, 2025

Hi,

I need to query an EF shadow property with DynamicLinq, so logicaly I do "EF.Property<string>(it, \"ShadowPropName\")".
But this exception throws: No property or field 'Property' exists in type 'EF'.
Of course, I added the EF static class with my CustomTypeProvider.
In your source code, I found that generic methods are not processed.
I'd try this code and it works fine:

public class ExpressionParser
{
    [...]

    private Expression ParseMemberAccess(Type? type, Expression? expression, string? id = null)
    {

        [...]

        if (_textParser.CurrentToken.Id == TokenId.OpenParen 
            || _textParser.CurrentToken.Id == TokenId.LessThan) // <-- +++++++ Detect generic arg types with "<"
        {
            Expression[]? args = null;

            var isStaticAccess = expression == null;
            var isConstantString = expression is ConstantExpression { Value: string };
            var isStringWithStringMethod = type == typeof(string) && _methodFinder.ContainsMethod(type, id, isStaticAccess);
            var isApplicableForEnumerable = !isStaticAccess && !isConstantString && !isStringWithStringMethod;

            if (isApplicableForEnumerable &&
                TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType) &&
                TryParseEnumerable(expression!, enumerableType, id, type, out args, out var enumerableExpression))
            {
                return enumerableExpression;
            }

            // +++++++++++++++++++++++++++++++++++++++++
            List<Type> typeArguments = new();
            if (_textParser.CurrentToken.Id == TokenId.LessThan)
            {
                _textParser.NextToken(); // Skip "<"
                do
                {
                    if (_textParser.CurrentToken.Id == TokenId.Comma)
                    {
                        _textParser.NextToken(); // Skip ","
                    }
                    if (_textParser.CurrentToken.Id == TokenId.Identifier)
                    {
                        string typeName = GetIdentifier();
                        Type typeArgument = ResolveTypeStringFromArgument(typeName);
                        typeArguments.Add(typeArgument);
                    }
                    _textParser.NextToken(); // Skip the type from identifier
                }
                while (_textParser.CurrentToken.Id != TokenId.GreaterThan);
                _textParser.NextToken(); // Skip ">". The next tkoen would be "("
            }
            // +++++++++++++++++++++++++++++++++++++++++

            // If args is not set by TryParseEnumerable (in case when the expression is not an Enumerable), do parse the argument list here.            
            args ??= ParseArgumentList();
            switch (_methodFinder.FindMethod(type, id, isStaticAccess, ref expression, ref args, out var methodBase))
            {
                case 0:
                    throw ParseError(errorPos, Res.NoApplicableMethod, id, TypeHelper.GetTypeName(type));

                case 1:
                    var method = (MethodInfo)methodBase!;
                    if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!) && !PredefinedMethodsHelper.IsPredefinedMethod(_parsingConfig, method))
                    {
                        throw ParseError(errorPos, Res.MethodIsInaccessible, id, TypeHelper.GetTypeName(method.DeclaringType!));
                    }

                    MethodInfo methodToCall;
                    if (!method.IsGenericMethod)
                    {
                        methodToCall = method;
                    }
                    else
                    {
                        // ----------- Removed original code
                        // +++++++++++ Simply make generic method with type list
                        methodToCall = method.MakeGenericMethod(typeArguments.ToArray());
                        // +++++++++++
                    }

                    return CallMethod(expression, methodToCall, args);

                default:
                    throw ParseError(errorPos, Res.AmbiguousMethodInvocation, id, TypeHelper.GetTypeName(type));
            }
        }

       [...]

        throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));
    }

Perhaps there is a better solution?

Best regards

@neilbgr neilbgr changed the title Call generic method failed (eg: EF shadow property EF.Property<string>(it, "PropName")) Call generic method failed (eg: EF shadow property "EF.Property<string>(it, \"PropName\")") Mar 29, 2025
@neilbgr
Copy link
Contributor Author

neilbgr commented Apr 1, 2025

@StefH , would you prefer me to create a PR?

@StefH
Copy link
Collaborator

StefH commented Apr 6, 2025

@neilbgr
You can create a PR, however this looks like a small change which could have a lot of impact, so extended unit tests are also needed.

Maybe I can start the PR, and then let you review it?

(I don't know when I can work on this...)

@StefH StefH added the feature label Apr 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants