簡體   English   中英

實體框架根據復合鍵插入重復項

[英]Entity Framework inserting duplicates based on a composite key

我正在嘗試將大量對象列表插入到我的數據庫中(一次大約 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.......)我認為這是問題所在,但顯然不是經過幾次運行它仍在插入重復項。

任何和所有提示都會非常有幫助。 提前致謝!

復合鍵給這樣的操作增加了挑戰,但是我看到這種方法的真正問題是:

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

這會將數據庫中的所有 Metrics 記錄加載到 memory 中。 隨着表的增長,這在性能和資源使用方面將變得不可持續。

對於較大的批量操作,我的第一選擇可能是使用 EF,而是將流程脫機/后台處理。

在使用 EF 進行批量操作時,我使用的方法是首先將所有處理記錄導入到一個空的暫存表中,然后您可以從那里執行暫存表和真實數據之間的連接。 暫存記錄可以基於復合鍵聲明與度量行的關系。 這兩個實體(Metrics 和 StagingMetrics)在有界上下文中注冊,作為僅知道這兩個實體及其關系的上下文。

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

從那里我們可以將其轉換回度量標准。 (它們是相同的,但需要進行轉換,以便可以將其寫為 Metric)

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 或主應用程序 DbContext 添加新的 Metrics。 要考慮的最后一步是截斷登台表,這可以通過 SQL 命令完成:

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

我會考慮在第一次運行該過程時截斷表格。

這種方法的一個考慮因素是 StagingDbContext 應限定在此操作范圍內,以避免代碼在 Truncate 之后引用 StagingMetrics 表中的行的可能性。

因此,更完整的 Import 方法流程可能如下所示:

using (var stagingDbContext = StagingDbContextFactory.Create())
{
    stagingDbContext.Database.ExecuteSqlCommand("TRUNCATE TABLE StagingMetrics");        

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

    if (newMetrics.Any())
    {
        AppDbContext.Metrics.AddRange(newMetrics);
        AppDbContext.SaveChanges();
    }
    // Can truncate the Staging table here as well, or leave the contents to debug if there was an issue.
}

其中 StagingDbContextFactory 是工廠 class 以返回 DbContext 的新實例,而 AppDbContext 是注入的主應用程序 DbContext。

這不是一個可以零碎觸發的過程,例如 web 請求的一部分,因為多個調用可能會在前一個調用完成之前嘗試截斷登台表。 如果這可以作為上傳或來自用戶的類似請求的一部分執行,則使用后台工作人員將請求記錄到處理隊列,以確保一次只處理一個導入。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM