Ограничение ССЫЛКИ на конфликт инструкций УДАЛЕНИЯ в db, EF

#c# #sql #entity-framework

#c# #sql #entity-framework

Вопрос:

Первый вопрос: Я столкнулся с проблемой, возникшей при попытке обновить базу данных:

УДАЛИТЬ ограничение ССЫЛКИ на конфликт инструкций «FK_Products_ProductTypes_ProductTypeId». Произошел конфликт в базе данных «GdmStore», таблица «dbo.Продукты», столбец ‘ProductTypeId’.

Второй вопрос: хороша ли моя объектная модель и взаимосвязь между объектами? Я должен создать базу данных, в которой у меня есть много разных «продуктов», и каждый «продукт» имеет свой собственный набор «параметров».

Мой DataContext:

 public DataContext(DbContextOptions<DataContext> opts) : base(opts) { }

public DbSet<Product> Products { get; set; }
public DbSet<ProductType> ProductTypes { get; set; }
public DbSet<Parameter> Parameters { get; set; }
public DbSet<ProductParameter> ProductParameters { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
        modelBuilder.Entity<Product>()
           .HasOne(c => c.ProductType)
           .WithMany(e => e.Products)
           .HasForeignKey(c => c.ProductTypeId)
           .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<Parameter>()
           .HasOne(Pr => Pr.ProductType)
           .WithMany(Pt => Pt.Parameters)
           .HasForeignKey(Pr => Pr.ProductTypeId)
           .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<ProductParameter>()
            .HasOne<Product>(bc => bc.Product)
            .WithMany(b => b.ProductParameters)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<ProductParameter>()
            .HasOne<Parameter>(bc => bc.Parameter)
            .WithMany(c => c.ProductParameters)
            .OnDelete(DeleteBehavior.Restrict);
}
  

Это мои классы моделей:

 public class Product //: BaseObject
{
    public int ProductId { get; set; }

    public string Number { get; set; }
    public double Amount { get; set; }
    public double PrimeCost { get; set; }

    [ForeignKey("ProductTypeId")]
    public int  ProductTypeId {  get; set; }
    public ProductType ProductType { get; set; }

    public int ProductParameterId { get; set; }
    public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}

public class ProductType 
{   
    public int ProductTypeId { get; set; }
    public string TypeName { get; set; }

    public ICollection<Product> Products { get; set; }
    public ICollection<Parameter> Parameters { get; set; }

    public ICollection<Parameter> Parameter { get; set; } = new List<Parameter>();
    public ICollection<Product> Product { get; set; } = new List<Product>();
}

public class Parameter
{
    public int ParameterId { get; set; }
    public string Name { get; set; }

    [ForeignKey("ProductTypeId")]
    public int ProductTypeId { get; set; }
    public ProductType ProductType { get; set; }

    public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}

public class ProductParameter 
{
    public int ProductParameterId { get; set; }

    public int ProductId { get; set; }
    public int ParameterId { get; set; }

    public Parameter Parameter { get; set; }
    public Product Product { get; set; }

    public string Value { get; set; }
}
  

Комментарии:

1. По одному четкому вопросу за раз.

2. Добавьте операцию, которую вы пытаетесь выполнить при получении исключения. Похоже, вы пытаетесь удалить тип продукта, в то время как продукт все еще ссылается на него. Исходя из общего первого наблюдения, я бы посоветовал удалить любые двунаправленные ссылки, которые вам не нужны. Например, действительно ли ProductType нужен список продуктов? (Вы всегда можете запросить это со стороны продукта с помощью context.Products.Where(x => x.ProductType.ProductTypeId == productTypeId)

Ответ №1:

Судя по сообщению об ошибке, похоже, что вы пытаетесь удалить строку, от которой зависит другая строка.

Например, посмотрите на следующие таблицы:

 --------------------------------------
         PRODUCT_TYPE
--------------------------------------
(PK)PRODUCT_TYPE_ID  |  TYPE
--------------------------------------
1000                 | Gimmick
1001                 | Life Changing



--------------------------------------------------------
                     PRODUCT
--------------------------------------------------------
(PK)PRODUCT_ID | (FK)PRD_TYPE_ID  | Name
--------------------------------------------------------
6521           | 1000             | Electric Pizza Cutter
6522           | 1001             | Bidet
  

Если бы я попытался запустить DELETE FROM PRODUCT_TYPE WHERE PRODUCT_TYPE_ID = 1000 , это завершилось бы неудачей. Почему? Что ж, если я удалю этот тип продукта, то у моего продукта Electric Pizza Cutter будет тип продукта mystery (больше нет 1000). Оно потеряно. Базы данных не похожи на Daddy Warbucks — они ненавидят сирот.

Если вам действительно нужно что-то удалить, вам нужно разобрать это с нижней части цепочки до самого верха. Но вы обнаружите, что очень редко что-либо когда-либо должно быть удалено в базе данных. Вместо удаления вы должны пометить его как неактивный. Мой предпочтительный маршрут использует конечную дату.

 --------------------------------------------------
         PRODUCT_TYPE
--------------------------------------------------
(PK)PRODUCT_TYPE_ID  |  TYPE          |  END_DATE
--------------------------------------------------
       1000          | Gimmick        | 3-25-2019
       1001          | Life Changing  | null
  

Точно так же мой тип продукта больше не используется, но старые вещи в DB все еще могут ссылаться на них, если это необходимо.