1
votes

Entity Framework Core: relation plusieurs-à-plusieurs avec le même objet

J'ai commencé à utiliser Entity Framework Core. J'ai une entité User :

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

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

Et j'ai aussi une entité Ingredient :

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; }
}

Comme vous pouvez le voir, je souhaite créer 2 tableaux qui rassemblent les ingrédients interdits et les ingrédients préférés pour chaque utilisateur. Que dois-je faire pour que cela fonctionne?


2 commentaires

Quelle est votre version d'Entity Framework Core?


@DoNhuVy Version 3.1.9


3 Réponses :


1
votes

La conception de votre relation de base de données était erronée. Vous devez ajouter plus d'un champ à Ingredient pour indiquer Forbidden ou Preferred (type tinyint ).


1 commentaires

Mais moi, pour un UTILISATEUR, l'INGRE est préféré, et pour l'autre c'est interdit.



1
votes

Vous devez normaliser votre DB. La première table conserve un utilisateur avec une clé primaire UserId, la deuxième table - Ingridient a IngridientId comme clé primaire et la troisième table UserIngridient conservera UserId et Ingridient Id comme clés étrangères. Tous les ingrédients sont les mêmes, seuls certains d'entre eux peuvent être interdits à l'utilisateur mais ce n'est peut-être pas interdit à un autre utilisateur. Vous avez donc besoin d'une quatrième table - Tapez. Mais comme vous n'avez que 2 types, vous pouvez utiliser un indicateur pour cela - Interdit ou Préféré. Cet indicateur doit être conservé dans la table UserIngridient car il dépend de l'utilisateur et de l'ingrédient de cet utilisateur.

Changez votre code comme suit:

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
}

Et vous devez AJOUTER une table supplémentaire pour la normalisation:

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; }
}


7 commentaires

L'utilisateur a les deux collections - PREF & FORB. Pourquoi renoncer à une collection?


Dans ce cas, vous aurez besoin de deux classes (et deux tables) PrefIngredient et ForbIngridient. Il est beaucoup plus facile d'ajouter simplement un drapeau. Et je mets que vous avez besoin d'une table supplémentaire UserIngridient pour la normalisation. Vous ne voulez pas répéter tous les ingrédients pour chaque utilisateur.


Réservoirs pour halp, j'en ai encore plus Q. Par cette solution, j'aurai 3 tables? Ce mot-clé virtual fera que EF enregistre le FK dans les tables de correspondance?


Vous devez normaliser votre db. La première table conserve un utilisateur avec une clé primaire UserId, la deuxième table - Ingridient a IngridientId comme clé primaire et la troisième table UserIngridient conservera UserId et Ingridient Id comme clés étrangères.


Merci! Votre solution m'aide!


Puisque vous n'êtes pas très à l'aise avec EF, je vous recommande vivement d'installer «EF Core Power Tools» dans votre Visual Studio. Vous pouvez créer votre base de données avec toutes les clés primaires et étrangères et après cela, vous pouvez utiliser ces outils pour générer votre EF code-first. C'est très utile pour apprendre à comparer votre DB et votre code EF. Vous apprendrez beaucoup.


Merci, je vais essayer



0
votes

Répondre à la question d'origine sur plusieurs clés étrangères à la même table. La logique est en cours de configuration dans DbContext.OnModelConfiguring via l'API Fluent.

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();
        }
    }
}


0 commentaires