使用运行时解析的类型调用 System.Linq.Queryable 方法
我正在构建一个基于 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)
};
}
}
由于用户必须能够对投影属性(而不是 Customer 属性)进行排序,因此我将表达式重新创建为 Func
而不是 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 是中的类型参数搜索定义。这会导致以下异常:
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
失败的原因是匿名类型被定义为内部
。这就是为什么它使用概念验证项目来工作,其中所有内容都在同一个程序集中。
我对此很满意。我仍然希望找到一种更简洁的方法来找到正确的 Queryable.Select 重载。
I'm building a LINQ-based query generator.
One of the features is being able to specify an arbitrary server-side projection as part of the query definition. For example:
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)
};
}
}
Since the user must then be able to sort on the projection properties (as opposed to Customer properties), I recreate the expression as a Func<Customer,anonymous type>
instead of 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());
In order to return the projected query, I'd love to be able to do this (which, in fact, works in an almost identical proof of concept):
return Queryable.Select((IQueryable<TRoot>)source, (dynamic)properProjection);
TRoot is the type parameter in SearchDefinition. This results in the following exception:
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 ...
If you look closely, it's inferring the generic parameters incorrectly: Customer,object
instead of Customer,anonymous type
, which is the actual type of the properProjection
expression (double-checked)
My workaround is using reflection. But with generic arguments, it's a real mess:
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 });
Does anyone know of a better way?
Update: the reason why dynamic
fails is that anonymous types are defined as internal
. That's why it worked using a proof-of-concept project, where everything was in the same assembly.
I'm cool with that. I'd still like to find a cleaner way to find the right Queryable.Select
overload.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
修复方法是如此简单却令人痛苦:
The fix is so simple it hurts:
这是我按照要求进行的测试。这是在 Northwind 数据库上的,这对我来说效果很好。
此外,在这种情况下,动态方法解析实际上并没有多大作用,因为只有两个截然不同的
Select()
重载。最终,您只需要记住,您不会对结果进行任何静态类型检查,因为您没有任何静态类型信息。话虽如此,这也适用于您(使用上面的代码示例):Here's my test as requested. This on a Northwind database and this works fine for me.
Also, dynamic method resolution doesn't really do much for you in this case as there are only two very distinct overloads of
Select()
. Ultimately you just need to remember that you won't have any static type checking on your results since you don't have any static type information. With that said, this will also work for you (using the above code example):