除了 Entity Framework 中太大外,性能不佳
Bad performance with except too large in Entity Framework
我必须将某些数据从一个数据库 (O) 迁移到另一个数据库 (D),其中一个问题是数据库的表在字段中具有不同的名称。我试图通过以下方式获取不在 D 数据库中的数据:
var dtOrigin = from o in genEntitCeres.X.AsNoTracking() select o;
var dtDestiny = from d in genEntitAgp.X.AsNoTracking() select d;
var rowsMatch = from tOrigin in dtOrigin.AsEnumerable()
join tDestiny in dtDestiny.AsEnumerable()
on new { tOrigin.a, tOrigin.b} equals
new { tDestiny.a, tDestiny.b}
select tOrigin;
var rowsNotMatch = (from tOrigin in dtOrigin.AsEnumerable()
where !rowsMatch.Contains(tOrigin)
select tOrigin);
当我尝试为每个或 rowsNotMatch.Count() 做一个时,它花费了太长时间...
我的解决方案是使用查询中的 SqlQuery 获取不匹配的行(5 秒以上,取决于数据不匹配的大小)。但我想知道 EF 是否还有其他方法,就像我的代码一样,但不会冻结。
尝试这样的事情:
var rowsNotMatch = from tOrigin in dtOrigin.AsEnumerable()
join tDestiny in dtDestiny.AsEnumerable()
on new { tOrigin.a, tOrigin.b} equals
new { tDestiny.a, tDestiny.b} into gD
from d in gD.DefaultIfEmpty()
where d == null
select tOrigin
这实质上是在 dtOrigin 和 dtDestiny 之间进行了左连接,仅返回 dtOrigin 中那些在 dtDestiny 中没有匹配项的项目。
这看起来像你在 2 个不同的 dbContexts 中打开 2 tables,想要根据每个 table 中的 2 列比较 2 tables,找到原始行table 没有匹配项。我不推荐使用 EF 来做这样的事情。在数据库 /w temp tables 中执行此操作会好得多。通过对每个 table 执行 .AsEnumerable(),您将把那些 table 的全部内容加载到内存中。
如果您必须在代码中做这样的事情:我会考虑这样的事情:
var destinationMatches = genEntitAgp.X.AsNoTracking()
.Select(x=> new { x.id, x.a, x.b })
.ToList();
如果目标 table 预计会很大,那么您应该使用 .Skip() 和 .Take() 在 1000 页左右的页面中执行此操作。
下一步是将所有行插入原始数据库中的缓冲区 table。缓冲区 table 保存目标行的 PK,以及要匹配的条件。
public class TempX
{
public int Id { get; set; }
public string a { get; set; }
public string b { get; set; }
}
我建议为此使用一个单独的 DBContext 而不是 Origin DBContext,以在没有更改跟踪的情况下进行初始化,并清除最初在 TempX table 中的所有记录。如果您要插入 > 1000 行,您希望以 1000 个为一组执行此操作,保存更改,然后处理并重新创建每个批次的上下文以保持操作快速。源 dbContext 也需要了解 TempX,只是不适用于总体。
一旦记录在源模式的 tempX table 中:
var entities = genEntitCeres.X.AsNoTracking()
.Where(x => !genEntitCeres.TempX.AsNoTracking().Any(tx=>tx.a == x.a && tx.b == x.b)).ToList();
同样,如果预计会有很多不匹配项,则不要使用 .ToList(),而是使用 .Skip() 和 .Take() 方法。
如果这可能在给定数据库上 运行 不止一次,那么我也会考虑希望有类似 CreatedAt/ModifiedAt dateTime 值的东西,我可以用它来过滤对于 Origin 和 Destination 数据库。 IE。记录最后一个 运行 DateTime,然后根据 > LastRunDate 从两个查询中过滤行以减少提取和比较的行数。
我必须将某些数据从一个数据库 (O) 迁移到另一个数据库 (D),其中一个问题是数据库的表在字段中具有不同的名称。我试图通过以下方式获取不在 D 数据库中的数据:
var dtOrigin = from o in genEntitCeres.X.AsNoTracking() select o;
var dtDestiny = from d in genEntitAgp.X.AsNoTracking() select d;
var rowsMatch = from tOrigin in dtOrigin.AsEnumerable()
join tDestiny in dtDestiny.AsEnumerable()
on new { tOrigin.a, tOrigin.b} equals
new { tDestiny.a, tDestiny.b}
select tOrigin;
var rowsNotMatch = (from tOrigin in dtOrigin.AsEnumerable()
where !rowsMatch.Contains(tOrigin)
select tOrigin);
当我尝试为每个或 rowsNotMatch.Count() 做一个时,它花费了太长时间...
我的解决方案是使用查询中的 SqlQuery 获取不匹配的行(5 秒以上,取决于数据不匹配的大小)。但我想知道 EF 是否还有其他方法,就像我的代码一样,但不会冻结。
尝试这样的事情:
var rowsNotMatch = from tOrigin in dtOrigin.AsEnumerable()
join tDestiny in dtDestiny.AsEnumerable()
on new { tOrigin.a, tOrigin.b} equals
new { tDestiny.a, tDestiny.b} into gD
from d in gD.DefaultIfEmpty()
where d == null
select tOrigin
这实质上是在 dtOrigin 和 dtDestiny 之间进行了左连接,仅返回 dtOrigin 中那些在 dtDestiny 中没有匹配项的项目。
这看起来像你在 2 个不同的 dbContexts 中打开 2 tables,想要根据每个 table 中的 2 列比较 2 tables,找到原始行table 没有匹配项。我不推荐使用 EF 来做这样的事情。在数据库 /w temp tables 中执行此操作会好得多。通过对每个 table 执行 .AsEnumerable(),您将把那些 table 的全部内容加载到内存中。
如果您必须在代码中做这样的事情:我会考虑这样的事情:
var destinationMatches = genEntitAgp.X.AsNoTracking()
.Select(x=> new { x.id, x.a, x.b })
.ToList();
如果目标 table 预计会很大,那么您应该使用 .Skip() 和 .Take() 在 1000 页左右的页面中执行此操作。
下一步是将所有行插入原始数据库中的缓冲区 table。缓冲区 table 保存目标行的 PK,以及要匹配的条件。
public class TempX
{
public int Id { get; set; }
public string a { get; set; }
public string b { get; set; }
}
我建议为此使用一个单独的 DBContext 而不是 Origin DBContext,以在没有更改跟踪的情况下进行初始化,并清除最初在 TempX table 中的所有记录。如果您要插入 > 1000 行,您希望以 1000 个为一组执行此操作,保存更改,然后处理并重新创建每个批次的上下文以保持操作快速。源 dbContext 也需要了解 TempX,只是不适用于总体。
一旦记录在源模式的 tempX table 中:
var entities = genEntitCeres.X.AsNoTracking()
.Where(x => !genEntitCeres.TempX.AsNoTracking().Any(tx=>tx.a == x.a && tx.b == x.b)).ToList();
同样,如果预计会有很多不匹配项,则不要使用 .ToList(),而是使用 .Skip() 和 .Take() 方法。
如果这可能在给定数据库上 运行 不止一次,那么我也会考虑希望有类似 CreatedAt/ModifiedAt dateTime 值的东西,我可以用它来过滤对于 Origin 和 Destination 数据库。 IE。记录最后一个 运行 DateTime,然后根据 > LastRunDate 从两个查询中过滤行以减少提取和比较的行数。