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 });