使用EntityFramework 4.我有EntityCollection<SomeEntity> currentEntities
个~500k实体和List<SomeEntity> importedEntities
也有~500k记录。我希望获得currentEntities
中importedEntities
中不存在的所有记录的列表。
调用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中是否可行?
答案 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().