Die*_*hon 5 linq generics reflection dynamic c#-4.0
我正在构建一个基于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)
};
}
}
Run Code Online (Sandbox Code Playgroud)
由于用户必须能够对投影属性进行排序(而不是Customer属性),我将表达式重新创建为: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());
Run Code Online (Sandbox Code Playgroud)
为了返回预计的查询,我希望能够做到这一点(事实上,它在一个几乎相同的概念证明中工作):
return Queryable.Select((IQueryable<TRoot>)source, (dynamic)properProjection);
Run Code Online (Sandbox Code Playgroud)
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 ...
Run Code Online (Sandbox Code Playgroud)
如果仔细观察,它会错误地推断通用参数: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 });
Run Code Online (Sandbox Code Playgroud)
有谁知道更好的方法?
更新:dynamic
失败的原因是匿名类型被定义为internal
.这就是为什么它使用概念验证项目,其中所有内容都在同一个程序集中.
我很酷.我仍然想找到一种更清洁的方法来找到正确的Queryable.Select
过载.
修复方法是如此简单却令人痛苦:
[assembly: InternalsVisibleTo("My.Search.Lib.Assembly")]
Run Code Online (Sandbox Code Playgroud)