Подтвердить что ты не робот

EntityFramework: Как настроить Cascade-Delete для аннулирования внешних ключей

Документация EntityFramework утверждает, что возможно следующее поведение:

Если внешний ключ зависимого объекта является нулевым, Code First делает не устанавливать каскадное удаление в отношении, а когда основной удаленный внешний ключ будет установлен на нуль.

(из http://msdn.microsoft.com/en-us/jj591620)

Однако я не могу добиться такого поведения.

У меня есть следующие Entities, определенные с помощью code-first:

public class TestMaster
{
    public int Id { get; set; }
    public string Name { get; set; }        
    public virtual ICollection<TestChild> Children { get; set; }       
}

public class TestChild
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual TestMaster Master { get; set; }
    public int? MasterId { get; set; }
}

Вот конфигурация отображения Fluent API:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<TestMaster>()
                    .HasMany(e => e.Children)
                    .WithOptional(p => p.Master).WillCascadeOnDelete(false);

        modelBuilder.Entity<TestChild>()
                    .HasOptional(e => e.Master)
                    .WithMany(e => e.Children)
                    .HasForeignKey(e => e.MasterId).WillCascadeOnDelete(false);
    }

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

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

 using (var dbContext = new TestContext())
        {
            var master = dbContext.Set<TestMaster>().Find(1);
            dbContext.Set<TestMaster>().Remove(master);
            dbContext.SaveChanges();
        }

В SaveChanges() он выдает следующее:

System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.UpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.SqlClient.SqlException : The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.TestChilds_dbo.TestMasters_MasterId". The conflict occurred in database "SCM_Test", table "dbo.TestChilds", column 'MasterId'.
The statement has been terminated.

Я делаю что-то неправильно или я неправильно понял, что говорит MSDN?

4b9b3361

Ответ 1

Это работает так, как описано, но статья о MSDN пропускает, чтобы подчеркнуть, что он работает, только если дети загружаются в контекст, а не только родительский объект. Таким образом, вместо использования Find (который загружает только родителя) вы должны использовать загрузку с помощью Include (или любым другим способом для загрузки детей в контекст):

using (var dbContext = new TestContext())
{
    var master = dbContext.Set<TestMaster>().Include(m => m.Children)
        .SingleOrDefault(m => m.Id == 1);
    dbContext.Set<TestMaster>().Remove(master);
    dbContext.SaveChanges();
}

Это приведет к удалению мастера из базы данных, установите все внешние ключи в объектах Child на null и напишите инструкции UPDATE для детей в базу данных.

Ответ 2

После следующего @Slauma большого ответа я все еще получал ту же ошибку, что и OP.

Итак, не будь таким наивным, как я, и подумайте, что приведенные ниже примеры приведут к тому же результату.

dbCtx.Entry(principal).State = EntityState.Deleted;
dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();

// code above will give error and code below will work on dbCtx.SaveChanges()

dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();
dbCtx.Entry(principal).State = EntityState.Deleted;

Сначала загрузите детей в контекст до, чтобы удалить состояние объекта (если вы это делаете).