简体   繁体   中英

Build expression tree to call function with 1 or 2 parameters

So, experimenting with expression trees a bit. Here is the idea: I want to return a Func<long, byte?, object> object. Depending on the type I want to use the function on, I'll have one of two methods to use: LoadById(long, byte?) or LoadByID(long) . Both return an object. So i'm trying to do the following: depending on whether the type implements a certain interface, I use either classToUseFunctionOn.LoadById(long, byte?) or classToUseFunctionOn.LoadByID(long) . So, basically I want to have the following code returned if it implements the interface: (long id, byte? options) => new TestFacade().LoadById(id, options) and (long id, byte? options) => new TestFacade().LoadByID(id). I'm just not sure how to do it. It goes wrong on the last few lines. The Lambda call states the amount of parameters is incorrect. Below is the code I have so far:



    private static Func GetDataExtractorForTypeWithId(Type type)
    {
        var paramId = Expression.Parameter(typeof(long), "id");
        ParameterExpression paramOptions = null;
        //gets the ConstructorInfo for the constructor of type T with a single parameter of type IDataReader
        var facadetype = GetFacadeType(type.Name);
        MethodInfo loadMethod;
        var linkedEntitiesInterface = facadetype.GetInterface(typeof(IFacadeLoadLinkedEntities).Name);
        var lamdaParameterExpressions = new List() { paramId };
        if (linkedEntitiesInterface != null)
        {
            loadMethod = facadetype.GetMethod("LoadById");
            paramOptions = Expression.Parameter(linkedEntitiesInterface.GetGenericTypeDefinition().GenericTypeArguments[1], "options");
            lamdaParameterExpressions.Add(paramOptions);
        }
        else
        {
            paramOptions = Expression.Parameter(typeof(byte?));
            loadMethod = facadetype.GetMethod("LoadByID", new Type[1]{typeof(long)});
        }
        var facadeConstructor = facadetype.GetConstructor(new Type[0]);
        var newFacade = Expression.New(facadeConstructor);
        var callLoad = Expression.Call(newFacade, loadMethod, lamdaParameterExpressions);
        lamdaParameterExpressions.Add(paramOptions);
        var returnValue = Expression.Parameter(typeof(object));
        lamdaParameterExpressions.Add(returnValue);
        var entityVariable = Expression.Variable(typeof(object), "entity");
        Expression.Assign(entityVariable, callLoad);
        var lambda = Expression.Lambda>(
            entityVariable, lamdaParameterExpressions.ToArray());
        //compiles the Expression to a usable delegete.
        return lambda.Compile();
    }

In the mean time, I found the way to make it work:



    private static Func GetDataExtractorForTypeWithId(Type type)
            {
                var paramId = Expression.Parameter(typeof(long), "id");
                ParameterExpression paramOptions = null;
                var facadetype = GetFacadeType(type.Name) ?? typeof(AzzFacade).MakeGenericType(type);
                MethodInfo loadMethod;
                var linkedEntitiesInterface = facadetype.GetInterface(typeof(IFacadeLoadLinkedEntities).Name);
                if (linkedEntitiesInterface != null)
                {
                    loadMethod = facadetype.GetMethod("LoadById");
                    paramOptions = Expression.Parameter(typeof(byte), "options");
                }
                else
                {
                    paramOptions = Expression.Parameter(typeof(byte));
                    loadMethod = facadetype.GetMethod("LoadByID", new Type[1]{typeof(long)});
                }
                var facadeConstructor = facadetype.GetConstructor(new Type[0]);
                if(facadeConstructor==null)
                    throw new NullReferenceException($"No parameterless constructor found for facade for type {type.Name}");
                MethodCallExpression callLoad;
                var newFacade = Expression.New(facadeConstructor);
                if (linkedEntitiesInterface != null)
                {
                    var conversionExpression = Expression.Convert(paramOptions, linkedEntitiesInterface.GetGenericArguments()[1]);
                    // ReSharper disable once AssignNullToNotNullAttribute
                    callLoad = Expression.Call(newFacade, loadMethod, paramId, conversionExpression);
                }
                else
                {
                    // ReSharper disable once AssignNullToNotNullAttribute
                    callLoad = Expression.Call(newFacade, loadMethod, paramId);
                }

                var lambda = Expression.Lambda>(
                    // ReSharper disable once AssignNullToNotNullAttribute
                    callLoad, paramId, paramOptions);
                //compiles the Expression to a usable delegate.
                return lambda.Compile();
            }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM