如何使用c#中的表达式树从属性列表中选择项目

时间:2015-07-16 18:28:34

标签: c# linq expression-trees

我有一个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);

...

但运行此代码我收到错误:

  

没有通用方法&#39;选择&#39;在&#39; System.Linq.Queryable&#39;类型是   与参数和提供的类型参数兼容。任何   如果方法不是通用的,则必须提供参数。

我查阅了DebugView表达式并将表达式实现为返回,但得到了上述错误。

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

我从未在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
    )
);

答案 1 :(得分:0)

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

我已经写过a library that takes an expression tree and returns a string representation,也可以通过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; }
}