【发布时间】:2011-07-23 06:35:46
【问题描述】:
我正在构建一个基于 LINQ 的查询生成器。
其中一项功能是能够将任意服务器端投影指定为查询定义的一部分。例如:
class CustomerSearch : SearchDefinition<Customer>
{
protected override Expression<Func<Customer, object>> GetProjection()
{
return x => new
{
Name = x.Name,
Agent = x.Agent.Code
Sales = x.Orders.Sum(o => o.Amount)
};
}
}
由于用户必须能够对投影属性(而不是客户属性)进行排序,因此我将表达式重新创建为 Func<Customer,anonymous type> 而不是 Func<Customer, object>:
//This is a method on SearchDefinition
IQueryable Transform(IQueryable source)
{
var projection = GetProjection();
var properProjection = Expression.Lambda(projection.Body,
projection.Parameters.Single());
为了返回投影查询,我希望能够做到这一点(事实上,这在几乎相同的概念证明中起作用):
return Queryable.Select((IQueryable<TRoot>)source, (dynamic)properProjection);
TRoot 是 SearchDefinition 中的类型参数。这会导致以下异常:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for
'System.Linq.Queryable.Select<Customer,object>(System.Linq.IQueryable<Customer>,
System.Linq.Expressions.Expression<System.Func<Customer,object>>)'
has some invalid arguments
at CallSite.Target(Closure , CallSite , Type , IQueryable`1 , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet]
(CallSite site, T0 arg0, T1 arg1, T2 arg2)
at SearchDefinition`1.Transform(IQueryable source) in ...
如果仔细观察,它会错误地推断泛型参数:Customer,object 而不是Customer,anonymous type,这是properProjection 表达式的实际类型(双重检查)
我的解决方法是使用反射。但是对于通用参数,这真是一团糟:
var genericSelectMethod = typeof(Queryable).GetMethods().Single(
x => x.Name == "Select" &&
x.GetParameters()[1].ParameterType.GetGenericArguments()[0]
.GetGenericArguments().Length == 2);
var selectMethod = genericSelectMethod.MakeGenericMethod(source.ElementType,
projectionBody.Type);
return (IQueryable)selectMethod.Invoke(null, new object[]{ source, projection });
有人知道更好的方法吗?
更新:dynamic失败的原因是匿名类型被定义为internal。这就是为什么它使用概念验证项目工作的原因,其中所有内容都在同一个程序集中。
我对此很满意。我仍然想找到一种更简洁的方法来找到正确的Queryable.Select 重载。
【问题讨论】:
-
真的有必要拨打
ParameterRebinder.ReplaceParameter吗?表达式主体已经具有正确的类型,因此在重建表达式时,它将具有正确的类型。我自己的测试似乎在这里工作。 -
@JeffM:调用是替换匿名类型初始值设定项中原始 lambda 表达式的参数所必需的,否则您会得到
variable 'x' of type 'Customer' referenced from scope '', but it is not defined。我可能应该创建一个完整的测试用例,因为它在概念验证项目中也适用于我。 -
哦,我忘记了您使用不同的参数实例来重建您的表达式。我的测试只是在新的 lambda 表达式中重用现有参数和主体(并且有效)。会为你做同样的工作吗?
-
@JeffM:你能发布你的测试吗?请记住,投影是在子类中定义的。
标签: linq generics reflection dynamic c#-4.0