【问题标题】:Using Expression<Func<T,bool>> as parameter for new expression使用 Expression<Func<T,bool>> 作为新表达式的参数
【发布时间】:2011-12-20 19:37:49
【问题描述】:

我有一些当前在 SQL 中构建 In 语句的代码。我建立一个表达式,它返回;

value(generic(list[T])).Contains(x => x.Id)

这很好用,所以如果我有一个对象列表;

public class ObjectToSearch 
{
  public int IdToSearchOn {get;set;}
}

我想搜索 ids 1、2、3,它工作得很好。我的 SQL 查询很棒。

现在,我需要搜索嵌套对象列表,所以我可能需要;

public class ParentObjectToSearch 
{
  public IEnumerable<ObjectToSearch> Objects {get;set;}
}

所以,查看我找到的一些代码 (How do I create an expression tree calling IEnumerable<TSource>.Any(...)?),我想我可以调整该方法,并封装对 Any 或 All 的调用,这样就可以了。这很好用,直到我真正开始对数据库进行测试,我才明白;

无法比较“System.Collections.Generic.ICollection`1”类型的元素。仅支持原始类型(如 Int32、String 和 Guid)和实体类型。

var collectionType = GetIEnumerableImpl( forCollection.Type );

Type elementType = collectionType.GetGenericArguments( )[0];

MethodInfo method = BaseFilter.GetType( ).GetMethod( "FilterWith" );

MethodInfo genericMethod = method.MakeGenericMethod( new[] { elementType } );

return (genericMethod.Invoke( BaseFilter, null ) as LambdaExpression);

FilterWith 是我在原始过滤器上调用的方法,希望能恢复我的表达。因此,当与外部表达式结合时,看起来我的内部表达式被错误地评估了。我的基本目标是(我相信);

x => x.Collection.Contains( y => new { 1, 3, 3}.Contains( y.Id));

如果我单独测试内部过滤,它可以正常工作,所以我认为这就是我尝试组合元素的方式,如果我尝试使用 Contains 而不是 Any 或 All,我仍然会遇到相同的错误。

我已将 Entity Framework 放入标签中,因为这是作为针对实体集的表达式进行评估的,并且有人可能有这样做的经验。

更新想了一晚上,我想我有一个更好的问题;

如何构建 Where 表达式,以便构建;

x => x.Collection.Where(y => new[] { 1, 3 }.Contains(y.Id)).Count( ) > 0

【问题讨论】:

  • 您是否尝试手动构建组合过滤器(即不通过反射)并验证它是否有效?您能否在问题中发布您完成的过滤器,并评论其中哪些部分有效,哪些无效?
  • 我继续返回错误;无法比较 'System.Collections.Generic.ICollection1'. Only primitive types (such as Int32, String, and Guid) and entity types are supported. leads me to believe that the inner expression I'm evaluating is actually what's causing the error. That seems to be x =&gt; value( System.Collections.Generic.List1[System.Int32] ).Contains( Convert( x.Id) ) ) 类型的元素

标签: c# entity-framework lambda expression


【解决方案1】:

我认为这里的问题是 EF 认为您要求它将 ObjectToSearch 发送到数据库并进行比较。换句话说,我认为您是在询问 SQL Server 某个字段中的任何值是否等于某个类的实例,这显然是行不通的:

// This won't work because it is asking EF to generate a SQL value equivalent to some class instance
((List<ParentObjectToSearch>)someList).Contains(x => x.Id)

我不能确定 - 问题中似乎缺少一个使用中的具体示例。如果这听起来不错,请尝试在生成查询之前展平您要搜索的值集:

// Assuming var outerList = some List<ParentObjectToSearch>
// this un-nests the IDs, so they can be sent to SQL Server as integers
// (which can be converted to a CONTAINS or = clause)
var listOfUnNestedIDs = outerList.SelectMany(po=>po.Objects.Select(o=>o.IdToSearchOn));

【讨论】:

    【解决方案2】:

    最初的错误实际上是由于我试图对集合进行 null 检查,嗯,这肯定不能在 SQL 中完成。

    那么,Any 和 All 不能转换为 SQL 表达式,所以;

    Expression<Func<TEntity, bool>> result = Expression.Lambda<Func<TEntity, bool>>(
                Expression.GreaterThan(
                    Expression.Call( CountMethod( elementType ),
                                    Expression.Call( WhereMethod( elementType ),
                                                    theCollectionWeAreSearching,
                                                    filter ) ),
                    Expression.Constant( 0 ) ), param );
    

    elementType 是集合中元素的类型。过滤器是一个测试我的列表的表达式。 Count 和 Where 方法检索如下;

    public MethodInfo GetMethodFromEnumerable(string methodName, params Func<MethodInfo, bool>[] filters)
            {
                var methods = typeof( Enumerable )
                    .GetMethods( BindingFlags.Static | BindingFlags.Public )
                    .Where( mi => mi.Name == methodName );
    
                methods = filters.Aggregate( methods, (current, filter) => current.Where( filter ) );
    
                return methods.First( );
            }
    
            public MethodInfo WhereMethod(Type collectionType)
            {
                // Get the Func<T,bool> version
                var getWhereMethod = GetMethodFromEnumerable( "Where",
                                        mi => mi.GetParameters( )[1].ParameterType.GetGenericArguments( ).Count( ) == 2 );
    
                return getWhereMethod.MakeGenericMethod( collectionType );
            }
    
            public MethodInfo CountMethod(Type collectionType)
            {
                var getCountMethod = GetMethodFromEnumerable( "Count" ); // There can be only one
    
                return getCountMethod.MakeGenericMethod( collectionType );
            }
    

    我认为发生的事情是引入了如此多的新代码,这让我开始寻找没有问题的问题!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-25
      • 2010-10-02
      • 1970-01-01
      相关资源
      最近更新 更多