c# - EntityFramework Core 1.1.1 - 用新值和覆盖值替换旧集合

标签 c# asp.net-core entity-framework-core

我有以下代码片段,几个月来我一直使用它来更新 Client 的数据模型,并通过 ViewModel 传回值。

我正在使用 LINQ 来过滤从客户端传递的电子邮件 CC 值的集合(这是一个字符串列表),并使用指定的电子邮件地址创建 EmailCC 对象的新实例,然后将其分配给客户端 EmailCC,这是一对多关系映射。我期望的正确行为是打破之前输入的电子邮件抄送之间的关系,然后使用新输入的值。

更新到最新版本的 EF Core 后,我在尝试更新时收到以下异常,我相信我理解这是由于在同一上下文中删除和修改 EmailCCs 集合造成的。

InvalidOperationException: The instance of entity type 'EmailCC' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.

我的 EmailCC 主键列是数据库生成的。

Post方法代码是

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(
    [Bind("Id,FirstName,LastName,Email,EmailCc")] ClientEditViewModel clientEditViewModel)
{
    if (!ModelState.IsValid) return View(clientEditViewModel);

    Client client =
        _dbContext.Client.Include(p => p.EmailCCs).SingleOrDefault(p => p.Id == clientEditViewModel.Id);

    client.FirstName = clientEditViewModel.FirstName;
    client.LastName = clientEditViewModel.LastName;
    client.Email = clientEditViewModel.Email;
    client.EmailCCs =
        clientEditViewModel.EmailCc.Where(p => !string.IsNullOrWhiteSpace(p))
            .Select(p => new EmailCC { Email = p }).ToList();

    await _dbContext.SaveChangesAsync();
    return RedirectToAction(nameof(Edit), new { id = client.Id });
}

EmailCC 类是

public class EmailCC
{
    [Column("ID")]
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public int Id { get; set; }
    public string Email { get; set; }
}

客户端类:

public class Client
{
    [Column("ID")]
    public int Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Email { get; set; }

    public virtual ICollection<EmailCC> EmailCCs { get; set; }
}

我是否以错误的方式处理这个问题?如果是,那么替换集合的正确方法是什么?只要我不关心该集合中的值,只要它们被新值覆盖即可?

最佳答案

从模型创建并应用迁移会生成以下 SQL 命令:

CREATE TABLE [EmailCCs] (
    [ID] int NOT NULL,
    [ClientId] int,
    [Email] nvarchar(max),
    CONSTRAINT [PK_EmailCCs] PRIMARY KEY ([ID]),
    CONSTRAINT [FK_EmailCCs_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([ID]) ON DELETE NO ACTION
);

您可以在这里注意到几个问题。 PK 不是IDENTITY,因此不是由数据库生成。您应该删除 [DatabaseGenerate(DatabaseGeneeratedOption.Compulated)] 注释或使用 [DatabaseGenerate(DatabaseGenerateOption.Identity)]

第二个问题是 ClientId 列可为空,并且删除级联已关闭(ON DELETE NO ACTION)。为了使替换代码正常工作,您需要配置删除级联所需的关系。

这是固定模型:

public class EmailCC
{
    [Column("ID")]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Email { get; set; }
}

和流畅的配置:

modelBuilder.Entity<Client>()
    .HasMany(e => e.EmailCCs)
    .WithOne()
    .IsRequired()
    .OnDelete(DeleteBehavior.Cascade);

现在创建了一个不同的命令:

CREATE TABLE [EmailCCs] (
    [ID] int NOT NULL IDENTITY,
    [ClientId] int NOT NULL,
    [Email] nvarchar(max),
    CONSTRAINT [PK_EmailCCs] PRIMARY KEY ([ID]),
    CONSTRAINT [FK_EmailCCs_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([ID]) ON DELETE CASCADE
); 

更重要的是,相关代码无需任何修改即可按预期工作(也不异常(exception),旧的 EmailCCs 集合将替换为新值)。

关于c# - EntityFramework Core 1.1.1 - 用新值和覆盖值替换旧集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43833794/

相关文章:

c# - 一对多关系在 EF 中不起作用

.net - 我可以使用仅面向 .NET 4.6.1 的 ASP.NET Core 吗?

asp.net - 如何在 Entity Framework 核心中调用标量函数

c# - 如何在 MVC Core 中使用缓存管理器?

c# - 在不删除数据的情况下重命名 Entity Framework Core 中的外键

c# - 在 .NET/C# 上下文中,什么是二分搜索以及如何/为什么使用二分搜索?

c# - 如何在 Web 应用程序(ASP.Net、C#、IIS)中上传文件

c# - Excel 文件上传在 Chrome 中有效,在 IE 8 中无效。错误 : 'could not find the part of the path'

asp.net-core - 如何绕过 .NET Core 2.2 和 3.1 错误处理并显示标准 IIS 状态代码页?

c# - Microsoft.IdentityModel.Tokens 中的参与者 token 是什么?