使用在运行时解析的类型调用System.Linq.Queryable方法

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过载.

Die*_*hon 3

修复方法是如此简单却令人痛苦:

[assembly: InternalsVisibleTo("My.Search.Lib.Assembly")]
Run Code Online (Sandbox Code Playgroud)