Общая таблица Entity Framework Core с каскадным удалением - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь создать следующий дизайн базы данных с EF Core (code-first)

  1. У сущности "Рецепт" может быть список типа "Ресурс"
  2. Сущность "Магазин" "может иметь один" Ресурс "
  3. Сущность" InstructionStep "может иметь список типа" Ресурс "

Если я удаляю ресурс из" Рецепта "," InstructionStep " (коллекции) или из «Магазина» (одно свойство), тогда соответствующая сущность «Ресурс» также должна быть удалена. (Каскадное удаление)

Я уже пробовал несколько вещей с таблицами отображения и без них, но ни один из моих подходов не увенчался успехом. Другая идея заключалась в том, чтобы иметь свойство «ItemRefId» в сущности «Ресурс» для сохранения «RecipeId / ShopId / InstructionStepId», но я не могу заставить его работать ...

Примеры классов:

    public class Recipe
    {
        public int RecipeId { get; set; }

        public string Title { get; set; }

        public ICollection<RecipeResource> Resources { get; set; } = new List<RecipeResource>();
    }

    public class Shop
    {
        public int ShopId { get; set; }
        public string Title { get; set; }
        public Resource Logo { get; set; }
    }

    public class Resource
    {
        public int ResourceId { get; set; }
        public string Path { get; set; }
        public int ItemRefId { get; set; }
    }

    public class InstructionStep
    {
        public string InstructionStepId { get; set; }
        public string Title { get; set; }
        public ICollection<RecipeResource> Resources { get; set; } = new List<RecipeResource>();
    }

Есть предложения? Большое спасибо заранее.

1 Ответ

1 голос
/ 07 апреля 2020

Это не каскадное удаление. Каскадное удаление может произойти при удалении рецепта, а также при удалении всех связанных ресурсов.

В EF Core 3 для этого можно использовать Типы собственных объектов . Сгенерированная реляционная модель отличается от предложенной вами тем, что Recipe_Resource и InstructionStep_Resource будут отдельными таблицами, а Shop.Lo go будет храниться в столбцах таблицы Shop. Но это правильная реляционная модель. Наличие одной таблицы ресурсов с несколькими строками, ссылающимися на рецепт, и несколькими строками, ссылающимися на InstructionStep, является плохой идеей.

Этот сценарий иногда называют «сильными отношениями», где идентичность связанного объекта зависит от основного объекта и должен быть реализован в реляционной модели, чтобы столбцы внешнего ключа были столбцами первичного ключа зависимого объекта. Таким образом, невозможно удалить Recipe_Resource без его удаления.

например

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;

namespace EfCore3Test
{

    public class Recipe
    {
        public int RecipeId { get; set; }

        public string Title { get; set; }

        public ICollection<Resource> Resources { get; } = new List<Resource>();
    }

    public class Shop
    {
        public int ShopId { get; set; }
        public string Title { get; set; }
        public Resource Logo { get; set; }
    }

    public class Resource
    {
        public int ResourceId { get; set; }
        public string Path { get; set; }
        public int ItemRefId { get; set; }
    }

    public class InstructionStep
    {
        public string InstructionStepId { get; set; }
        public string Title { get; set; }
        public ICollection<Resource> Resources { get; } = new List<Resource>();
    }

    public class Db : DbContext
    {
        public DbSet<Recipe> Recipes { get; set; }
        public virtual DbSet<Shop> Shops { get; set; }
        public virtual DbSet<InstructionStep> InstructionSteps { get; set; }

        private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
            {
                builder.AddFilter((category, level) =>
                   category == DbLoggerCategory.Database.Command.Name
                   && level == LogLevel.Information).AddConsole();
            });
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseLoggerFactory(loggerFactory)
                          .UseSqlServer("Server=.;database=EfCore3Test;Integrated Security=true", 
                                        o => o.UseRelationalNulls());

            base.OnConfiguring(optionsBuilder);
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Shop>().OwnsOne(p => p.Logo);
            modelBuilder.Entity<InstructionStep>().OwnsMany(p => p.Resources);
            modelBuilder.Entity<Recipe>().OwnsMany(p => p.Resources);
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            using var db = new Db();

            db.Database.EnsureDeleted();
            db.Database.EnsureCreated();

            var r = new Recipe();
            r.Resources.Add(new Resource() { ItemRefId = 2, Path = "/" });

            db.Recipes.Add(r);
            db.SaveChanges();

            r.Resources.Remove(r.Resources.First());

            db.SaveChanges();

            var s = new Shop();
            s.Logo = new Resource { ItemRefId = 2, Path = "/" };
            db.Shops.Add(s);
            db.SaveChanges();

            s.Logo = null;
            db.SaveChanges();
        }
    }
}
...