简体   繁体   中英

Invoke a method using reflection by passing an Expression<Func<T, object>>

Following my own question it has occured to me that I now need to invoke the same service and the same method by passing an Expression<Func<T, object>> to my method. Here is the method definition:

IList<T> List(params Expression<Func<T, object>>[] includeProperties);

Here is the code I have right now:

  //Get generic type
  var entityRelationType = typeof(Applicant).Assembly.GetType(string.Format("Permet.BackEnd.ETL.Domain.Models.{0}", tableRelation.RelationEntityName));

  //create service that will receive the generic type
  var definitionIService = typeof(IService<>).MakeGenericType(entityRelationType);

  //instantiate the service using Unity (todo: fix singleton)
  var serviceInstance = UnitySingleton.Container.Resolve(definitionIService, "");

  //create the argument for the method that we invoke
  var paramsType =
            typeof(Expression<>).MakeGenericType(typeof(Func<,>)
            .MakeGenericType(entityRelationType, typeof(object))).MakeArrayType();


#region Get Dynamic Data
   ParameterExpression relationParameter = Expression.Parameter(entityRelationType, "");

   //build the parameter that we want to pass to the method (Expression<Func<T, object>>
   var include =
         Expression.Lambda(
                    Expression.Property(relationParameter,  tableRelation.NaviguationProprietyName),
                    relationParameter
                    );

dynamic datas = constructedIService
                            .GetMethod("List", new Type[] { paramsType }).Invoke(serviceInstance, new object[] { include });

The include successfully creates my lambda expression (Param_0 => Param_0.Groupings) which I believed would be my Expression<Func<T, object>> . However, since Param_0.Groupings is actually an IList, I get an exception:

Object of type 'System.Linq.Expressions.Expression 1[System.Func 2[Permet.BackEnd.ETL.Domain.Models.CLLI,System.Collections.Generic.IList 1[Permet.BackEnd.ETL.Domain.Models.Grouping]]]' cannot be converted to type 'System.Linq.Expressions.Expression 1[System.Func`2[Permet.BackEnd.ETL.Domain.Models.CLLI,System.Object]][]'.

Which basically means that my Expression<Func<CLLI, IList<Grouping>>> cannot be use in my method which expects a Expression<Func<CLLI, object>> .

If I actually call my service directly doing:

IService<CLLI> clliService = new Service<CLLI>();
clliService.List(clli => clli.Groupings);

It works.

How would I go around this issue? Isn't an IList an object?

The problem is that Expression<T> is invariant, so even if you have a type T which can be assigned to type U , that doesn't mean that Expression<T> can be assigned to Expression<U> . In your case, T is Func<CLI, IList<Grouping>> and U is Func<CLLI, object> .

I think the only solution is to create a function to wrap a given expression in an Expression<Func<T, object>> which delegates to the inner expression and casts the result to object :

public static Expression<Func<T, object>> ConvertResult<T, TOut>(Expression<Func<T, TOut>> expr)
{
    var paramExpr = Expression.Parameter(typeof(T));
    var invokeExpr = Expression.Invoke(expr, paramExpr);
    var castExpr = Expression.Convert(invokeExpr, typeof(object));

    return Expression.Lambda<Func<T, object>>(castExpr, paramExpr);
}

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