如何在 C# 中使用表达式树从 属性 列表中 select 项

How to select Item from an property list using Expression Tree in c#

我有一个 lambda 表达式如下:

var source = new List<Entidade>();

var z = source.Select<Entidade, Resultado>(
                s =>
                new Resultado
                    {
                        Detalhes =
                            new List<DetalheResultado>(
                            s.Detalhes.Select<Detalhe, DetalheResultado>(
                                t => new DetalheResultado { Id = t.Id, Valor = t.Valor }))
                    });

我正在尝试使用以下代码使用表达式执行相同的查询:

ParameterExpression sourceItem = Expression.Parameter(typeof(Entidade), "s");

var source3 = Expression.Parameter(typeof(Detalhe), "t");
var property3 = typeof(DetalheResultado).GetProperty("Id");
var member3 = Expression.Property(source3, "Id");
var itemResult3 = Expression.New(typeof(DetalheResultado).GetConstructor(Type.EmptyTypes));
var memberBinding3 = Expression.Bind(property3, member3);
var memberInit3 = Expression.MemberInit(itemResult3, memberBinding3);
var selector3 = Expression.Lambda(memberInit3, source3);

var detalhes = Expression.Property(sourceItem, "Detalhes");

// here you get an error
var lista3 = Expression.Call(
    typeof(Queryable), 
    "Select", 
    new Type[] { typeof(Detalhe), typeof(DetalheResultado) },
    detalhes, 
    selector3);

var listaResultado = typeof(DetalheResultado).GetProperty("Detalhes");
var memberBindigs4 = Expression.Bind(listaResultado, lista3);

...

但是运行这段代码我得到了错误:

No generic method 'Select ' on ' System.Linq.Queryable ' type is compatible with the arguments and the supplied type arguments. Any argument must be provided if the method is not generic.

我查阅了 DebugView 表达式并将表达式作为其 return 实现,但出现上述错误。

有什么建议吗?

我从来没有在 LINQ 泛型方法上使用 Expression.Call 方法。我总是单独获取它(参见变量 firstSelectMethodsecondSelectMethod)。我不知道为什么,如果其他人知道为什么那行不通,我将非常感激。下面的代码有效,尽管我对你的 类 的样子做了一些假设。

请注意,我用 Queryable 代替了 Enumerable

var paramS = Expression.Parameter(typeof(Entidade), "s");
var paramT = Expression.Parameter(typeof(Detalhe), "t");

var firstSelectMethod = typeof(Enumerable).GetMethods().First(m => m.Name == "Select").MakeGenericMethod(typeof(Entidade), typeof(Resultado));
var secondSelectMethod = typeof(Enumerable).GetMethods().First(m => m.Name == "Select").MakeGenericMethod(typeof(Detalhe), typeof(DetalheResultado));

var lista4 = Expression.Call(
    firstSelectMethod,
    Expression.Constant(source),
    Expression.Lambda(
        Expression.MemberInit(
            Expression.New(typeof(Resultado).GetConstructor(Type.EmptyTypes)), 
            Expression.Bind(
                typeof(Resultado).GetProperty("Detalhes"), 
                Expression.New(
                    typeof(List<DetalheResultado>).GetConstructor(new Type[] {typeof(IEnumerable<DetalheResultado>)}),
                    Expression.Call(
                        secondSelectMethod,
                        Expression.Property(
                            paramS,
                            "Detalhes"
                        ),
                        Expression.Lambda(
                            Expression.MemberInit(
                                Expression.New(typeof(DetalheResultado).GetConstructor(Type.EmptyTypes)), 
                                Expression.Bind(
                                    typeof(DetalheResultado).GetProperty("Id"),
                                    Expression.Property(paramT, "Id")
                                ),
                                Expression.Bind(
                                    typeof(DetalheResultado).GetProperty("Valor"),
                                    Expression.Property(paramT, "Valor")
                                )
                            ),
                            paramT
                        )
                    )
                )
            )
        ), 
        paramS
    )
);

(免责声明:我是相关图书馆的作者。)

我写了a library that takes an expression tree and returns a string representation, also available via NuGet。该库允许您查看用于生成表达式的工厂方法调用。

例如,您可以编写以下代码:

var source = new List<Entidade>();

Expression<Action> expr = () => source.Select<Entidade, Resultado>(
    s =>
        new Resultado {
            Detalhes = new List<DetalheResultado>(
                s.Detalhes.Select<Detalhe, DetalheResultado>(
                    t => new DetalheResultado { Id = t.Id, Valor = t.Valor }
                )
            )
        }
);

Console.WriteLine(expr.ToString("Factory methods"));

并返回以下输出:

// using static System.Linq.Expressions.Expression

Lambda(
    Call(
        typeof(Enumerable).GetMethod("Select"),
        source,
        Lambda(
            MemberInit(
                New(
                    typeof(Resultado).GetConstructor()
                ),
                Bind(
                    typeof(Resultado).GetProperty("Detalhes"),
                    New(
                        typeof(List<DetalheResultado>).GetConstructor(),
                        Call(
                            typeof(Enumerable).GetMethod("Select"),
                            MakeMemberAccess(s,
                                typeof(Entidade).GetProperty("Detalhes")
                            ),
                            Lambda(
                                MemberInit(
                                    New(
                                        typeof(DetalheResultado).GetConstructor()
                                    ),
                                    Bind(
                                        typeof(DetalheResultado).GetProperty("Id"),
                                        MakeMemberAccess(t,
                                            typeof(Detalhe).GetProperty("Id")
                                        )
                                    ),
                                    Bind(
                                        typeof(DetalheResultado).GetProperty("Valor"),
                                        MakeMemberAccess(t,
                                            typeof(Detalhe).GetProperty("Valor")
                                        )
                                    )
                                ),
                                var t = Parameter(
                                    typeof(Detalhe),
                                    "t"
                                )
                            )
                        )
                    )
                )
            ),
            var s = Parameter(
                typeof(Entidade),
                "s"
            )
        )
    )
)

如果您插入自己的 类,您可能会获得更好的结果。我用的类都是Visual Studio自动生成的,如下:

internal class Detalhe {
    public object Id { get; internal set; }
    public object Valor { get; internal set; }
}

internal class DetalheResultado {
    public object Id { get; internal set; }
    public object Valor { get; internal set; }
}

internal class Resultado {
    public List<DetalheResultado> Detalhes { get; internal set; }
}

internal class Entidade {
    public IEnumerable<Detalhe> Detalhes { get; internal set; }
}