Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用运行时解析的类型调用System.Linq.Queryable方法_Linq_Generics_Reflection_Dynamic_C# 4.0 - Fatal编程技术网

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

使用运行时解析的类型调用System.Linq.Queryable方法,linq,generics,reflection,dynamic,c#-4.0,Linq,Generics,Reflection,Dynamic,C# 4.0,我正在构建一个基于LINQ的查询生成器 其中一个特性是能够将任意服务器端投影指定为查询定义的一部分。例如: class CustomerSearch : SearchDefinition<Customer> { protected override Expression<Func<Customer, object>> GetProjection() { return x => new

我正在构建一个基于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)
                    };
    }
}
为了返回投影查询,我希望能够这样做(事实上,这在几乎相同的概念证明中起作用):

有人知道更好的方法吗?


更新
动态
失败的原因是匿名类型被定义为
内部
。这就是为什么它使用概念验证项目工作的原因,在这个项目中,所有东西都在同一个组件中


我不介意。我仍然希望找到一种更干净的方法来找到正确的
查询表。选择
重载。

这是我的测试。这是一个Northwind数据库,对我来说很好

static void Main(string[] args)
{
    var dc = new NorthwindDataContext();
    var source = dc.Categories;
    Expression<Func<Category, object>> expr =
        c => new
        {
            c.CategoryID,
            c.CategoryName,
        };
    var oldParameter = expr.Parameters.Single();
    var parameter = Expression.Parameter(oldParameter.Type, oldParameter.Name);
    var body = expr.Body;
    body = RebindParameter(body, oldParameter, parameter);

    Console.WriteLine("Parameter Type: {0}", parameter.Type);
    Console.WriteLine("Body Type: {0}", body.Type);

    var newExpr = Expression.Lambda(body, parameter);
    Console.WriteLine("Old Expression Type: {0}", expr.Type);
    Console.WriteLine("New Expression Type: {0}", newExpr.Type);

    var query = Queryable.Select(source, (dynamic)newExpr);
    Console.WriteLine(query);

    foreach (var item in query)
    {
        Console.WriteLine(item);
        Console.WriteLine("\t{0}", item.CategoryID.GetType());
        Console.WriteLine("\t{0}", item.CategoryName.GetType());
    }

    Console.Write("Press any key to continue . . . ");
    Console.ReadKey(true);
    Console.WriteLine();
}

static Expression RebindParameter(Expression expr, ParameterExpression oldParam, ParameterExpression newParam)
{
    switch (expr.NodeType)
    {
    case ExpressionType.Parameter:
        var parameterExpression = expr as ParameterExpression;
        return (parameterExpression.Name == oldParam.Name)
            ? newParam
            : parameterExpression;
    case ExpressionType.MemberAccess:
        var memberExpression = expr as MemberExpression;
        return memberExpression.Update(
            RebindParameter(memberExpression.Expression, oldParam, newParam));
    case ExpressionType.AndAlso:
    case ExpressionType.OrElse:
    case ExpressionType.Equal:
    case ExpressionType.NotEqual:
    case ExpressionType.LessThan:
    case ExpressionType.LessThanOrEqual:
    case ExpressionType.GreaterThan:
    case ExpressionType.GreaterThanOrEqual:
        var binaryExpression = expr as BinaryExpression;
        return binaryExpression.Update(
            RebindParameter(binaryExpression.Left, oldParam, newParam),
            binaryExpression.Conversion,
            RebindParameter(binaryExpression.Right, oldParam, newParam));
    case ExpressionType.New:
        var newExpression = expr as NewExpression;
        return newExpression.Update(
            newExpression.Arguments
                         .Select(arg => RebindParameter(arg, oldParam, newParam)));
    case ExpressionType.Call:
        var methodCallExpression = expr as MethodCallExpression;
        return methodCallExpression.Update(
            RebindParameter(methodCallExpression.Object, oldParam, newParam),
            methodCallExpression.Arguments
                                .Select(arg => RebindParameter(arg, oldParam, newParam)));
    default:
        return expr;
    }
}

解决方法非常简单,令人心痛:

[assembly: InternalsVisibleTo("My.Search.Lib.Assembly")]

调用
ParameterRebinder.ReplaceParameter
真的有必要吗?表达式主体已具有正确的类型,因此重建表达式时,它将具有正确的类型。我自己的测试似乎在这里工作。@JeffM:调用是替换匿名类型初始值设定项中原始lambda表达式中的参数所必需的,否则您将从作用域“”中引用类型为“Customer”的
变量“x”,但它未定义
。我可能应该创建一个完整的测试用例,因为它在一个概念验证项目中也适用于我。我的测试只是在一个新的lambda表达式中重用现有的参数和主体(并且有效)。做同样的工作对你有帮助吗?@JeffM:你能发布你的测试吗?记住投影是在一个子类中定义的。@Jeff我删除了RebindParameter的东西,这一部分现在更简单了,但我仍然得到相同的错误。“我将尝试创建一个完整的复制品。@杰夫:我发现了为什么它对你有效而对我无效。检查我的最新更新。@Diego:好的,很好。但一般来说,只要可以确定运行时类型,动态调度就会正常工作。如果表达式中有任何变量被声明为动态变量,则在运行时解决所有问题。@JeffM:不幸的是,后期绑定上下文中的
Cast()
就像
Cast
:它不允许我在投影类型的属性上添加
OrderBy
(这就是我做整个表达式的原因。Lambda hack)@JeffM:看看我的解决方案是什么:-)如果你没有将此标记为社区wiki,我仍然会投票支持你的努力,并让我走上正确的道路。谢谢
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 ...
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 });
static void Main(string[] args)
{
    var dc = new NorthwindDataContext();
    var source = dc.Categories;
    Expression<Func<Category, object>> expr =
        c => new
        {
            c.CategoryID,
            c.CategoryName,
        };
    var oldParameter = expr.Parameters.Single();
    var parameter = Expression.Parameter(oldParameter.Type, oldParameter.Name);
    var body = expr.Body;
    body = RebindParameter(body, oldParameter, parameter);

    Console.WriteLine("Parameter Type: {0}", parameter.Type);
    Console.WriteLine("Body Type: {0}", body.Type);

    var newExpr = Expression.Lambda(body, parameter);
    Console.WriteLine("Old Expression Type: {0}", expr.Type);
    Console.WriteLine("New Expression Type: {0}", newExpr.Type);

    var query = Queryable.Select(source, (dynamic)newExpr);
    Console.WriteLine(query);

    foreach (var item in query)
    {
        Console.WriteLine(item);
        Console.WriteLine("\t{0}", item.CategoryID.GetType());
        Console.WriteLine("\t{0}", item.CategoryName.GetType());
    }

    Console.Write("Press any key to continue . . . ");
    Console.ReadKey(true);
    Console.WriteLine();
}

static Expression RebindParameter(Expression expr, ParameterExpression oldParam, ParameterExpression newParam)
{
    switch (expr.NodeType)
    {
    case ExpressionType.Parameter:
        var parameterExpression = expr as ParameterExpression;
        return (parameterExpression.Name == oldParam.Name)
            ? newParam
            : parameterExpression;
    case ExpressionType.MemberAccess:
        var memberExpression = expr as MemberExpression;
        return memberExpression.Update(
            RebindParameter(memberExpression.Expression, oldParam, newParam));
    case ExpressionType.AndAlso:
    case ExpressionType.OrElse:
    case ExpressionType.Equal:
    case ExpressionType.NotEqual:
    case ExpressionType.LessThan:
    case ExpressionType.LessThanOrEqual:
    case ExpressionType.GreaterThan:
    case ExpressionType.GreaterThanOrEqual:
        var binaryExpression = expr as BinaryExpression;
        return binaryExpression.Update(
            RebindParameter(binaryExpression.Left, oldParam, newParam),
            binaryExpression.Conversion,
            RebindParameter(binaryExpression.Right, oldParam, newParam));
    case ExpressionType.New:
        var newExpression = expr as NewExpression;
        return newExpression.Update(
            newExpression.Arguments
                         .Select(arg => RebindParameter(arg, oldParam, newParam)));
    case ExpressionType.Call:
        var methodCallExpression = expr as MethodCallExpression;
        return methodCallExpression.Update(
            RebindParameter(methodCallExpression.Object, oldParam, newParam),
            methodCallExpression.Arguments
                                .Select(arg => RebindParameter(arg, oldParam, newParam)));
    default:
        return expr;
    }
}
var query = Queryable.Select(source, expr).Cast<dynamic>();
Console.WriteLine(query);

foreach (var item in query)
{
    Console.WriteLine(item);
    Console.WriteLine("\t{0}", item.CategoryID.GetType());
    Console.WriteLine("\t{0}", item.CategoryName.GetType());
}
[assembly: InternalsVisibleTo("My.Search.Lib.Assembly")]