【问题标题】:Expression.Lambda<Func<T,bool>> Says 'T' could not be foundExpression.Lambda<Func<T,bool>> 说找不到'T'
【发布时间】:2012-01-10 17:33:56
【问题描述】:

我正在尝试创建一个通用 Exists() Command 类,该类能够为我的数据上下文(数据库)中任何 LinqToSQL 类(表)的任何公共属性(字段)组合构造一个 where 子句。

如果基于 MSDN 中的 sn-ps 编写了以下内容,并且发现除非我明确声明 Expression.Lambda 参数的实体类型(在本例中为 MyApp.Data.Organization),否则我无法成功运行代码Expression.Call 语句。

换句话说,

Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe }));

不工作,但是,

Expression.Lambda<Func<MyApp.Data.Organization,bool>>(predBody, new ParameterExpression[] { pe }));

确实有效。我想要类似前者的方法,因此该方法对我的数据上下文中的所有 LinqToSQL 类保持通用。

我已经验证了 Expression.Call 语句之前的所有代码都可以正常工作并且会生成正确的谓词。为了保持参数类型的通用性,我需要更改什么,以便相同的代码适用于数据上下文中的任何 LinqToSQL 类?

清晰度修订 似乎我使用 T 作为变量是令人困惑的事情。这是修改后的代码:

OrganizationCriteria criteria = new OrganizationCriteria { OrganizationId = 2 };
 using (var ctx = ContextManager<MyApp.Data.MyAppDataContext>.GetManager(myConnectionString, false)) {
                IQueryable tbl = ctx.DataContext.GetTable(criteria.EntityType).AsQueryable();
                Type tblType = tbl.ElementType;

                ParameterExpression pe = Expression.Parameter( tblType, "Item" );

                Expression left;
                Expression right;
                Expression prev = null;
                Expression curr = null;
                Expression predBody = null;
                foreach ( KeyValuePair<string, object> kvp in criteria.StateBag ) {
                    prev = curr;
                    object val = kvp.Value;
                    if (val is System.String ) {
                        left = Expression.Call( pe, typeof( string ).GetMethod( "ToLower", System.Type.EmptyTypes ) );
                        right = Expression.Constant( kvp.Value.ToString() );
                    }
                    else {
                        left = Expression.Property( pe, tblType.GetProperty( kvp.Key ) );
                        right = Expression.Constant( kvp.Value, tblType.GetProperty( kvp.Key ).PropertyType );
                    }
                    curr = Expression.Equal( left, right );

                    if ( prev != null ) {
                        predBody = Expression.AndAlso( prev, curr );
                    }
                    else {
                        predBody = curr;
                    }
                }

                MethodCallExpression whereCall = Expression.Call(
                    typeof( Queryable ),
                    "Where",
                    new Type[] { tblType },
                    tbl.Expression,
                    Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe }));

                var results = tbl.Provider.CreateQuery( whereCall );
}

【问题讨论】:

  • 那么, T 是从哪里来的?
  • @BoltClock 他想创建一个接受类型参数T的方法,所以在创建表达式时无法指定具体的T
  • 答案是使用 Expression.Lambda() 的非泛型重载。因此,将 Expression.Lambda>(predBody, new ParameterExpression[] { pe }) 替换为 Expression.Lambda(predBody, new ParameterExpression[] { pe }) 就可以了,

标签: c# linq generics lambda expression


【解决方案1】:

在您的代码中,T 是一个变量,而不是一个类型;您不能将变量用作泛型类型参数(即使它是 Type 类型的变量)

【讨论】:

  • 谢谢托马斯。我认为我使用 T 作为声明的局部变量是令人困惑的事情。我在原始帖子的代码中将其更改为 tblType。
  • @FuzzyCoder 你的 tblType 仍然是一个变量,而不是一个类型。这不是你使用泛型的方式。您必须在方法签名中传入一个 T。
【解决方案2】:

看起来您正在尝试将T 作为一种类型进行访问 - 请记住,它实际上是一个泛型类型参数。如果要使用实际类型,请使用typeof(T)。例如这一行:

ParameterExpression pe = Expression.Parameter(T, "Item");

应该是这样的:

ParameterExpression pe = Expression.Parameter(typeof(T), "Item");

【讨论】:

  • 谢谢。请用更清晰的代码查看我的修改后的帖子。当我尝试在 Expression.Lambda() 调用中使用已知类型以外的任何内容时,我收到编译器错误。在运行时之前不知道输入参数类型的情况下如何让它工作?
【解决方案3】:

您必须将类型参数传递给包含您的代码的方法(或作为您的类的类型参数):

void SomeMethod<T>() {
   ...       
    Expression.Lambda<Func<T,bool>> ...
   ...
}

【讨论】:

    【解决方案4】:

    由于tblType 仅在运行时已知,因此您只能通过反射调用Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;()。假设您知道如何为静态Lambda 方法的所需重载获取正确的 MethodInfo:

    var funcType = typeof(Func<,>).MakeGenericType(tblType, typeof(bool));
    var genericMethodInfo = typeof(Expression).GetMethod("Lambda", ...
    var methodInfo = genericMethodInfo.MakeGenericMethod(funcType);
    var expression = (Expression)methodInfo.Invoke(...
    

    但是对于所有这些重载,获得正确的 MethodInfo 绝非易事。

    【讨论】:

      【解决方案5】:

      答案是使用 Expression.Lambda() 的非泛型重载。因此,替换:

      Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe })
      

      Expression.Lambda(predBody, new ParameterExpression[] { pe })
      

      成功了,我现在有了一个完全通用的方法来查询任何字段的任何表。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-31
        • 1970-01-01
        相关资源
        最近更新 更多