【问题标题】:Entity Framework Core: many-to-many relationship with the same objectEntity Framework Core:同一个对象的多对多关系
【发布时间】:2020-11-01 11:45:25
【问题描述】:

我已经开始使用 Entity Framework Core。我有一个User 实体:

public class UserEntity
{
    public UserEntity()
    {
        ForbiddenIngredients = new HashSet<Ingredient>();
        PreferredIngredients = new HashSet<Ingredient>();
    }

    [Key]
    public string ID { get; set; }

    public string Email { get; set; } //UserName
    public string FullName { get; set; }

    public virtual ICollection<Ingredient> ForbiddenIngredients { get; set; }
    public virtual ICollection<Ingredient> PreferredIngredients { get; set; }
}

我也有Ingredient实体:

public class Ingredient
{
    [Key]
    public string IngredientID { get; set; }

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

如您所见,我想为每个用户创建 2 个收集禁用成分首选成分的表。我应该怎么做才能让它发挥作用?

【问题讨论】:

  • 您的 Entity Framework Core 版本是什么?
  • @DoNhuVy 版本 3.1.9

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


【解决方案1】:

您必须标准化您的数据库。第一个表保留一个带有主键 UserId 的用户,第二个表 - Ingridient 将 IngridientId 作为主键,第三个表 UserIngridient 将保留 UserId 和 Ingridient Id 作为外键。所有的元素都是一样的,只有其中一些可能禁止用户使用,但可能不禁止其他用户使用。所以你需要第四个表 - 类型。但由于您只有 2 种类型,您可以为此使用标志 - 禁止或首选。该标志应保存在 UserIngridient 表中,因为它取决于用户和该用户的 ingridient。

将您的代码更改为:

public class User
{
    public User()
    {
        Ingredients = new HashSet<Ingredient>();
       
    }

    [Key]
    public string ID { get; set; }

    public string Email { get; set; } //UserName
    public string FullName { get; set; }

    public virtual ICollection<Ingredient> Ingredients { get; set; }
   
}

public class Ingredient
{
    [Key]
    public int ID { get; set; }

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

你必须再添加一张表进行规范化:

public class UserIngredient
{
    [Key]
    public int ID { get; set; }

     public int UserID { get; set; }
    public int IngredientID { get; set; }
    public int IngredientTypeId { get; set; }  // 0 or 1
   // OR   public string IngredientType { get; set; }   // Forb or Pref
.... add some virtual props  here
}

【讨论】:

  • 用户拥有两个集合 - PREF 和 FORB。为什么要放弃一个系列?
  • 在这种情况下,您将需要两个类(和两个表)PrefIngredient 和 ForbIngridient。添加标志要容易得多。我说你需要一个额外的表 UserIngridient 来进行规范化。您不想为每个用户重复所有内容。
  • 坦克停一下,我还有一些问题。通过这个解决方案,我将有 3 张桌子?这个virtual 关键字会导致EF 将FK 保存在匹配表中?
  • 你必须标准化你的数据库。第一个表保留一个带有主键 UserId 的用户,第二个表 - Ingridient 将 IngridientId 作为主键,第三个表 UserIngridient 将保留 UserId 和 Ingridient Id 作为外键。
  • 谢谢!你的解决方案帮助我!
【解决方案2】:

您的数据库关系设计错误。您应该在Ingredient 中添加更多字段以指出ForbiddenPreferredtinyint 类型)。

【讨论】:

  • 但是对于一个用户,INGRE 是首选,而对于另一个用户,它是被禁止的。
【解决方案3】:

回答关于同一张表的多个外键的原始问题。正在通过 Fluent API 在DbContext.OnModelConfiguring 中设置逻辑。

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace ConsoleApp3
{
    public class User
    {
        public User()
        {
            ForbiddenIngredients = new HashSet<Ingredient>();
            PreferredIngredients = new HashSet<Ingredient>();
        }

        [Key]
        public string ID { get; set; }

        public string Email { get; set; }
        public string FullName { get; set; }

        public virtual ICollection<Ingredient> ForbiddenIngredients { get; set; }
        public virtual ICollection<Ingredient> PreferredIngredients { get; set; }
    }

    public class Ingredient
    {
        [Key]
        public int IngredientID { get; set; }

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

        public virtual User ForbiddenUser { get; set; }

        public string ForbiddenUserId { get; set; }

        public virtual User PreferredUser { get; set; }

        public string PreferredUserId { get; set; }
    }

    public class AppDbContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        public DbSet<Ingredient> Ingredients { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("connection string");
            base.OnConfiguring(optionsBuilder);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                .HasMany(x => x.PreferredIngredients)
                .WithOne(x => x.PreferredUser)
                .HasForeignKey(x => x.PreferredUserId);

            modelBuilder.Entity<User>()
                .HasMany(x => x.ForbiddenIngredients)
                .WithOne(x => x.ForbiddenUser)
                .HasForeignKey(x => x.ForbiddenUserId);

            base.OnModelCreating(modelBuilder);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var user = new User()
            {
                Email = "test@test.com",
                ID = Guid.NewGuid().ToString(),
                FullName = "name",
            };

            user.ForbiddenIngredients.Add(new Ingredient()
            {
                ForbiddenUser = user,
                Name = "forbidden",
            });

            user.PreferredIngredients.Add(new Ingredient()
            {
                PreferredUser = user,
                Name = "preferred",
            });

            var dbContext = new AppDbContext();

            dbContext.Add(user);
            dbContext.SaveChanges();

            var dbUser = dbContext.Users
                .Include(x => x.ForbiddenIngredients)
                .Include(x => x.PreferredIngredients)
                .FirstOrDefault();
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多