使用表达式>作为新表达式的参数

发布于 12-22 10:31 字数 1680 浏览 4 评论 0 原文

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

因此,查看我发现的一些代码(如何创建表达式树调用 IEnumerable.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,我仍然会得到相同的错误。

我已将实体框架放入标签中,因为它被评估为针对实体集的表达式,并且有人可能有这样做的经验。

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

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

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

I have some code that currently builds up an In statement in SQL. I build an expression, it returns;

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

This works fine, so if I have a list of objects;

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

And I want to search for ids 1, 2, 3, it works just fine. My SQL query is great.

Now, I have a need to search a list of nested objects, so I might have;

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

So, looking at some code I found (How do I create an expression tree calling IEnumerable<TSource>.Any(...)?) I figured I could adapt the method, and wrap a call to Any or All, and it would work. This worked great, until I actually came to test against the database, I get;

Cannot compare elements of type 'System.Collections.Generic.ICollection`1'. Only primitive types (such as Int32, String, and Guid) and entity types are supported.

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 is the method I'm calling on the original filter, in the hope of getting back my expression. So, it looks like my inner expression is being evaluated incorrectly when combined with the outer expression. What I'm basically aiming for is (I believe);

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

If I test the inner filtering separately, it works fine, so I assume it's just how I'm trying to combine the elements, and if I try and use Contains instead of Any or All, I still get the same error.

I've put Entity Framework in the tags, because this is being evaluated as expressions against an entity set, and someone may have experience of doing this.

Update Having a night to think about it, I think I have a better question;

How do I build a Where expression, so I can build;

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

愛放△進行李 2024-12-29 10:31:08

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

I think that the problem here is that EF thinks that you are asking it to ship the ObjectToSearch to the database and compare on that. In other words, I think you are asking SQL Server whether any values in some field are equal to an instance of some class, which clearly won't work:

// 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)

I can't be sure- it seems like a concrete example of this in use is missing from the question. If this sounds right, try flattening the set of values that you want to search on before generating the query:

// 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));
养猫人 2024-12-29 10:31:08

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

我认为发生的事情是引入了如此多的新代码,导致我去寻找根本不存在的问题!

The original error was actually being caused by the fact that I was trying to do check of null against the collection, well, that certainly can't be done in SQL.

Then, Any and All can't be converted to SQL expressions, so;

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 );

The elementType is the type of the element within the collection. The filter is an expression that tests my list. The Count and Where methods retrieved as follows;

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 );
        }

I think that what happened was the introduction of so much new code led me to look for problems where there weren't any!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文