3

使用 EntityFramework 4。我有一个EntityCollection<SomeEntity> currentEntities~500k 实体和一个List<SomeEntity> importedEntities也有 ~500k 记录。我想要列出所有currentEntities不存在的记录的列表importedEntities

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

调用currentEntities.Where(x => !importedEntities.Any(y => y.ID == x.ID))失败并显示NotSupportedException(“在此上下文中仅支持原始类型('例如 Int32、String 和 Guid')”)。

currentEntities在 SQL Server 2008 R2 数据库上,而importedEntities在内存中。

这在 L2E 中甚至可能吗?

4

4 回答 4

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 EXISTSsql 查询。OutOfMemoryException如果有“许多”新项目,您可能仍然会遇到问题。如果是这样,您可以使用分页机制 ( Skip- Take) 以块的形式处理新项目。

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

于 2012-08-21T19:02:40.583 回答
0

试试这个。

以下是importedEntities 是内存中集合而不是数据库中的集合时的情况。

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

以下是importedEntities 是数据库中的集合时的情况。

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();
于 2012-08-21T05:41:12.203 回答
0

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

您可能可以使用结果将位于 IEnumerable 中的事实,该 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 或类似的事情。

于 2012-08-21T13:17:56.587 回答
-1

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

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

创建一个哈希集:

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

过滤当前实体:

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

于 2012-08-21T06:09:15.143 回答