使用LINQ选择集合中每个项目的列表,除非它存在于另一个集合中

时间:2014-03-12 15:19:19

标签: c# linq entity-framework lambda ienumerable

这有效,但不仅看起来不好,但看起来效率不高(我还没有评估性能,因为我知道必须有更好的方法来实现这一点)。

public IEnumerable<Observation> AvailableObservations
{
    get 
    {
        foreach (var observation in db.Observations)
        {
            if (Observations.Any(x => x.Id == observation.Id))
            {

            }
            else
            {
                yield return observation;
            }
        }
    }
}

基本上,我想要列表db.Observations中的所有内容 (通过EF6从数据库中提取)并删除this.Observations中当前选中的所有条目,这是ICollection<Observations>

我已经尝试过使用.Except(this.Observations)但是我得认为可能与使用相关的错误除了在IEnumerable的实体上使用ICollection之外。

任何会删除foreach循环的东西都是一个好的开始。

2 个答案:

答案 0 :(得分:2)

你的循环相当于:

return db.Observations.Where(o => !Observations.Any(oo => oo.Id == o.Id));

但这并不比你拥有的更有效。

一种更有效的方法是创建一个ID的HashSet并过滤掉它:

HashSet<int> ids = new HashSet<int>(Observations.Select(o => o.Id));
return db.Observations.Where(o => !ids.Contains(o.Id));

这样,您只需遍历主列表一次,即可创建可在O(1)时间内搜索的HashSet

答案 1 :(得分:1)

您可以在此处进行两项优化:

  1. 限制从数据库中提取的观察数量
  2. 快速查找选定的观察ID
  3. 当前的实施具有O(N * M)复杂度,其中Ndb.Observations中的项目数,Mthis.Observations中的项目数。

    首先在HashSet中创建this.Observations个ID会有什么帮助?

    var observationIds = new HashSet<int>(this.Observations.Select(x => x.Id));
    

    这将允许您快速查找ID。

    将此与where子句(使用LINQ的Where())相结合,以获得有效的查询:

    public IEnumerable<Observation> AvailableObservations
    {
        get 
        {
            var observationIds = new HashSet<int>(this.Observations.Select(x => x.Id));
            return db.Observations.Where(x => !observationIds.Contains(x.Id));
        }
    }