EF核心多对多插入:如何很好地避免EF尝试添加已经存在的唯一约束子实体?

问题描述 投票:0回答:1

晚上好:-)

关于EFCore多对多INSERT案例,我无法回答。

我有一个Recipe,其中有很多Tag。一个Tag(例如“晚餐”)可以在多个Recipe中。两者都有其SQL表。

为了进行多对多工作,我还具有一个联接的SQL表及其关联的对象RecipeTag

注意#1:班级代码在这篇文章的结尾

注#2:每个GET / SELECT操作都像一个超级按钮,实现了多对多并且有效

举个简单的例子,从新的空表开始,我发布新的Recipe

POST /recipes
{
  "title": "A first title",
  "tags": [
    {
      "name": "dinner"
    }
  ]
}

很好。我在Recipe表,Tag表和RecipeTag表中都链接了两个id

现在,我用same

Recipe:]重新注册一个新的Tag
POST /recipes
{
  "title": "A second title",
  "tags": [
    {
      "name": "dinner"
    }
  ]
}

在这里,我从EF Core获得了唯一键约束违规。它将尝试使用名称Tag注册一个新的dinner。因为我已经在Tagtagnam字段中添加了唯一的约束,所以异常实际上很正常。

Question:

是否有解决此问题的好方法?意思是,要让EF Core也注册第二个Recipe和加入的RecipeTag实体,就认识到Tag本身存在,不仅这次不是通过PK,而是通过唯一约束。

似乎(并且这是逻辑)如果EF Core看到id(PK)已存在于db中,则不会尝试添加实体。但在我的情况下:我也希望EF Core也检查唯一约束,如果发生冲突,则将db中有冲突的实体的id和我的Tag对象中的lazy-put-it都使用,以便它可以( )用正确的RecipeTag正确插入id加入实体!

对不起,这有点混乱...

我目前唯一的“不太糟糕”的解决方案:

  1. API控制器接收到RecipeDto
  2. 我们从这个新配方中将标签提取为string[]
  3. 我们从数据库中提取现有标签
  4. 匹配我们要添加的标签
  5. [将RecipeDto转换为Recipe时,字段ICollection<RecipeTag>将包含适当的Tag,其中id设置为现有对象,某些no-id Tag对象,对于我们的对象确实需要添加
  6. 如前所述,EF Core将识别出某些Tag填充了一个ID,并且不会尝试将其插入db-它只会插入不带id的那些[]
  7. 我使用的班级:

    /// <summary>
    /// Recipe that can contain many tags
    /// </summary>
    [Table("rcp")]
    public class Recipe
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Column("id")]
        public Guid Id { get; set; }
    
        [Column("rcptit")]
        public string Title { get; set; }
    
        public ICollection<RecipeTag> RecipeTags { get; set; }
    }
    
    /// <summary>
    /// Tag contained into many recipes
    /// </summary>
    [Table("tag")]
    public class Tag
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Column("id")]
        public Guid Id { get; set; }
    
        [Column("tagnam")] // this has a UNIQUE constraint in db to avoid having 2 times the same tag
        public string Name { get; set; }
    }
    
    /// <summary>
    /// Join table between Recipes and Tags
    /// </summary>
    [Table("rcptag")]
    public class RecipeTag
    {
        [Column("rcpid")]
        public Guid RecipeId { get; set; }
    
        [Column("tagid")]
        public Guid TagId { get; set; }
    
        public Recipe Recipe { get; set; }
        public Tag Tag { get; set; }
    }
    
    public class MyDbContext : DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
        { }
    
        public DbSet<Recipe> Recipes { get; set; }
        public DbSet<Tag> Tags { get; set; }
        public DbSet<RecipeTag> RecipeTags { get; set; }
    
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            builder.HasPostgresExtension("uuid-ossp");
    
            builder.Entity<Tag>().HasIndex(e => e.Name).IsUnique();
            // composite primary key for RecipeTag
            builder.Entity<RecipeTag>().HasKey(rt => new { rt.RecipeId, rt.TagId });
            builder.Entity<RecipeTag>().HasOne(rt => rt.Recipe).WithMany(r => r.RecipeTags).HasForeignKey(rt => rt.RecipeId);
        }
    }
    

[晚上好:-)关于EFCore的多对多INSERT案例,我有一个我无法回答的问题。我的食谱有很多标签。标签(例如“晚餐”)可以存在于许多配方中。两者...

<<<<
看来您缺少配置中“多对多”设置的一侧,这可能会导致问题。尝试如下切换设置。删除
builder.Entity<RecipeTag>().HasOne(rt => rt.Recipe).WithMany(r => r.RecipeTags).HasForeignKey(rt => rt.RecipeId);

然后添加

builder.Entity<RecipeTag>().HasOne(rt => rt.Tag).WithMany(r => r.RecipeTags).HasForeignKey(rt => rt.TagId).IsRequired();
builder.Entity<RecipeTag>().HasOne(rt => rt.Recipe).WithMany(r => r.RecipeTags).HasForeignKey(rt => rt.RecipeId).IsRequired();

这定义了组合键两侧的关系。

c# entity-framework-core many-to-many
1个回答
0
投票
© www.soinside.com 2019 - 2024. All rights reserved.