EF Core Каскадная ссылочная целостность с DeleteBehavior.Restrict не работает хорошо - PullRequest
1 голос
/ 27 февраля 2020

У меня есть одна sql база данных сервера, созданная с кодом в первую очередь. Есть две таблицы, которые имеют отношение один ко многим. База данных работает и создана хорошо. На сервере sql, если я пытаюсь удалить одну из записей классификации, я получаю ошибку (ограничение ссылочной целостности). Вот как я хочу, чтобы это работало. Но в основном, если я удаляю одну классификацию dbset.Remove(classification), классификация удаляется, и классификация у клиента устанавливается на null. Я думаю, что это должно работать для DeleteBehavior.ClientSetNull. В https://docs.microsoft.com/en-us/ef/core/saving/cascade-delete есть примечание « Изменения в EF Core 2.0 », которое объясняет функцию DeleteBehavior.

I have the next records:
Classification:
Id      Name
1       General
2       Others
Customers:
Id      Name        IdClassification
1       Customer A  1
2       Customer B  2
3       Customer C  <null>

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    ...
    public int? IdClassification { get; set; }
    public Classification Classification { get; set; }
}

public class Classification
{
    public int Id { get; set; }
    public string Name { get; set; }
    ...
    public ICollection<Customer> Customers { get; set; }
}

public class Context : DbContext
{
    public virtual DbSet<Classification> Classifications { get; set; }
    public virtual DbSet<Customer> Customers { get; set; }

    ...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Classification>(
        entity => 
        {
            entity.HasKey(e => e.Id);
        });

        modelBuilder.Entity<Customer>(
        entity =>
        {
            entity.HasKey(e => e.Id);
            entity.HasIndex(e => e.IdClassification);
            ...
            // Claves foráneas
            entity.HasOne(c => c.Classification)
                .WithMany(x => x.Customers)
                .HasForeignKey(x => x.IdClassification)
                .OnDelete(DeleteBehavior.Restrict)
                .HasConstraintName("FK_Customer_Classification");
        });
    }
}

Есть ли способ предотвратить удаление классификация записей в ядре? (Я не хочу проверять, есть ли какая-либо запись клиента, связанная с классификацией, потому что я должен использовать классификацию с большим количеством таблиц). Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 27 февраля 2020

EF Core 3.0 добавил несколько новых значений в перечисление DeleteBehavior - ClientCascade, NoAction, ClientNoAction. К сожалению, документация не обновляется (за исключением значений enum в справочнике по API), и только 3 ClientNoAction упомянуто в 3.0 Breaking Changes - DeleteBehavior.Restrict имеет более чистую семантику :

Старое поведение

До версии 3.0 DeleteBehavior.Restrict создавал внешние ключи в базе данных с семантикой Restrict, но также изменял внутреннюю фиксацию неочевидным способом.

Новое поведение

Начиная с 3.0, DeleteBehavior.Restrict обеспечивает создание внешних ключей с семантикой Restrict, то есть без каскадов; выбросить при нарушении ограничения - без влияния на внутреннюю фиксацию EF.

Почему

Это изменение было сделано для улучшения опыта использования DeleteBehavior в интуитивно понятном виде, без неожиданных побочных эффектов.

Смягчения

Предыдущее поведение можно восстановить с помощью DeleteBehavior.ClientNoAction.

Дополнительная информация содержится в связанной с этим проблеме отслеживания - 12661: обновите DeleteBehavior, чтобы он был более последовательным и понятным

Честно говоря, даже после прочтения всего этого я не нахожу это чище, но еще более запутанно. Restrict кажется устаревшим и заменен на NoAction, который независимо от того, что было сказано на самом деле устанавливает свойство навигации для загруженных связанных объектов / FK в null, таким образом вызывая поведение базы данных SET NULL как вы уже испытали.

После попытки всех из них, единственный вариант, который делает то, что вы ожидаете, это вышеупомянутый ClientNoAction :

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

Для объектов, отслеживаемых DbContext, значения свойств внешнего ключа в зависимых объектах не изменяются, когда связанный основной объект удален Это может привести к несогласованности графа сущностей, где значения свойств внешнего ключа не соответствуют отношениям в графе.

Если база данных была создана из модели с использованием Entity Framework Migrations или метода EnsureCreated() тогда поведение в базе данных - генерировать ошибку, если ограничение внешнего ключа нарушено.

независимо от их примечания в начале.


С учетом всего сказанного просто замените Restrict на ClientNoAction, и проблема будет решена. Миграция базы данных не требуется, поскольку это изменение влияет только на поведение клиента.

0 голосов
/ 27 февраля 2020

Что ж, объект классификации нуждается в корректной инициализации, предположим, для удаления правила ограничения.

modelBuilder.Entity<Classification>()
    .HasKey(e => e.Id)
    .HasMany(e => e.Customers)
    .WithOne(e => e.Classification)
    .OnDelete(DeleteBehavior.Restrict)
    .IsRequired(true);

Надеюсь, это поможет.

...