Compartilhar via


Exclusão em cascata

O EF Core (Entity Framework Core) representa relações usando chaves estrangeiras. Uma entidade com uma chave estrangeira é a entidade filho ou dependente na relação. O valor da chave estrangeira dessa entidade deve corresponder ao valor da chave primária (ou um valor de chave alternativo) da entidade principal/pai relacionada.

Se a entidade principal/pai for excluída, os valores de chave estrangeira dos dependentes/filhos não corresponderão mais à chave primária ou alternativa de qualquer entidade/pai. Esse é um estado inválido e causará uma violação de restrição referencial na maioria dos bancos de dados.

Há duas opções para evitar essa violação de restrição referencial:

  1. Definir os valores do FK como nulos
  2. Exclua também as entidades dependentes/filho

A primeira opção só é válida para relações opcionais em que a propriedade de chave estrangeira (e a coluna de banco de dados para a qual ela está mapeada) deve ser anulável.

A segunda opção é válida para qualquer tipo de relação e é conhecida como "exclusão em cascata".

Dica

Este documento descreve exclusões em cascata (e exclusão de órfãos) da perspectiva de atualizar o banco de dados. Ele faz uso intenso dos conceitos introduzidos no Rastreamento de Alterações no EF Core e na Mudança de Chaves Estrangeiras e Navegações. Certifique-se de entender completamente esses conceitos antes de abordar o material aqui.

Dica

Você pode executar e depurar todo o código neste documento baixando o código de exemplo do GitHub.

Quando comportamentos em cascata acontecem

Exclusões em cascata são necessárias quando uma entidade dependente/filho não pode mais ser associada à sua entidade principal/pai atual. Isso pode acontecer porque a principal/pai é excluída, ou pode acontecer quando a principal/pai ainda existe, mas o dependente/filho não está mais associado a ela.

Excluindo uma entidade principal/pai

Considere esse modelo Blog simples em que o principal/pai está em uma relação com Post, que é o dependente/filho. Post.BlogId é uma propriedade de chave estrangeira, que deve corresponder à Blog.Id chave primária do blog à qual a postagem pertence.

public class Blog
{
    public int Id { get; set; }

    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    public int Id { get; set; }

    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

Por convenção, essa relação é configurada como obrigatória, já que a Post.BlogId propriedade de chave estrangeira não é anulável. As relações necessárias são configuradas para usar exclusões em cascata por padrão. Consulte Relações para obter mais informações sobre relações de modelagem.

Ao excluir um blog, todas as postagens são excluídas em cascata. Por exemplo:

using var context = new BlogsContext();

var blog = await context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).FirstAsync();

context.Remove(blog);

await context.SaveChangesAsync();

O SaveChanges gera o SEGUINTE SQL usando o SQL Server como exemplo:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Cortando uma relação

Em vez de excluir o blog, poderíamos, em vez disso, cortar a relação entre cada postagem e seu blog. Isso pode ser feito definindo a navegação Post.Blog de referência como nula para cada postagem:

using var context = new BlogsContext();

var blog = await context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).FirstAsync();

foreach (var post in blog.Posts)
{
    post.Blog = null;
}

await context.SaveChangesAsync();

A relação também pode ser cortada removendo cada postagem da navegação da Blog.Posts coleção:

using var context = new BlogsContext();

var blog = await context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).FirstAsync();

blog.Posts.Clear();

await context.SaveChangesAsync();

Em ambos os casos, o resultado é o mesmo: o blog não é excluído, mas as postagens que não estão mais associadas a nenhum blog são excluídas:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

A exclusão de entidades que não estão mais associadas a nenhum principal/dependente é conhecida como "exclusão de órfãos".

Dica

A exclusão em cascata e a exclusão de órfãos estão intimamente relacionadas. Ambos resultam na exclusão de entidades dependentes/filhas quando a relação com a entidade principal/mãe necessária é cortada. Para exclusão em cascata, essa separação ocorre porque o elemento principal/pai é excluído. Para órfãos, a entidade principal/pai ainda existe, mas não está mais relacionada às entidades dependentes/filho.

Onde ocorrem comportamentos em cascata

Comportamentos em cascata podem ser aplicados a:

  • Entidades rastreadas pelo atual DbContext
  • Entidades no banco de dados que não foram carregadas no contexto

Exclusão em cascata de entidades controladas

O EF Core sempre aplica comportamentos em cascata configurados a entidades controladas. Isso significa que, se o aplicativo carregar todas as entidades dependentes/filho relevantes no DbContext, conforme mostrado nos exemplos acima, os comportamentos em cascata serão aplicados corretamente independentemente de como o banco de dados está configurado.

Dica

O tempo exato de quando comportamentos em cascata ocorrem com entidades controladas pode ser controlado usando ChangeTracker.CascadeDeleteTiming e ChangeTracker.DeleteOrphansTiming. Consulte Como alterar as chaves estrangeiras e as navegaçãos para obter mais informações.

Exclusão em cascata no banco de dados

Muitos sistemas de banco de dados também oferecem comportamentos em cascata que são disparados quando uma entidade é excluída no banco de dados. O EF Core configura esses comportamentos com base no comportamento de exclusão em cascata configurado no modelo do EF Core quando um banco de dados é criado usando EnsureCreated ou migrações do EF Core. Por exemplo, usando o modelo acima, a tabela a seguir é criada para postagens ao usar o SQL Server:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NULL,
    [Content] nvarchar(max) NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]) ON DELETE CASCADE
);

Observe que a restrição de chave estrangeira que define a relação entre blogs e postagens está configurada com ON DELETE CASCADE.

Se soubermos que o banco de dados está configurado assim, podemos excluir um blog sem primeiro carregar postagens e o banco de dados cuidará da exclusão de todas as postagens relacionadas a esse blog. Por exemplo:

using var context = new BlogsContext();

var blog = await context.Blogs.OrderBy(e => e.Name).FirstAsync();

context.Remove(blog);

await context.SaveChangesAsync();

Observe que não há nenhum Include para postagens, portanto, elas não são carregadas. O SaveChanges, nesse caso, excluirá apenas o blog, pois essa é a única entidade que está sendo rastreada:

-- Executed DbCommand (6ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

Isso resultaria em uma exceção se a restrição de chave estrangeira no banco de dados não estiver configurada para exclusões em cascata. No entanto, nesse caso, as postagens são excluídas pelo banco de dados porque ele foi configurado com ON DELETE CASCADE quando ele foi criado.

Observação

Normalmente, os bancos de dados não têm nenhuma maneira de excluir órfãos automaticamente. Isso ocorre porque, embora o EF Core represente relações usando navegações, bem como chaves estrangeiras, os bancos de dados têm apenas chaves estrangeiras e nenhuma navegação. Isso significa que geralmente não é possível cortar uma relação sem carregar ambos os lados no DbContext.

Observação

No momento, o banco de dados na memória do EF Core não dá suporte a exclusões em cascata no banco de dados.

Aviso

Não configure exclusão em cascata no banco de dados ao realizar exclusão lógica de entidades. Isso pode fazer com que as entidades sejam excluídas acidentalmente em vez de excluídas suavemente.

Limitações em cascata de banco de dados

Alguns bancos de dados, principalmente o SQL Server, têm limitações nos comportamentos em cascata que formam ciclos. Por exemplo, considere o seguinte modelo:

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();

    public int OwnerId { get; set; }
    public Person Owner { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }

    public int AuthorId { get; set; }
    public Person Author { get; set; }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();

    public Blog OwnedBlog { get; set; }
}

Esse modelo tem três relações, todas necessárias e, portanto, configuradas para exclusão em cascata por convenção:

  • Excluir um blog excluirá em cascata todas as postagens relacionadas
  • Excluir o autor de postagens fará com que as postagens criadas sejam excluídas em cascata
  • Excluir o proprietário de um blog fará com que o blog seja excluído em cascata

Isso tudo é razoável (se um pouco draconiano nas políticas de gerenciamento de blog!) mas a tentativa de criar um banco de dados do SQL Server com essas cascatas configuradas resulta na seguinte exceção:

Microsoft.Data.SqlClient.SqlException (0x80131904): a introdução da restrição FOREIGN KEY 'FK_Posts_Person_AuthorId' na tabela 'Postagens' pode causar ciclos ou vários caminhos em cascata. Especifique ON DELETE NO ACTION ou ON UPDATE NO ACTION, ou modifique outras restrições FOREIGN KEY.

Há duas maneiras de lidar com essa situação:

  1. Altere uma ou mais das relações para não excluir em cascata.
  2. Configure o banco de dados sem uma ou mais dessas exclusões em cascata e verifique se todas as entidades dependentes são carregadas para que o EF Core possa executar o comportamento em cascata.

Tomando a primeira abordagem com nosso exemplo, poderíamos tornar a relação pós-blog opcional, dando-lhe uma propriedade de chave estrangeira anulável:

public int? BlogId { get; set; }

Uma relação opcional permite que a postagem exista sem um blog, o que significa que a exclusão em cascata não será mais configurada por padrão. Isso significa que não há mais um ciclo em ações em cascata e o banco de dados pode ser criado sem erros no SQL Server.

Usando a segunda abordagem, podemos manter a relação de proprietário do blog necessária e configurada para exclusão em cascata, mas fazer com que essa configuração se aplique apenas a entidades controladas, não ao banco de dados:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .HasOne(e => e.Owner)
        .WithOne(e => e.OwnedBlog)
        .OnDelete(DeleteBehavior.ClientCascade);
}

Agora, o que acontece se carregarmos uma pessoa e o blog que ela possui e excluir a pessoa?

using var context = new BlogsContext();

var owner = await context.People.SingleAsync(e => e.Name == "ajcvickers");
var blog = await context.Blogs.SingleAsync(e => e.Owner == owner);

context.Remove(owner);

await context.SaveChangesAsync();

O EF Core colocará em cascata a exclusão do proprietário para que o blog também seja excluído:

-- Executed DbCommand (8ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [People]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

No entanto, se o blog não for carregado quando o proprietário for excluído:

using var context = new BlogsContext();

var owner = await context.People.SingleAsync(e => e.Name == "ajcvickers");

context.Remove(owner);

await context.SaveChangesAsync();

Em seguida, uma exceção será gerada devido à violação da restrição de chave estrangeira no banco de dados:

Microsoft.Data.SqlClient.SqlException: a instrução DELETE está em conflito com a restrição REFERENCE "FK_Blogs_People_OwnerId". O conflito ocorreu no banco de dados "Scratch", tabela "dbo. Blogs", coluna 'OwnerId'. A instrução foi finalizada.

Nulos em cascata

As relações opcionais têm propriedades de chave estrangeira anuláveis mapeadas para colunas de banco de dados anuláveis. Isso significa que o valor da chave estrangeira pode ser definido como nulo quando a entidade principal/pai atual é excluída ou separada do dependente/filho.

Vamos examinar novamente os exemplos de Quando comportamentos em cascata ocorrem, mas desta vez com uma relação opcional representada por uma propriedade de chave estrangeira anulável Post.BlogId :

public int? BlogId { get; set; }

Essa propriedade de chave estrangeira será definida como nula para cada postagem quando seu blog relacionado for excluído. Por exemplo, esse código, que é o mesmo de antes:

using var context = new BlogsContext();

var blog = await context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).FirstAsync();

context.Remove(blog);

await context.SaveChangesAsync();

Agora, resultará nas seguintes atualizações de banco de dados quando SaveChanges for chamado:

-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p1='2', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (1ms) [Parameters=[@p2='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p2;
SELECT @@ROWCOUNT;

Da mesma forma, se a relação for cortada usando um dos exemplos acima:

using var context = new BlogsContext();

var blog = await context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).FirstAsync();

foreach (var post in blog.Posts)
{
    post.Blog = null;
}

await context.SaveChangesAsync();

Ou:

using var context = new BlogsContext();

var blog = await context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).FirstAsync();

blog.Posts.Clear();

await context.SaveChangesAsync();

Em seguida, as postagens são atualizadas com valores nulos de chave estrangeira quando SaveChanges é chamado:

-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p1='2', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Consulte Alterando as chaves estrangeiras e as navegações para obter mais informações sobre como o EF Core gerencia chaves estrangeiras e navegações à medida que seus valores são alterados.

Observação

A correção de relações como essa tem sido o comportamento padrão do Entity Framework desde a primeira versão em 2008. Antes do EF Core, ele não tinha um nome e não era possível alterar. Agora é conhecido como ClientSetNull descrito na próxima seção.

Os bancos de dados também podem ser configurados para anular em cascata como este quando uma entidade principal/pai em uma relação opcional é excluída. No entanto, isso é muito menos comum do que usar exclusões em cascata no banco de dados. Usar exclusões em cascata e nulos em cascata no banco de dados ao mesmo tempo quase sempre resultará em ciclos de relação ao usar o SQL Server. Consulte a próxima seção para obter mais informações sobre como configurar nulos em cascata.

Configurando comportamentos em cascata

Dica

Leia as seções acima antes de vir aqui. As opções de configuração provavelmente não farão sentido se o material anterior não for compreendido.

Os comportamentos em cascata são configurados por relação usando o OnDelete método em OnModelCreating. Por exemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .HasOne(e => e.Owner)
        .WithOne(e => e.OwnedBlog)
        .OnDelete(DeleteBehavior.ClientCascade);
}

Consulte Relações para obter mais informações sobre como configurar relações entre tipos de entidade.

OnDelete aceita um valor da enumeração, reconhecidamente confusa DeleteBehavior . Essa enumeração define o comportamento do EF Core em entidades controladas e a configuração de exclusão em cascata no banco de dados quando o EF é usado para criar o esquema.

Impacto no esquema de banco de dados

A tabela a seguir mostra o resultado de cada valor de OnDelete para a restrição de chave estrangeira criada pelas migrações do EF Core ou EnsureCreated.

DeleteBehavior Impacto no esquema de banco de dados
Cascata ON DELETE CASCADE
Restringir AO EXCLUIR RESTRIÇÃO
SemAção padrão do banco de dados
SetNull AO DELETAR, DEFINIR COMO NULO
ClientSetNull padrão do banco de dados
ClientCascade padrão do banco de dados
ClientNoAction padrão do banco de dados

Os comportamentos de ON DELETE NO ACTION (o padrão do banco de dados) e ON DELETE RESTRICT em bancos de dados relacionais normalmente são idênticos ou muito semelhantes. Apesar do que NO ACTION pode implicar, ambas as opções resultam na imposição de restrições referenciais. A diferença, quando há uma, é quando o banco de dados verifica as restrições. Verifique a documentação do banco de dados para obter as diferenças específicas entre ON DELETE NO ACTION e ON DELETE RESTRICT em seu sistema de banco de dados.

O SQL Server não dá suporte ON DELETE RESTRICT, portanto ON DELETE NO ACTION , é usado em vez disso.

Os únicos valores que causarão comportamentos em cascata no banco de dados são Cascade e SetNull. Todos os outros valores configurarão o banco de dados para não fazer nenhuma alteração em cascata.

Impacto no comportamento de SaveChanges

As tabelas nas seções a seguir abrangem o que acontece com entidades dependentes/filho quando o principal/pai é excluído ou sua relação com as entidades dependentes/filho é cortada. Cada tabela abrange uma das seguintes:

  • Relações opcionais (FK anulável) e necessárias (FK não anulável)
  • Quando dependentes/filhos são carregados e acompanhados pelo DbContext e quando eles existem apenas no banco de dados

Relação obrigatória com dependentes/filhos carregada

DeleteBehavior Ao excluir principal/pai Ao separar do principal/mãe
Cascata Dependentes excluídos pelo EF Core Dependentes excluídos pelo EF Core
Restringir InvalidOperationException InvalidOperationException
SemAção InvalidOperationException InvalidOperationException
SetNull SqlException na criação de banco de dados SqlException na criação de banco de dados
ClientSetNull InvalidOperationException InvalidOperationException
ClientCascade Dependentes excluídos pelo EF Core Dependentes excluídos pelo EF Core
ClientNoAction DbUpdateException InvalidOperationException

Observações:

  • O padrão para relações necessárias como esta é Cascade.
  • Usar qualquer coisa que não seja a exclusão em cascata para as relações necessárias resultará em uma exceção quando SaveChanges for chamado.
    • Normalmente, isso é de um InvalidOperationException do EF Core, já que o estado inválido é detectado nos filhos/dependentes carregados.
    • ClientNoAction força o EF Core a não verificar os dependentes para correção antes de enviá-los ao banco de dados, portanto, nesse caso, o banco de dados lança uma exceção, que é envolta em um DbUpdateException pelo SaveChanges.
    • SetNull é rejeitado ao criar o banco de dados, pois a coluna de chave estrangeira não é anulável.
  • Como os dependentes/filhos são carregados, eles são sempre excluídos pelo EF Core e nunca deixados para que o banco de dados os exclua.

Relação necessária com dependentes/filhos não carregada

DeleteBehavior Ao excluir principal/pai Ao separar do principal/mãe
Cascata Dependentes excluídos pelo banco de dados Não aplicável
Restringir DbUpdateException Não aplicável
SemAção DbUpdateException Não aplicável
SetNull SqlException na criação de banco de dados Não aplicável
ClientSetNull DbUpdateException Não aplicável
ClientCascade DbUpdateException Não aplicável
ClientNoAction DbUpdateException Não aplicável

Observações:

  • A ruptura de uma relação não é válida aqui, uma vez que os dependentes/filhos não estão carregados.
  • O padrão para relações necessárias como esta é Cascade.
  • Usar qualquer coisa que não seja a exclusão em cascata para as relações necessárias resultará em uma exceção quando SaveChanges for chamado.
    • Normalmente, isso ocorre DbUpdateException porque os dependentes/filhos não são carregados e, portanto, o estado inválido só pode ser detectado pelo banco de dados. Em seguida, SaveChanges encapsula a exceção de banco de dados em um DbUpdateException.
    • SetNull é rejeitado ao criar o banco de dados, pois a coluna de chave estrangeira não é anulável.

Relação opcional com dependentes/filhos listados

DeleteBehavior Ao excluir principal/pai Ao separar do principal/mãe
Cascata Dependentes excluídos pelo EF Core Dependentes excluídos pelo EF Core
Restringir FKs dependentes definidos como nulos pelo EF Core FKs dependentes definidos como nulos pelo EF Core
SemAção FKs dependentes definidos como nulos pelo EF Core FKs dependentes definidos como nulos pelo EF Core
SetNull FKs dependentes definidos como nulos pelo EF Core FKs dependentes definidos como nulos pelo EF Core
ClientSetNull FKs dependentes definidos como nulos pelo EF Core FKs dependentes definidos como nulos pelo EF Core
ClientCascade Dependentes excluídos pelo EF Core Dependentes excluídos pelo EF Core
ClientNoAction DbUpdateException FKs dependentes definidos como nulos pelo EF Core

Observações:

  • O padrão para relações opcionais como esta é ClientSetNull.
  • Dependentes/filhos nunca são excluídos, a menos que Cascade ou ClientCascade estejam configurados.
  • Todos os outros valores fazem com que os FKs dependentes sejam definidos como nulos pelo EF Core...
    • ... exceto ClientNoAction que informa ao EF Core para não tocar nas chaves estrangeiras de dependentes/filhos quando o principal/pai é excluído. Portanto, o banco de dados gera uma exceção, que é encapsulada como um DbUpdateException por SaveChanges.

Relação opcional com dependentes ou filhos não foi carregada

DeleteBehavior Ao excluir principal/pai Ao separar do principal/mãe
Cascata Dependentes excluídos pelo banco de dados Não aplicável
Restringir DbUpdateException Não aplicável
SemAção DbUpdateException Não aplicável
SetNull FKs dependentes definidos como nulos por banco de dados Não aplicável
ClientSetNull DbUpdateException Não aplicável
ClientCascade DbUpdateException Não aplicável
ClientNoAction DbUpdateException Não aplicável

Observações:

  • A ruptura de uma relação não é válida aqui, uma vez que os dependentes/filhos não estão carregados.
  • O padrão para relações opcionais como esta é ClientSetNull.
  • Dependentes/filhos devem ser carregados para evitar uma exceção de banco de dados, a menos que o banco de dados tenha sido configurado para excluir ou anular em cascata.