使用表达式<func <t,bool>&gt;作为新表达式的参数</func <t,bool>

时间:2011-12-20 19:37:50

标签: c# entity-framework lambda expression

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

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

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

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

我想搜索ID 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 =&gt; x.Collection.Where(y =&gt; new [] {1,3} .Contains(y.Id))。Count()&gt; 0

2 个答案:

答案 0 :(得分: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));

答案 1 :(得分:1)

原始错误实际上是由于我试图对集合进行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 );
        }

我认为发生的事情是引入了如此多的新代码导致我在没有任何问题的情况下寻找问题!