使用Expression< Func< T, bool>比;作为new表达式的参数

本文关键字:作为 new 参数 表达式 Func Expression bool 使用 | 更新日期: 2023-09-27 18:18:21

我有一些代码,目前在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;}
}

所以,看看我发现的一些代码(我如何创建一个表达式树调用IEnumerable.Any(…)?)我想我可以调整这个方法,把一个呼叫包装成Any or 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,我仍然会得到相同的错误。

我把实体框架在标签中,因为这是对实体集的表达式进行评估,有人可能有这样做的经验。

更新经过一晚上的思考,我想我有一个更好的问题;

如何构建Where表达式,这样我就可以构建;

x => x. collection。其中(y => new[]{1,3}。包含(y.Id))。Count()> 0

使用Expression< Func< T, bool>比;作为new表达式的参数

我认为这里的问题是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));

最初的错误实际上是由我试图对集合做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 );
        }

我认为原因是引入了太多的新代码,导致我去寻找根本不存在的问题!