首页 > 解决方案 > Entity Framework Core 在多对多上添加新条目时尝试添加旧条目

问题描述

我的EF Core 5.0.2代码优先中存在多对多关系:

public sealed class BlipPluginDto
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public string BlipPluginId { get; set; }

    [Required]
    public ICollection<SmartContactDto> SmartContacts { get; set; }
}
public sealed class SmartContactDto
{
    [Key]
    public int SmartContactId { get; set; }

    [Required]
    public string Identifier { get; set; }

    public ICollection<BlipPluginDto> BlipPlugins { get; set; }
}

关系在 DBContext( MarketplaceDb) 中配置为:

modelBuilder.Entity<BlipPluginDto>().HasMany(bp => bp.SmartContacts).WithMany(sc => sc.BlipPlugins);

然后我尝试将SmartContactDto(新的或现有的)记录添加到现有的BlipPluginDto

public async Task<SmartContactDto> GetOrCreateSmartContactAsync(
    string identifier,
    CancellationToken cancellationToken) =>
        await MarketplaceDb.SmartContacts
            .Include(sc => sc.BlipPlugins)
            .FirstOrDefaultAsync(sc => sc.Identifier == identifier, cancellationToken)
        ?? new SmartContactDto() { Identifier = identifier };

public async Task<bool> AddActivePluginAsync(
    string smartContactIdentifier,
    string blipPluginId,
    CancellationToken cancellationToken = default)
{
    var blipPlugin = await _blipPluginService.GetPluginByIdAsync(blipPluginId, cancellationToken);
    if (blipPlugin == default)
    {
        return false;
    }

    var smartContact = await GetOrCreateSmartContactAsync(smartContactIdentifier, cancellationToken);

    if (smartContact.BlipPlugins?.Any(bp => bp.BlipPluginId == blipPluginId) == true)
    {
        return false;
    }

    blipPlugin.SmartContacts.Add(smartContact);

    MarketplaceDb.Update(blipPlugin);

    await MarketplaceDb.SaveChangesAsync(cancellationToken);
    return true;
}

_blipPluginService.GetPluginByIdAsync: _

public async Task<BlipPluginDto> GetPluginByIdAsync(string id, CancellationToken cancellationToken = default)
{
    var plugin = await MarketplaceDb.BlipPlugins
        .Include(bp => bp.SmartContacts)
        .FirstOrDefaultAsync(bp => bp.BlipPluginId == id, cancellationToken);
    return plugin;
}

如果blipPlugin没有smartContact它可以正常工作( a 的第一个插入SmartContactDto),但是当blipPlugin已经有一个smartContact实体告诉我我正在尝试在 Join 表上添加一个具有相同 id 的项目时(由EF 核心)。

假设我正在尝试将一个SmartContactDtowith添加SmartContactId = 68到一个已经有一个的 withBlipPluginDto我得到以下内部异常:BlipPluginId = "foo"SmartContactDtoSmartContactId = 12

Violation of PRIMARY KEY constraint 'PK_BlipPluginDtoSmartContactDto'. Cannot insert duplicate key in object 'Marketplace.BlipPluginDtoSmartContactDto'. The duplicate key value is (foo, 12)

我尝试了许多不同的方法,但我总是坚持这一点,即使我做相反的事情(创建并保存一个新的smartContact然后添加blipPluginsmartContact.

标签: c#entity-frameworkentity-framework-coremany-to-many

解决方案


所有这些工作都需要在同一个 MarketplaceDb 上下文(工作单元)中完成。

我怀疑您的 GetOrCreateSmartContact 使用的 DbContext 与您在 _blipPlugInService 中检索原始插件的位置不同,然后您在最后对其进行更新。

因此,当您添加由 GetOrCreateSmartContact 找到的现有 SmartContact 时,它来自与您执行最终结果的上下文不同的上下文

blipPlugin.SmartContacts.Add(smartContact);

MarketplaceDb.Update(blipPlugin);

await MarketplaceDb.SaveChangesAsync(cancellationToken);

结果,这个最终上下文没有跟踪您检索到的 SmartContact,因此认为它是一个新上下文。

总之,当执行这样的一批操作时,您必须使用 DbContext for Entity Framework 的相同实例来发挥它的魔力。


推荐阅读