EntceptCollection与List上的(除外)

时间:2012-08-21 05:34:25

标签: c# entity-framework linq-to-entities out-of-memory

使用EntityFramework 4.我有EntityCollection<SomeEntity> currentEntities个~500k实体和List<SomeEntity> importedEntities也有~500k记录。我希望获得currentEntitiesimportedEntities中不存在的所有记录的列表。

调用currentEntities.Select(x => x.ID).Except(importedEntities.Select(x => x.ID))获取发生记录的唯一ID会导致System.OutOfMemoryException,因为它显然会将所有实体加载到内存中。

调用currentEntities.Where(x => !importedEntities.Any(y => y.ID == x.ID))失败,NotSupportedException(“只有基本类型(例如Int32,String和Guid')在此上下文中受支持”)。

currentEntities位于SQL Server 2008 R2数据库中,而importedEntities位于内存中。

这在L2E中是否可行?

4 个答案:

答案 0 :(得分:2)

在内存中的两个“大”整数列表上执行except不是重点。如果我执行Enumerable.Range(0, 500000).Except(Enumerable.Range(500, 500000)).Count(),它会在我说出OutOfMemoryException之前返回500。所以我认为将问题减少到整数except应该适合你:

var newIds = importedEntities.Select(x => x.ID).ToArray()
             .Except(currentEntities.Select(x => x.ID).ToArray()).ToArray();

因此,只有整数将被加载到内存中,而不是实体对象。

现在你可以做到:

importedEntities.Where(x => newIds.Contains(x.ID))

如果newIds不太长。什么太长了? linq语句产生一个IN子句,它可以很容易地包含几千个项目,但是如果它比10,000个更长,你可能应该以块的形式处理这些ID。

顺便说一句(1)。我在这里假设两个列表都在不同的上下文中,甚至可能是不同的数据库。但是,如果它们处于相同的上下文中,您可能会成功:

importedEntities.Where(x => !currentEntities.Select(y => y.ID)
                           .Any(id => id == x.ID))

这会产生NOT EXISTS sql查询。如果有“很多”新项目,您仍可能遇到OutOfMemoryException。如果是这样,您可以使用分页机制(Skip - Take)来处理块中的新项目。

顺便说一下(2),我交换了currentEntities和importEntities,因为我假设你对新导入的项目感兴趣,如果我错了,请撤消。

答案 1 :(得分:0)

试试这个。

以下是当importEntities是内存中集合时,而不是数据库中的集合。

var importedEntityIds = importedEntities
    .Select(x => x.ID).ToList(); // Convert to a list of some value type.
var result = currentEntities
    .Where(x => !importedEntityIds.Contains(x.ID))
    .ToList();

以下是当importEntities是数据库中的集合时。

var importedEntityIds = importedEntities
    .Select(x => x.ID); // This will be translated in SQL to a NOT IN (...)
var result = currentEntities
    .Where(x => !importedEntityIds.Contains(x.ID))
    .ToList();

答案 2 :(得分:0)

我假设您正在尝试找出需要添加到某个系统或某些内容的新条目。这并不罕见。因为你的数据集太大了,要么两者都不能同时存在于内存中,要么各种LINQ扩展方法在内存中创建集合的副本,这些副本在内存中最大化,因为你现在在内存中有3或4个列表副本。

你或许可以使用结果将在IEnumerable中的事实,它只会在sql server的结果上进行迭代时加载大量的记录。

试试这个

foreach (var current in currentEntities)
{
    if (importedEntities.Contains(current))
    {
        importedEntities.Remove(current);
    }
}

代码假定Equals和GetHashCode方法被覆盖以通过属性比较对象,而不是来自对象基类的默认对象,它只比较指针或一些低级别的东西。

注意:如果您要执行以下操作

foreach (var current in currentEntities.ToList())
{
    if (importedEntities.Contains(current))
    {
        importedEntities.Remove(current);
    }
}

然后您将获得System.OutOfMemoryException,因为现在整个数据库表都在内存中,而不仅仅是您要比较的行。所以不要做ToList或类似的东西。

答案 3 :(得分:-1)

从导入的实体中选择所有ID:

var importedids = importedEntities.Select(entity => entity.ID).ToList();

创建一个HashSet:

var importedidshashset = new HashSet<int>(importedids);

过滤currentEntities:

currentEntities.Where(entity => !importedidshashset.Contains(entity.ID)).ToList().