C# 组合多个表达式以创建Linq选择器表达式

C# 组合多个表达式以创建Linq选择器表达式,c#,linq,expression,C#,Linq,Expression,我正在尝试动态构建Linq查询的select语句。 我有这样一个函数: public Task<List<T>> RunQuery<T>( IQueryable<T> query, FieldSelector<T> fields, int pageNumber, int pageSize) { var skip = (pageNumber-1) * pageSize; var query =

我正在尝试动态构建Linq查询的select语句。 我有这样一个函数:

public Task<List<T>> RunQuery<T>(
    IQueryable<T> query, 
    FieldSelector<T> fields, 
    int pageNumber, int pageSize)
{
    var skip = (pageNumber-1) * pageSize;
    var query = query.Skip(skip).Take(pageSize);
    var selector = fields.GetSelector();
    return query.Select(selector).ToListAsync();
}
公共任务运行查询(
可查询的查询,
字段选择器字段,
int pageNumber,int pageSize)
{
var skip=(页码-1)*页面大小;
var query=query.Skip(Skip).Take(pageSize);
var selector=fields.GetSelector();
返回query.Select(selector.toListSync();
}
这是FieldSelector类:(I我的代码I每个字段都有额外的属性)

公共类字段选择器
{
私有列表表达式;
公共字段选择器()
{
表达式=新列表();
}
公共空添加(表达式表达式表达式)
{
添加(expr);
}
公共表达式GetSelector()
{
//构建一个表达式,如e=>new{e.Name,e.Street}
}
}
如何实现GetSelector函数?可能吗?(不会变得太复杂)。
我想这样使用它:

var fields = new FieldSelector<Employee>();
fields.Add(e => e.Name);
fields.Add(e => e.Street);
RunQuery<Employee>(query, fields, 1, 100);
var fields=new FieldSelector();
fields.Add(e=>e.Name);
添加(e=>e.Street);
RunQuery(查询,字段,1100);

您需要生成一个自定义类型,它是如何为
匿名类型
生成编译器的,因为您无法生成具有动态属性计数的真正的
匿名类型。生成此类型后,您可以轻松设置传递给
FieldSelector
的表达式的赋值,并将其组合为自定义类型

    public class FieldSelector<T>
    {
        private List<LambdaExpression> expressions;

        public FieldSelector()
        {
            expressions = new List<LambdaExpression>();
        }

        public void Add(Expression<Func<T, object>> expr)
        {
            expressions.Add(expr);
        }

        public Expression<Func<T, object>> GetSelector()
        {
            // We will create a new type in runtime that looks like a AnonymousType
            var str = $"<>f__AnonymousType0`{expressions.Count}";

            // Create type builder
            var assemblyName = Assembly.GetExecutingAssembly().GetName();
            var modelBuilder = AppDomain.CurrentDomain
                                        .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect)
                                        .DefineDynamicModule("module");
            var typeBuilder = modelBuilder.DefineType(str, TypeAttributes.Public | TypeAttributes.Class);

            var types = new Type[expressions.Count];
            var names = new List<string>[expressions.Count];

            for (int i = 0; i < expressions.Count; i++)
            {
                // Retrive passed properties
                var unExpr = expressions[i].Body as UnaryExpression;
                var exp = unExpr == null ? expressions[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);

            ParameterExpression parameter = Expression.Parameter(typeof(T), "MyItem");

            // Create nested properties
            var assignments = genericType.GetFields().Select((prop, i) => Expression.Bind(prop, GetAllNestedMembers(parameter, names[i])));
            return Expression.Lambda<Func<T, object>>(Expression.MemberInit(Expression.New(genericType.GetConstructors()[0]), assignments), parameter);
        }

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

        private 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;
        }
    }
不幸的是,cusObj将是编译类型的
对象
,如果不将其标记为
动态
,则无法检索其属性

顺便说一下,您的
公共任务RunQuery
不会被编译,因为
field.GetSelector()
返回
Func
,当您调用return
return query.Select(field.GetSelector()).ToListAsync()时,您将得到一个
任务


希望,这会有所帮助。

您需要生成一个自定义类型,它是如何为
匿名类型
生成编译器的,因为您无法生成具有动态属性计数的真正的
匿名类型。生成此类型后,您可以轻松设置传递给
FieldSelector
的表达式的赋值,并将其组合为自定义类型

    public class FieldSelector<T>
    {
        private List<LambdaExpression> expressions;

        public FieldSelector()
        {
            expressions = new List<LambdaExpression>();
        }

        public void Add(Expression<Func<T, object>> expr)
        {
            expressions.Add(expr);
        }

        public Expression<Func<T, object>> GetSelector()
        {
            // We will create a new type in runtime that looks like a AnonymousType
            var str = $"<>f__AnonymousType0`{expressions.Count}";

            // Create type builder
            var assemblyName = Assembly.GetExecutingAssembly().GetName();
            var modelBuilder = AppDomain.CurrentDomain
                                        .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect)
                                        .DefineDynamicModule("module");
            var typeBuilder = modelBuilder.DefineType(str, TypeAttributes.Public | TypeAttributes.Class);

            var types = new Type[expressions.Count];
            var names = new List<string>[expressions.Count];

            for (int i = 0; i < expressions.Count; i++)
            {
                // Retrive passed properties
                var unExpr = expressions[i].Body as UnaryExpression;
                var exp = unExpr == null ? expressions[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);

            ParameterExpression parameter = Expression.Parameter(typeof(T), "MyItem");

            // Create nested properties
            var assignments = genericType.GetFields().Select((prop, i) => Expression.Bind(prop, GetAllNestedMembers(parameter, names[i])));
            return Expression.Lambda<Func<T, object>>(Expression.MemberInit(Expression.New(genericType.GetConstructors()[0]), assignments), parameter);
        }

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

        private 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;
        }
    }
不幸的是,cusObj将是编译类型的
对象
,如果不将其标记为
动态
,则无法检索其属性

顺便说一下,您的
公共任务RunQuery
不会被编译,因为
field.GetSelector()
返回
Func
,当您调用return
return query.Select(field.GetSelector()).ToListAsync()时,您将得到一个
任务


希望对您有所帮助。

谢谢!这个很好用!我只需要使用这个,因为我使用的是dotnet核心:AssemblyBuilder.DefinedDynamicAssemblyThank!这个很好用!我之所以使用它,是因为我使用的是dotnet核心:AssemblyBuilder.DefinedDynamicAssembly
private class TestClass
{
    public string Arg2 { get; set; }

    public TestClass Nested { get; set; }

    public int Id { get; set; }
}

var field = new FieldSelector<TestClass>();
field.Add(e => e.Arg2);
field.Add(e => e.Id);
field.Add(e => e.Nested.Id);
dynamic cusObj = field.GetSelector().Compile()(new TestClass { Arg2 = "asd", Id = 6, Nested = new TestClass { Id = 79 } });
Console.WriteLine(cusObj.Arg2);
Console.WriteLine(cusObj.Id);
Console.WriteLine(cusObj.Nested_Id);