我有以下代码片段,几个月来我一直使用它来更新 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/