c# EF core Удаление главной сушности и всех связанных с ней

Доброго времени суток Есть модель каталога

namespace Market.Model.CatalogFolder
{
    public class Category
    {
        public int CategoryId { get; set; }
        /// <summary>
        ///Название категории 
        /// </summary>
        public string? Name { get; set; } = null!;
        /// <summary>
        ///Товар в категории 
        public List<Product> Product { get; set; } = new();
        public List<Sub_Category> Sub_Category { get; set; } = new();
    }
}

И модель товара

namespace Market.Model.CatalogFolder
{
    public class Product
    {
        public int ProductId { get; set; }
        /// <summary>
        ///Название товара 
        /// </summary>
        public string? Name { get; set; } = null!;
        /// <summary>
        ///Цена товара 
        /// </summary>
        public double Price { get; set; }
        /// <summary>
        ///Описание товара 
        /// </summary>
        public string? Description { get; set; } = null!;
        /// <summary>
        ///Инстуркция использования товара 
        /// </summary>
        public string? Instruction { get; set; } = null!;
        /// <summary>
        ///Тип файла txt 
        /// </summary>
        public bool TypeTxt { get; set; } = false;
        /// <summary>
        ///Тип файла => цельный файл 
        /// </summary>
        public bool TypeFile { get; set; } = false;
        public List<Catalog?> Catalog { get; set; } = new List<Catalog?>();
        public List<Category?> Category { get; set; } = new List<Category?>();
        public List<Sub_Category?> Sub_Category { get; set; } = new List<Sub_Category?>();
        public List<Product_File> Product_File { get; set; } = new();
    }
}

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

var category = db.Category.Include(x => x.Name == "Категория").ToList();
 db.RemoveRange(category);
 db.SaveChanges();

Этим кодом я могу удалить что хочу, но тогда присутствие ключей в моделях, не много теряют смысл

var categories = db.Category.Include(x => x.Product)
.Where(x => x.Name == Name).ToList();
var products = db.Product.Include(x => x.Category).Where(x => x.Category
.Any(x => x.Name == Name)).ToList();
                    if(categories.Count() > 0)
                    db.RemoveRange(categories);
                    if(products.Count() > 0)
                    db.RemoveRange(products);
                    db.SaveChanges();






using Microsoft.EntityFrameworkCore;
using VIPMarket.Model.AdminModel;
using VIPMarket.Model.CatalogFolder;
using VIPMarket.Model.UserModel.BuyProductFolder;
using VIPMarket.Model.UserModel.Referal;

namespace Market
{
    public class DataContext : DbContext
    {
        public DbSet<Model.AdminModel.Admin> Admin { get; set; } = null!;
        public DbSet<Model.UserModel.User> User { get; set; } = null!;
        public DbSet<Referal_LVL1> Referal_LVL1 { get; set; } = null!;
        public DbSet<Referal_LVL2> Referal_LVL2 { get; set; } = null!;
        public DbSet<Master> Master { get; set; } = null!;
        public DbSet<Statistics> Statistics { get; set; } = null!;
        public DbSet<PromoDB> PromoDB { get; set; } = null!;
        public DbSet<BuyProduct> BuyProduct { get; set; } = null!;
        public DbSet<ConfigBot> ConfigBot { get; set; } = null!;

        public DbSet<Category> Category { get; set; } = null!;
        public DbSet<Catalog> Catalog { get; set; } = null!;
        public DbSet<Sub_Category> Sub_Category { get; set; } = null!;
        public DbSet<Product> Product { get; set; } = null!;
        public DbSet<Product_File> Product_File { get; set; } = null!;

        protected readonly IConfiguration Configuration;

        public DataContext(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlite(Configuration.GetConnectionString("DB"));
        }
    }
}

Ответы (2 шт):

Автор решения: Andrei Brizhak

Вам нужно создать в товарах внешний ключ с указанием на Id Категории, что бы средствами EF Core, в базе данных была связана связь "один ко многим". Попробуйте изменить описание моделей Товаров и Категорий, а затем создайте миграцию и проконтролируйте что в ней явно указан признак каскадного удаления.

  1. Модель Товара

    • Удалите сначала эти свойства из модели Товара:

    public List<Catalog?> Catalog { get; set; } = new List<Catalog?>();

    public List<Category?> Category { get; set; } = new List<Category?>();

    public List<Sub_Category?> Sub_Category { get; set; } = new List<Sub_Category?>();

    public List<Product_File> Product_File { get; set; } = new();

    • И попробуйте, сначала создать внешний ключ только для категории:

[ForeingKey("Category")]

public int CategoryId {get; set;}

public Category Category{get; set;}

  1. Удалите вручную все существующие записи в базе данных в этих двух таблицах, что бы не было проблем с миграцией

  2. В модели Category удалите вот эти свойства:

    public List Product { get; set; } = new();

     public List<Sub_Category> Sub_Category { get; set; } = new();
    
  3. Создайте новую миграцию

  4. Проверьте код миграции, в которой должны быть строки с созданием внешнего ключа в таблице с Товарами, но не Категориями. А также при добавлении этого внешнего ключа, должна быть сгенерирована строка по умолчанию, что то типа... onDelete: ReferentialAction.Cascade; Таким образом при удалении Категории, будут также удаляться все Товары, со ссылкой на эту категорию. И здесь можно вручную изменить опции для удаления, так как такая миграция еще не передавалась в базу данных.

  5. Попробуйте обновить базу через команду update-data

  6. Если миграция прошла успешно, то проверьте результат в базе данных.

  7. По аналогии добавляйте внешние ключи со ссылками на субкатегории и так далее..

Учебный пример с созданием Категорий и Товаров для интернет магазина на ASP.NET MVC можно посмотреть на видео.

→ Ссылка
Автор решения: xellan

Код который решил проблему

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF_Core_Test
{
    public class Catalog
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public ICollection<Category> Category { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF_Core_Test
{
    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int CatalogId { get; set; }
        public Catalog Catalog { get; set; }
        public ICollection<Product> Product { get; set; }
        public ICollection<Sub_Category>? Sub_Category { get; set; }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF_Core_Test
{
    public class Sub_Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }
        public ICollection<Product> Product { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF_Core_Test
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        [ForeignKey("Category")]
        public int CategoryId { get; set; }
        public Category Category { get; set; }
        [ForeignKey("Catalog")]
        public int CatalogId { get; set; }
        public Catalog Catalog { get; set; }
        [ForeignKey("Sub_Category")]
        public int? Sub_CategoryId { get; set; }
        public Sub_Category? Sub_Category { get; set; } //Если свойство может иметь null значение, то Sub_CategoryId тоже должен принимать null значение
    }
}

using Microsoft.EntityFrameworkCore;

namespace EF_Core_Test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (DataContext db = new DataContext())
            {
                db.Database.EnsureCreated();
                lock (this)
                    db.SaveChanges();
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            using (DataContext db = new DataContext())
            {
                Catalog catalog = new Catalog()
                {
                    Name = "Каталог"
                };
                db.AddRange(catalog);
                Category category = new()
                {
                    Name = "Категория",
                    Catalog = catalog
                };
                db.AddRange(category);
                Sub_Category sub_Category = new()
                {
                    Name = "Под категория Exist",
                    Category = category
                };
                Product product = new()
                {
                    Name = "Товар",
                    Catalog = catalog,
                    Category = category,
                    Sub_Category = sub_Category
                };
                db.Product.AddRange(product);
                lock (this)
                    db.SaveChanges();
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            using(DataContext db = new DataContext())
            {
                db.Remove(db.Catalog.FirstOrDefault());
                db.SaveChanges();
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            using (DataContext db = new DataContext())
            {
                Product product = new()
                {
                    Name = "Товар",
                    Catalog = db.Catalog.FirstOrDefault(),
                    Category = db.Category.FirstOrDefault(),
                };
                db.Product.AddRange(product);
                db.SaveChanges();
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            using (DataContext db = new DataContext())
            {
                //Удаляем все категорию и все связанные с ней сушности
                db.Remove(db.Product.OrderBy(x => x.Category).Include(x => x.Category).LastOrDefault());
                db.SaveChanges();
            }
        }

        private void button6_Click(object sender, EventArgs e)
        {
            ///Создаем под категорию и товар в ней
            using (DataContext db = new DataContext())
            {
                
                Sub_Category sub_Category = new()
                {
                    Name = "Под категория",
                    Category = db.Category.FirstOrDefault()
                };
                Product product = new()
                {
                    Name = "Товар sub_Category",
                    Catalog = db.Catalog.FirstOrDefault(),
                    Category = db.Category.FirstOrDefault(),
                    Sub_Category = sub_Category
                };
                db.Product.AddRange(product);
                db.SaveChanges();
            }
        }

        private void button7_Click(object sender, EventArgs e)
        {
            using (DataContext db = new DataContext())
            {
                //Выводим количество товар под категории 
                MessageBox.Show(db.Product.Include(x => x.Sub_Category).Where(x => x.Sub_Category.Name == "Под категория").ToList().Count().ToString());
            }
        }
    }
}
→ Ссылка