Reputation: 13
I have a lambda expression as follows:
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 }))
});
I am trying to execute the same query with Expressions with the following code:
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);
...
but running this code I got the error:
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.
I consulted the DebugView expression and implemented expressions as its return, but get the aforementioned error.
Any suggestions?
Upvotes: 1
Views: 1859
Reputation: 15297
(Disclaimer: I am the author of the library in question.)
I've written a library that takes an expression tree and returns a string representation, also available via NuGet. The library allows you to see the factory method calls used to generate the expression.
For example, you could write the following code:
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"));
and get back the following output:
// 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"
)
)
)
)
If you plug in your own classes, you'll probably get better results. The classes I used were all auto-generated by Visual Studio, as follows:
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; }
}
Upvotes: 0
Reputation: 14350
I have never had luck with using that Expression.Call
method on the LINQ generic methods. I always fetch it separately (see variables firstSelectMethod
and secondSelectMethod
). I don't know why, and if someone else knows why that won't work, I would be much obliged. The below code works, though I made some assumptions about what your classes look like.
Please note that I substituted Queryable
for 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
)
);
Upvotes: 1