【问题标题】:An Entity Framework expression to find entities that don't match some property用于查找与某些属性不匹配的实体的实体框架表达式
【发布时间】:2016-09-12 14:58:56
【问题描述】:

我需要一个在实体框架中工作的自定义表达式。该方法应具有以下签名:

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

这应该给我所有Foo 实体,它们不具有与列表中匹配的Id 属性。

我的尝试基于existing example here

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 为空时,它会返回所有实体。 它应该不返回任何实体

【问题讨论】:

  • 奇怪的要求。你想排除一些id,如果没有要排除的id,不排除什么是正常的,即包含一切,即返回所有实体。
  • @IvanStoev 不,它不应该返回任何实体。它应该返回一个空集。
  • @hbob - 我想他明白了,他说这是不合逻辑的操作。
  • @ErikFunkenbusch 也许你是对的......虽然你能用形式逻辑描述为什么它不合逻辑吗?我不记得到底该怎么做,但似乎这不是逻辑问题,而是期望:“给我所有 ID 不是 {nothing} 的实体”。这是否意味着它们必须是“某物”?也许这在形式上是不合逻辑的,我不知道,想想就头疼:-)
  • @hbob - 在集合论(Link 和 SQL 所基于的)中,“无”被称为“空集”。这就像一个空集合。所以是的,说某事不包括空集意味着返回所有内容(除了空集,这是你想要返回的,所以它与逻辑期望相反)

标签: c# entity-framework linq expression func


【解决方案1】:

如果id列表为空,则返回一个空集合:

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

【讨论】:

    【解决方案2】:

    你可以试试如下图。

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

    【讨论】:

    • 返回 null 对调用者来说是非常意外的。他会假设结果,或者是一个空集。
    【解决方案3】:

    这个怎么样? (只是想法,未完成代码)

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

    【讨论】:

    • @Servy,不同意。最初的问题是关于“这应该给我所有不具有与列表中匹配的 Id 属性的 Foo 实体。”虽然其他答案试图改进建议的解决方案,但我试图解决最初的问题。
    • @hbob Eric 的回答甚至没有编译。这远不是一个可行的答案。正如您所说,这个答案绝不符合问题的要求。它没有实现带有指定为要求的签名的方法。
    • @tym32167 问题特别提到它需要创建一个具有特定签名的方法,其中要过滤的列在Expression 中指定。这不会那样做,因此不符合问题的要求。
    • @Servy ,“这个答案绝不符合要求......它没有实现该方法”。我不必完全实现该方法来给出有用的答案。这不是强制性的。我给出的答案可以帮助用户,它问。这是本网站的主要目的,为人们提供有用的答案。对我来说很明显,鲍勃想问什么,我会在几分钟内给出答案。如果您不同意这一点,那么,对不起。
    • @Servy 我仍然不同意,我认为我的回答非常符合问题并且是正确的。我不必完全实现该方法,但我只展示了想法,即如何实现该方法。我在这里看不到任何“完全不同”的问题。如果您认为只有实现该方法才能作为正确答案,请从网站规则或其他一些文件中证明这一点,因为对我来说仍然不清楚。
    【解决方案4】:

    我的问题无需复杂的表达就可以解决,如上面的答案所示(使用简单的“where+contains”,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);
    }
    

    【讨论】:

    • 注意:1)这是矫枉过正,因为 EF 支持“where+contains”,2)@ErikFunkenbusch 评论现有行为(返回一切而不是什么)是正确的。这意味着这是对该问题的正确技术答案,但不应使用它。
    • 没有任何价值通过调用Where 与一个总是假的谓词而不是只返回一个空的可查询,如其他答案所示。即使您确实想要这样做,也没有理由使用 lambda 手动构建表达式。实际进行过滤时当然是另一回事。
    • 当然 EF 支持在 Where 中调用 Contains。问题的表达式代码正在构建一个调用Contains 的表达式。如果你想使用属性选择器,那么构建表达式当然是必要的,你必须在编写这个方法时将它应用到一个未知的字段。
    • @Servy 已编辑。更好?
    • 所以现在您只是在复制其他两个答案所做的事情。您不应该仅仅为了复制另一个答案而发布新答案,而是因为您有新的解决方案要添加。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-17
    • 1970-01-01
    • 2018-07-23
    • 1970-01-01
    相关资源
    最近更新 更多