实体框架基于复合键插入重复项

时间:2021-01-11 21:34:17

标签: c# .net entity-framework linq linq-to-sql

我正在尝试将一个大型对象列表插入到我的数据库中(一次大约 30,000 条记录),并根据各列的复合键判断该记录是否为重复行。下面是使这更清楚一点的代码:

await {db_context}.AddRangeAsync(Metrics.Where(x =>!MetricsInDb.AsEnumerable().Any(y => 
x.CreativeId == y.CreativeId 
&& x.LineItemId == y.LineItemId 
&& x.Date.Date == y.Date.Date 
&& x.City == y.City 
&& x.Country == y.Country 
&& x.Metro == y.Metro 
&& x.State == y.State)));

为了进一步解释,我有两个列表。

Metrics 是我要插入的对象列表。
MetricsInDb 是我正在比较的另一个对象列表。
.Any() 中,基本上我想说的是,如果所有这些列都匹配,那么它是重复的。不要插入重复的行。

对我来说,逻辑似乎是合理的。我不确定在如此大的复合键上是否有更好的方法来做到这一点。

我最初在这里有一个 .AsParallel().... Metrics.AsParallel().Where(x => !MetricsInDb.......),我认为这是问题所在,我删除了它,显然运行几次后它仍然插入重复项。

任何和所有提示都会非常有帮助。提前致谢!

1 个答案:

答案 0 :(得分:2)

复合键给这样的操作带来了挑战,但是我看到这种方法的真正问题是:

4.10.1

这会将数据库中的所有指标记录加载到内存中。随着表的增长,这在性能和资源使用方面将变得不可持续。

对于较大的批量操作,我的第一选择可能是使用 EF,而是使流程脱机/后台运行。

谈到使用 EF 进行批量操作时,我使用的方法是首先将所有处理记录导入到一个空的临时表中,然后您可以在那里执行临时表和真实数据之间的连接。暂存记录可以根据组合键声明与 Metric 行的关系。这两个实体(Metrics 和 StagingMetrics)注册在一个有界上下文中,是一个仅知道这两个实体及其关系的上下文。

Metrics.Where(x =>!MetricsInDb.AsEnumerable().Any( ...

获取要添加的项目:

modelBuilder.Entity<StagingMetrics>()
    .HasOptional(x => x.Metric)
    .WithMany()
    .HasForeignKey(x => new 
    {
        x.CreativeId, 
        x.LineItemId, 
        x.Date.Date,
        x.City,
        x.Country,
        x.Metro,
        x.State
    });

从那里我们可以将其转换回公制。 (它们是相同的,但需要将其转换为度量标准)

var newMetrics = stagingDbContext.StagingMetrics.Where(x => x.Metric == null);

由此,我们可以通过 StagingDbContext 或主应用程序 DbContext 添加新的指标。要考虑的最后一步是截断可以由 SQL 命令完成的临时表:

var newMetrics = stagingDbContext.StagingMetrics.Where(x => x.Metric == null)
    .Select(x => new Metric
    {
        CreativeId = x.CreativeId, 
        LineItemId = x.LineItemId, 
        Date = x.Date,
        City = x.City,
        Country = x.Country,
        Metro = x.Metro,
        State = x.State
    }).ToList();

我会考虑在第一次运行进程时截断表格。

这种方法的一个考虑因素是 StagingDbContext 应限定在此操作的范围内,以避免代码在截断后引用 StagingMetrics 表中的行。

所以更完整的导入方法流程可能如下所示:

stagingDbContext.Database.ExecuteSqlCommand("TRUNCATE TABLE StagingMetrics");

其中 StagingDbContextFactory 是返回 DbContext 新实例的工厂类,AppDbContext 是注入的主应用程序 DbContext。

这不会是一个可以被逐次触发的过程,例如给定多个调用的 Web 请求的一部分可能会尝试在前一个调用完成之前截断临时表。如果这可以作为上传或来自用户的类似请求的一部分执行,则将请求记录到处理队列,并由后台工作人员确保一次只处理一个导入。