我有一个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表达式并将表达式实现为返回,但得到了上述错误。
有什么建议吗?
答案 0 :(得分:1)
我从未在LINQ泛型方法上使用Expression.Call
方法。我总是单独获取它(请参阅变量firstSelectMethod
和secondSelectMethod
)。我不知道为什么,如果有人知道为什么那不起作用,我会非常感激。下面的代码可行,但我对你的类看起来有些假设。
请注意,我将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; }
}