Home > Enterprise >  Attempt to trickshoot for building a Select Expression using reflection
Attempt to trickshoot for building a Select Expression using reflection

Time:06-29

I'm trying to build a select expression in reflection from string. My beginning is good and my select expression is built correctly from this source:

Type entityType = Type.GetType("NameSpacePath.MyEntity");
ParameterExpression xParameter = Expression.Parameter(entityType, "x");

string propertyPath = "ReferenceProperty.SubReference.Property";
Expression body = propertyPath.Split('.').Aggregate((Expression)xParameter, Expression.PropertyOrField);

This work as expected :
Body output : { x.ReferenceProperty.SubReference.Property }
SelectExpression Final output : x => new { Property = x.ReferenceProperty.SubReference.Property }

So now I have my body expression in a simple way but the probleme is when i try to get a List. I would like to be able to write somethings like:

string propertyPath = "ReferenceProperty.SubReference.PropertyList.Select(y => y.Id)";

or

string propertyPath = "ReferenceProperty.SubReference.PropertyList.Select(y => new { Id = y.Id })";

This doesn't work as expected :
Exception : Select(y => y.Id) is not a member of MyEntity
Body output expected : {x.ReferenceProperty.SubReference.PropertyList.Select(....)}

In the .Aggregate tricks, 'Select' is not a member of my type ...

Does anyone have a clever way to help me to perform this task following my logic or should I revise the way I do things to handle the case of a select on a list ?

Thank you for your time.

CodePudding user response:

You can simply use Install-Package System.Linq.Dynamic.Core

Type entityType = Type.GetType("NameSpacePath.MyEntity");
ParameterExpression xParameter = Expression.Parameter(entityType, "x");
LambdaExpression e = DynamicExpressionParser.ParseLambda(
        new ParameterExpression[] { xParameter }, 
        null, 
        "x.SubReference.PropertyList.Select(y => y.Id)");

CodePudding user response:

Thanks for Aron's answer which helped me to realize that packages are more adequate than "classical" / "home-made" code to create an Expression from a string or a PropertyInfo[].

After comparing Roslyn, CSharpScript.EvaluateAsync and DynamicExpressionParser.ParseLambda, I think EvaluateAsync is the most convenient in my case. It is less complicated to manage than ParseLambda which requires a lot of work on the string in order to get a correct result.

The performances between both are similar, no winner here.

I expose my solution here if it can help someone :

  • Install-Package Microsoft.CodeAnalysis.CSharp.Scripting
public static Expression<Func<TEntity, dynamic>> CreateSelectExpression<TEntity>(string select)
{

    Assembly[] assemblies = {
        typeof(TEntity).Assembly,
        Assembly.Load("System.Collections"),
        Assembly.Load("System.Linq"),
        Assembly.GetExecutingAssembly()
    };

    var options = ScriptOptions.Default.AddReferences(assemblies);
    options = options.AddImports("System.Linq");

    var taskSelectBuilder = (Task<Expression<Func<TEntity, dynamic>>>)typeof(CSharpScript)
        .GetMethods()
        .First(x => x.Name == nameof(CSharpScript.EvaluateAsync) && x.IsGenericMethod)
        .MakeGenericMethod(new Type[] { typeof(Expression<Func<TEntity, dynamic>>) })
        .Invoke(null, new object[] { select, options, null, null, default(CancellationToken) });

    taskSelectBuilder.Wait();

    return taskSelectBuilder.Result;
}

Called with reflection :

var selectExpression = (Expression)typeof(NameOfClassContainingTheFunction)
        .GetMethods()
        .First(x => x.Name == nameof(NameOfClassContainingTheFunction.CreateSelectExpression) && x.IsGenericMethod)
        .MakeGenericMethod(new Type[] { entityType })
        .Invoke(null, new object[] { select });
  • Related