【问题标题】:Insert new entity with EF Core with a manually assigned Id使用手动分配的 Id 插入带有 EF Core 的新实体
【发布时间】:2018-05-03 15:41:54
【问题描述】:

我需要将一个实体插入到对其 id 有约束的数据库中,所以我想手动设置它。

注意:下面的模型是了解我在做什么的最小示例。整个事情都在带有 EF Core 和 Pomelo MySql 提供程序的 ASP.NET Core 2 上运行。

我正在尝试在我已经存在的数据库 (MySQL) 中创建一个实体,其定义如下:

CREATE TABLE Users (
    id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(128) NOT NULL);

CREATE TABLE UserData (
    id INTEGER PRIMARY KEY,
    name VARCHAR(32) NOT NULL,
    FOREIGN KEY(id) REFERENCES Users(id));

所以Users 表中的每条记录在UserData 表中都有一条记录。 现在在我的代码中我想使用类似的东西

Context.Users.Add(user);
Context.SaveChanges();
data.Id = user.Id;
Context.UserData.Add(data);
Context.SaveChanges();

User 应该让他们的Id 由数据库生成,然后他们的UserData 用那个Id 创建。但是EF执行的语句却报错:

Failed executing DbCommand (6ms) [Parameters=[@p0='?', @p1='?' (Size = 32)], CommandType='Text', CommandTimeout='30']
  INSERT INTO `UserData` (`id`, `name`)
  VALUES (@p0, @p1);

MySql.Data.MySqlClient.MySqlException: Cannot add or update a child row:
  a foreign key constraint fails (`Main`.`UserData`, CONSTRAINT `UserData_ibfk_1`
  FOREIGN KEY (`id`) REFERENCES `Users` (`id`))

这意味着无论 Id 传递给 MySQL,它都不是 User 之一。

我什至尝试按照Entering keys manually with Entity Framework 中的建议将[DatabaseGenerated(DatabaseGeneratedOption.None)] 添加到UserData.Id,但我认为这更多的是关于CodeFirst 方法而不是已经运行的数据库。

从这个问题:EF Core Add function returns negative id 我了解到Context.Add() 修改了Id,但是在Add 之后分配我的值并没有改变任何东西。

如果它很重要,这是我的 DbContext 配置

modelBuilder.Entity<UserData>(entity =>
{
    entity.Property(e => e.Id)
        .HasColumnName("id")
        .HasColumnType("int(11)")
        .ValueGeneratedNever();

    entity.Property(e => e.Name)
        .IsRequired()
        .HasColumnName("name")
        .HasMaxLength(32);

    entity.HasOne(d => d.User)
        .WithOne(p => p.UserData)
        .HasForeignKey<UserData>(d => d.Id)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("UserData_ibfk_1");
}
modelBuilder.Entity<User>(entity =>
{
    entity.Property(e => e.Id)
        .HasColumnName("id")
        .HasColumnType("int(11)")
        .ValueGeneratedNever();

    entity.Property(e => e.Email)
        .IsRequired()
        .HasColumnName("email")
        .HasMaxLength(128);
}

【问题讨论】:

    标签: c# mysql entity-framework-core asp.net-core-2.0


    【解决方案1】:

    事实证明,在添加一个新实体并做之后

    Context.SaveChanges()
    

    新添加的对象没有更新。从数据库中进行新的拉取后

    Context.Users.First(u => u.Email == user.Email);
    

    返回的实体具有正确的 Id,分配给我的 UserData.Id 已允许正确添加到表中。

    【讨论】:

      【解决方案2】:

      User 应该得到他们的Id 由数据库生成

      不是根据你的流利配置:

      modelBuilder.Entity<User>(entity =>
      {
          entity.Property(e => e.Id)
              .HasColumnName("id")
              .HasColumnType("int(11)")
              .ValueGeneratedNever(); // <-- the problem
      }
      

      ValueGeneratedNever()换成ValueGeneratedOnAdd(),问题就解决了。

      附带说明,由于您似乎在UserUserData 中都有导航属性,您可以使用它们而不是FK 属性,从而无需中间SaveChanges 只需获取生成的@ 987654329@。例如,而不是:

      Context.Users.Add(user);
      Context.SaveChanges();
      data.Id = user.Id;
      Context.UserData.Add(data);
      Context.SaveChanges();
      

      你可以简单地使用:

      user.UserData = data;
      Context.Users.Add(user); // <-- will also add `UserData`
      Context.SaveChanges();
      

      EF 将以正确的顺序插入两条记录(首先是 User,然后是 UserData 以及从前一个插入生成的 Id)。

      【讨论】:

      • 好吧,我已经找到了解决方案(如下),并且该值由数据库生成,因为它具有自动增量。无论上下文配置中的内容是什么,如果我不是使用 EF 生成用于创建表的 SQL,而是使用现有数据库,则无关紧要。无论如何,感谢关于逆转的提示 - user.UserData = data
      • 是否从 EF 生成数据库都没有关系。重要的是将您的数据库表正确映射到您的班级。由于您在现有数据库表中有自动增量列,因此您需要让 EF 知道这一点,因此它会在插入 db 记录后检索Id自动更新对象。而不是像您自己回答那样手动这样做。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-29
      • 1970-01-01
      • 2022-10-18
      • 1970-01-01
      • 1970-01-01
      • 2018-12-05
      • 1970-01-01
      相关资源
      最近更新 更多