[英]Entity Framework inserting record with composite key not inserting second field
[英]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.