Выражение Entity Framework для поиска объектов, не соответствующих какому-либо свойству

Мне нужно пользовательское выражение, которое работает в Entity Framework. Метод должен иметь такую ​​подпись:

var ids = new List<int> { 1, 2, 3 };
Context.FooEntities.WithoutId(e => e.Id, ids);

Это должно дать мне все Foo лица, которые не имеют Id свойства, которые соответствуют тем в списке.

Моя попытка основана на существующем примере здесь.

public static IQueryable<T> WithoutId<T>(
    this IQueryable<T> entities,
    Expression<Func<T, int>> propertySelector,
    ICollection<int> ids) {

    var property = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;

    ParameterExpression parameter = Expression.Parameter(typeof(T));

    var expression = Expression.Lambda<Func<T, bool>>(
        Expression.Not(
            Expression.Call(
                Expression.Constant(ids),
                typeof(ICollection<int>).GetMethod("Contains"), 
                Expression.Property(parameter, property))), 
        parameter);

    return entities.Where(expression);
}

Проблема в том, когда ids пусто, то возвращает все сущности. Он не должен возвращать никаких сущностей.

4 ответа

Решение

Как насчет этого? (просто как идея, не законченный код)

IEnumerable<Entity> Get()
{
    var ids = new[] { 1, 2, 3 };
    if (ids.Length == 0) return Enumerable.Empty<Entity>();

    return MyContext.MyEntities.Where(x=>ids.Contains(x.Id)).ToArray();
}

Если список идентификаторов пуст, просто верните пустую коллекцию:

if (ids.Count() != 0)
{
   var property = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;

    ParameterExpression parameter = Expression.Parameter(typeof(T));

    var expression = Expression.Lambda<Func<T, bool>>(
        Expression.Not(
            Expression.Call(
                Expression.Constant(ids),
                typeof(ICollection<int>).GetMethod("Contains"), 
                Expression.Property(parameter, property))), 
        parameter);

    return entities.Where(expression);
}
return new List<T>().AsQueryable()//Or Enumerable.Empty<T>().AsQueryable();

Вы можете попробовать, как показано ниже.

public static IQueryable<T> WithoutId<T>(this IQueryable<T> entities,Expression<Func<T, int>> propertySelector,ICollection<int> ids) {

    if (ids.Any())
     {
    var property = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;

    ParameterExpression parameter = Expression.Parameter(typeof(T));

    var expression = Expression.Lambda<Func<T, bool>>(
        Expression.Not(
            Expression.Call(
                Expression.Constant(ids),
                typeof(ICollection<int>).GetMethod("Contains"), 
                Expression.Property(parameter, property))),parameter);

    return entities.Where(expression);
   }
   else{
      return Enumerable.Empty<T>().AsQueryable();
     }
}

Мой вопрос может быть решен без сложного выражения, как указано в ответах выше (с использованием простого "где + содержит", который поддерживается EF).

Но формальный способ мог бы быть таким, который, кажется, работает для меня (даже если это излишне):

public static IQueryable<T> WithoutId<T>(
    this IQueryable<T> entities,
    Expression<Func<T, int>> propertySelector,
    ICollection<int> ids) {

    if (!ids.Any()) {                                          // here is the trick
        /*
        expression = Expression.Lambda<Func<TEntity, bool>>(
            Expression.Constant(false), 
            parameter);
        */
        return Enumerable.Empty<T>().AsQueryable()
    }

    var property = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;

    ParameterExpression parameter = Expression.Parameter(typeof(T));

    var expression = Expression.Lambda<Func<T, bool>>(
        Expression.Not(
            Expression.Call(
                Expression.Constant(ids),
                typeof(ICollection<int>).GetMethod("Contains"), 
                Expression.Property(parameter, property))), 
        parameter);

    return entities.Where(expression);
}
Другие вопросы по тегам