简体   繁体   English

选择器表达式在IQueryable上动态

[英]Selector expression dynamic on IQueryable

I would like to generate dynamically a selector expression from some lambdas. 我想从一些lambda动态生成选择器表达式。

I want to declare a list of lambda expression like this 我想声明这样的lambda表达式列表

Expression<Func<MyEntity, object>> select1 = myentity => myentity.Label;
Expression<Func<MyEntity, object>> select2 = myentity => myentity.User.Name;
Expression<Func<MyEntity, object>> select3 = myentity => myentity.Fields.Where(1 == 1).Select(f => f.Code).FirstOrDefault();

And let's say i have a class : 假设我有一堂课:

class MyClass 
{
   public string Label { get; set; }
   public string UserName { get; set; }
   public string CodeField { get; set; }
}

I want to compose dynamically the selector expression using the declared expressions. 我想使用声明的表达式动态组成选择器表达式。 The goal is that I want to choose the data to recover, not all together. 目的是我要选择要恢复的数据,而不是全部。

Expression.Lambda<Func<MyEntity, MyClass>> selectExpression = ?? 

req.Select(selectExpression).ToList();

I want to generate a selector expression to have something like this 我想生成一个选择器表达式以具有这样的内容

return req.Select(myentity => new MyClass {
    Label = myentity.Label,
    UserName = myentity.User.Name,
    CodeField = myentity.Fields.Where(1 == 1).Select(f => f.Code).FirstOrDefault()
}).ToList();

Can i do this? 我可以这样做吗?


I succeeded for example like this but it's not the way that i'm look for 例如,我成功获得了这样的成功,但这并不是我寻找的方式

var entityT = Expression.Parameter(typeof(MyEntity), "entity");

var propertyA = Expression.Property(entityT, typeof(MyEntity).GetProperty("Label"));

var propertyB = Expression.Property(entityT, typeof(MyEntity).GetProperty("User"));
var propertyC = Expression.Property(propertyB, typeof(UserEntity).GetProperty("Name"));


var binding = Expression.MemberInit(Expression.New(typeof(MyClass)),
   new[]
   {
       Expression.Bind(typeof(MyClass).GetProperty("Label"), propertyA),
       Expression.Bind(typeof(MyClass).GetProperty("UserName"), propertyC),   
   });

var selectExpression = Expression.Lambda<Func<Benef, MyClass>>(binding, entityT);

return req.Select(selectExpression).ToList();

In the same idea, I was tempted to do this, it compiles but does'nt work: 在相同的想法中,我很想这样做,它可以编译但不起作用:

var binding = Expression.MemberInit(Expression.New(typeof(T)),
    new[]
    {
        Expression.Bind(typeof(T).GetProperty("Label"), select1.Body),
        Expression.Bind(typeof(T).GetProperty("UserName"), select2.Body),   
    });

I have this error : "variable 'myentity' of type 'MyEntity' referenced from scope '', but it is not defined" 我有此错误:“从作用域”引用了类型'MyEntity'的变量'myentity',但未定义”

Thank you for your answers. 谢谢您的回答。

Basically you need to extract expressions from each lambda and connect it with parameter from MyClass . 基本上,您需要从每个lambda中提取表达式,并将其与MyClass参数连接。

Something like: Expression.Bind(typeof(MyClass).GetParameter("x"), selectX.Body) . 类似于: Expression.Bind(typeof(MyClass).GetParameter("x"), selectX.Body)

The only difficulty is that all selectX.Body needs to point to the same paramter, so each body expression needs to be adjusted. 唯一的困难是所有selectX.Body需要指向相同的参数,因此每个主体表达式都需要调整。

Here is sample code: 这是示例代码:

class Program
{
    static void Main(string[] args)
    {
        var mapped = entities
            .Select(MakeExpression<MyEntity, MyClass>(select1, select2, select3))
            .ToList();
    }

    // Create lambda expression 
    private static Expression<Func<TEntity, TModel>> MakeExpression<TEntity, TModel>(params Expression<Func<TEntity, object>>[] select)
    {
        var param = Expression.Parameter(typeof(TEntity));

        // Map expressions [select1, ..., selectN] with properties
        // For keeping things simple I map nth expression with nth property
        // eg. select1 with first property from MyClass
        var body = Expression.MemberInit(
            Expression.New(typeof(TModel)),
            typeof(TModel)
                .GetProperties()
                .Select((p, i) => Expression.Bind(p, MakeParam(param, select[i])))
                .ToArray()
        );  

        return Expression.Lambda<Func<TEntity, TModel>>(body, param);
    }

    // Replace parameter from given expression with param
    // All expressions must have same MyEntity parameter
    private static Expression MakeParam<TEntity>(ParameterExpression param, Expression<Func<TEntity, object>> select)
    {
        Expression body = select.Body;

        return new ParamVisitor<TEntity>(param).Visit(body);
    }
}

class ParamVisitor<TEntity> : ExpressionVisitor
{
    private readonly ParameterExpression _param;

    public ParamVisitor(ParameterExpression param)
    {
        this._param = param;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node.Type == typeof(TEntity))
        {
            return this._param;
        }

        return base.VisitParameter(node);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM