I found out a solution, shown below, to create an anonymous object through multiple Expression<Func<T1,T2>>
public class FieldSelector
{
public static Expression<Func<TSource, TDestination>> GetSelector<TSource, TDestination>(params Expression<Func<TSource, TDestination>>[] selectors)
{
// We will create a new type in runtime that looks like a AnonymousType
var str = $"<>f__AnonymousType0`{selectors.Count()}";
// Create type builder
var assemblyName = Assembly.GetCallingAssembly().GetName();
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("module");
var typeBuilder = moduleBuilder.DefineType(str, TypeAttributes.Public | TypeAttributes.Class);
var types = new Type[selectors.Count()];
var names = new List<string>[selectors.Count()];
for (int i = 0; i < selectors.Count(); i++)
{
Expression exp;
// Retrive passed properties
exp = !(selectors[i].Body is UnaryExpression unExpr) ?
selectors[i].Body as MemberExpression :
unExpr.Operand as MemberExpression;
types[i] = exp.Type;
// Retrive a nested properties
names[i] = GetAllNestedMembersName(exp);
}
// Defined generic parameters for custom type
var genericParams = typeBuilder.DefineGenericParameters(types.Select((_, i) => $"PropType{i}").ToArray());
for (int i = 0; i < types.Length; i++)
{
typeBuilder.DefineField($"{string.Join("_", names[i])}", genericParams[i], FieldAttributes.Public);
}
// Create generic type by passed properties
var type = typeBuilder.CreateType();
var genericType = type.MakeGenericType(types);
var parameter = Expression.Parameter(typeof(TSource), "item");
// Create nested properties
var assignments = genericType
.GetFields()
.Select((prop, i) => Expression.Bind(prop, GetAllNestedMembers(parameter, names[i])));
return Expression.Lambda<Func<TSource, TDestination>>(Expression.MemberInit(Expression.New(genericType.GetConstructors()[0]), assignments), parameter);
}
static Expression GetAllNestedMembers(Expression parameter, List<string> properties)
{
Expression expression = parameter;
for (int i = 0; i < properties.Count; ++i)
{
expression = Expression.Property(expression, properties[i]);
}
return expression;
}
static List<string> GetAllNestedMembersName(Expression arg)
{
var result = new List<string>();
var expression = arg as MemberExpression;
while (expression != null && expression.NodeType != ExpressionType.Parameter)
{
result.Insert(0, expression.Member.Name);
expression = expression.Expression as MemberExpression;
}
return result;
}
}
Problem
The above solution is working pretty well with MemberExpressions like
- obj => obj.{PropertyName}
But and if I have expressions like the following ones
- obj => string.Join(',', obj.Values.Select(v => v.Name)) - MethodCallExpression
- obj => obj.Item != null ? obj.Item.Description : string.Empty - Conditional
Is it possible to bind the kinda expressions as MemberAssignments?
I put just a random example, feel free to test this with your own.
Thanks in advance
Aucun commentaire:
Enregistrer un commentaire