'C# EFCore.BulkExtensions The MERGE statement conflicted with the FOREIGN KEY constraint

Parent:

public class Currency
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public string? Name { get; set; }
}

Child:

public class CurrencyRate
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Column(TypeName = "date")]
    public DateTime Date { get; set; }

    [Column(TypeName = "decimal(18,6)")]
    public decimal Value { get; set; }

    public virtual Currency? Currency { get; set; }
}

Seed:

modelBuilder.Entity<Currency>().HasData(
    new Currency { Id = 1, Name = "JPY" },
    new Currency { Id = 2, Name = "USD" },
    new Currency { Id = 3, Name = "EUR" },
    new Currency { Id = 4, Name = "TRY" }
);

Insert:

public async Task UpdateCurrencyRates()
{
    var trackedCurrencies = await _context.Currency.ToListAsync();

    BulkConfig bulkConfig = new BulkConfig()
    {
        IncludeGraph = true,
        SetOutputIdentity = true,
        CalculateStats = true,
        PropertiesToIncludeOnUpdate = new List<string> { string.Empty }
    };
   
    List<CurrencyRate> currencyRates = GetCurrencyRatesFromSomewhere();

    currencyRates.ForEach(currencyRate =>
    {
        var trackedCurrency = trackedCurrencies.SingleOrDefault(r => r.Name == currencyRate.Currency?.Name);
        if (trackedCurrency != null)
        {
            currencyRate.Currency = trackedCurrency;
        }
        else if (currencyRate.Currency != null)
        {
            trackedCurrencies.Add(currencyRate.Currency);
        }
    });


    _context.BulkInsertOrUpdate(currencyRates, bulkConfig);
}

The above code works well unless new currency comes in (the one not included in Seed). Then I get 'The MERGE statement conflicted with the FOREIGN KEY constraint'. Why is it so? From what I read BulkExtensions should insert new currency first, then update CurrencyRates with its Id and only then perform insert of CurrencyRates.

Update: If I do this, it works, but that defeats the grace of the BulkInsert:

currencyRates.ForEach(async currencyRate =>
{
    var trackedCurrency = trackedCurrencies.SingleOrDefault(r => r.Name == currencyRate.Currency?.Name);
    if (trackedCurrency != null)
    {
        currencyRate.Currency = trackedCurrency;            
    }
    else if (currencyRate.Currency != null)
    {
        trackedCurrencies.Add(currencyRate.Currency);
        //this line added
        await _context.AddAsync(currencyRate.Currency);

    }
});

//this line added
await _context.SaveChangesAsync();




Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source