Использование выражения> как параметр для нового выражения - PullRequest
0 голосов
/ 20 декабря 2011

У меня есть код, который в настоящее время создает оператор In в SQL. Я строю выражение, оно возвращает;

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

Это отлично работает, поэтому, если у меня есть список объектов;

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

И я хочу найти идентификаторы 1, 2, 3, он работает просто отлично. Мой SQL-запрос отличный.

Теперь мне нужно выполнить поиск в списке вложенных объектов, так что я мог бы иметь;

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

Итак, глядя на некоторый код, который я нашел ( Как мне создать дерево выражений, вызывающее IEnumerable .Any (...)? ) Я решил, что могу адаптировать метод и обернуть позвоните любому или всем, и это будет работать. Это прекрасно работало, пока я не пришел на тестирование базы данных, я получаю;

Невозможно сравнить элементы типа '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

Ответы [ 2 ]

1 голос
/ 22 декабря 2011

Первоначальная ошибка на самом деле была вызвана тем фактом, что я пытался выполнить проверку нуля по отношению к коллекции, ну, конечно, это не может быть сделано в 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 );
        }

Я думаю, что то, что произошло, было введением такого большого количества нового кода, который заставил меня искать проблемы там, где их не было!

1 голос
/ 21 декабря 2011

Я думаю, что проблема здесь в том, что 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));
...