Skip to content

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

@neilbgr

Description

@neilbgr

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions