cascade delete related tables in Entity Framework

160
04 ноября 2018, 03:20

Модель связных таблиц

модель City

public class City
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public Country Country { get; set; }
}

модель Country

public class Country 
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
}

Модель Advertisement

public class Advertisement
{
    public int Id { get; set; }
    [Required(ErrorMessage ="incorrect information")]
    public string Title { get; set; }
    [Required]
    public string Description { get; set; }
    public string Photo { get; set; }
    public Country Country { get; set; }
    public City City { get; set; }
}

Контекст

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<City>()
        .HasOne(p => p.Country)
        .WithMany()
        .OnDelete(DeleteBehavior.SetNull);
    modelBuilder.Entity<Advertisement>()
        .HasOne(p => p.Country)
        .WithMany()
        .OnDelete(DeleteBehavior.SetNull);
}

Контекст позволяет при удалении Country set NULL в в поля CountryId в таблицы Countries и Advertisements

Вопрос: Необходимо при удаление поля из Countries удалять связные Cities и SetNuLL в поля CityId и CountryID в таблицу Advertisement

Answer 1

У вас отношение один-ко-многим оформлено не полностью:

public class Advertisement
{
    public int Id { get; set; }
    [Required(ErrorMessage ="incorrect information")]
    public string Title { get; set; }
    [Required]
    public string Description { get; set; }
    public string Photo { get; set; }
    public int CountryId { get; set; }
    public Country Country { get; set; }
    public int CityId { get; set; }
    public City City { get; set; }
}

У одного города может быть много адверток:

public class City
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public int CountryId { get; set; }
    public Country Country { get; set; }
    public virtual ICollection<Advertisement> Advertisements { get; set; }
}

У одной страны может быть много адверток и много городов:

public class Country 
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public virtual ICollection<Advertisement> Advertisements { get; set; }
    public virtual ICollection<City> CountryCities { get; set; }
}

И вот когда вы выпишите эти связи - можно будет переходить и более подробно указывать связи и действия при удалении.

Сравните то, что у вас и что у меня:

builder.Entity<City>()
            .HasOne(p => p.Country)
            .WithMany(p => p.CountryCities)
            .HasForeignKey(x => x.CountryId)
            .OnDelete(DeleteBehavior.Cascade);
builder.Entity<Advertisement>()
            .HasOne(p => p.Country)
            .WithMany(p => p.Advertisements)
            .HasForeignKey(x => x.CountryId)
            .OnDelete(DeleteBehavior.SetNull);
builder.Entity<Advertisement>()
            .HasOne(p => p.City)
            .WithMany(p => p.Advertisements)
            .HasForeignKey(x => x.CityId)
            .OnDelete(DeleteBehavior.SetNull);

Документация по теме:

  • MSDN. Каскадное удаление

Как это вообще читается. Запомним термины:

  • principal/parent entity (основной или родительской сущность)
  • dependent/child entity (зависимая или дочерняя сущность)

В связи один-ко-многим principal/основной сущностью является та, которая "одна", а dependent/зависимой - та, которых "много":

  • у одной страны много городов: при удалении страны нужно что-то сделать с городами которые находятся в этой стране
  • в одном городе много рекламных объявлений: при удалении города нужно что-то сделать с обьявлениями, которые относились к этому городу
  • в одной стране много рекламных объявлений: при удалении страны нужно что-то сделать с обьявлениями, которые относились к этой стране

Когда вы ставите точку после modelBuidler вы указываете всегда основную/родительскую сущность:

 builder.Entity<City>()
        .HasOne(p => p.Country)
        .WithMany(p => p.CountryCities)
        .HasForeignKey(x => x.CountryId)
        .OnDelete(DeleteBehavior.Cascade);

То есть:

Сущность Город
Находится в одной конкретной Стране
У которой много Городов
При удалении основной сущности (Город)
Произвести удаление дочерних сущностей (Страна) по внешнему ключу ИдГород

В качестве примера (ваш посложнее будет) возьмите из документации (ссылка выше) пример Blog < -- > Post, код этого примера лежит на гитхабе:

protected override void OnModelCreating(ModelBuilder modelBuilder)
    => modelBuilder
    .Entity<Blog>()
    .HasMany(e => e.Posts)
    .WithOne(e => e.Blog)
    .OnDelete(DeleteBehavior)
    .IsRequired(RequiredRelationship);

Пример удаления блога:

var blog = context.Blogs.Include(b => b.Posts).First();
var posts = blog.Posts.ToList();
DumpEntities("  After loading entities:", context, blog, posts);
context.Remove(blog);
DumpEntities($"  After deleting blog '{blog.BlogId}':", context, blog, posts);
try
{
    Console.WriteLine();
    Console.WriteLine("  Saving changes:");
    context.SaveChanges();
    DumpSql();
    DumpEntities("  After SaveChanges:", context, blog, posts);
}
catch (Exception e)
{
    DumpSql();
    Console.WriteLine();
    Console.WriteLine($"  SaveChanges threw {e.GetType().Name}: {(e is DbUpdateException ? e.InnerException.Message : e.Message)}");
}

Пошаговые действия:

After loading entities:
    Blog '1' is in state Unchanged with 2 posts referenced.
      Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
      Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
  After deleting blog '1':
    Blog '1' is in state Deleted with 2 posts referenced.
      Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
      Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
  Saving changes:
    DELETE FROM [Posts] WHERE [PostId] = 1
    DELETE FROM [Posts] WHERE [PostId] = 2
    DELETE FROM [Blogs] WHERE [BlogId] = 1
  After SaveChanges:
    Blog '1' is in state Detached with 2 posts referenced.
      Post '1' is in state Detached with FK '1' and no reference to a blog.
      Post '1' is in state Detached with FK '1' and no reference to a blog.

И ещё. У вас структура базы не находится в третьей нормальной форме (3NF), у вас избыточная структура: на Advertisement достаточно указать City, но не указывать Country -- потому что зная City можно сделать .Include к Country.

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

READ ALSO
В чем проблема с установкой browscap?

В чем проблема с установкой browscap?

Я не смог себе поставить browscap на php72 серевер nginx browscap

153
Нужна помощь с php кодом в базе данных SQL [закрыт]

Нужна помощь с php кодом в базе данных SQL [закрыт]

Задача такая есть база данных SQL для которой нужно написать php код который будит "определять товар, количество которого больше всего на складе,...

159
Разбить HTML на элементы в массиве

Разбить HTML на элементы в массиве

Делаю парсер информации с одного сайтаНужную информацию спарсил, но теперь не могу разбить эту информацию на элементы в массиве

181