我在构建一个基于LINQ的查询生成器。使用在运行时解析的类型调用System.Linq.Queryable方法
其中一项功能是能够将任意的服务器端投影指定为查询定义的一部分。例如:
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)
};
}
}
由于用户必须随后能够排序的投影特性(相对于客户性能),I重新创建表达的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'真的有必要?表达式体已经有了正确的类型,所以当表达式被重建时,它将具有正确的类型。我自己的测试似乎在这里工作。 – 2011-03-25 23:39:43
@JeffM:在匿名类型初始化程序中调用是必要的,以取代原始lambda表达式的参数,否则您会从范围''引用类型为'Customer'的变量'x',但未定义' 。我应该创建一个完整的测试用例,因为它也适用于我的概念验证项目。 – 2011-03-25 23:44:42
哦,我忘了你使用了一个不同的参数实例来重建你的表达式。我的测试只是重新使用新的lambda表达式(和工程)现有的参数和正文。会为你做同样的工作吗? – 2011-03-25 23:49:17