EF Core 2.x -  Soft Delete (Exclusão suave)


  Hoje vou apresentar o recurso Soft Delete ou exclusão suave do EF Core 2.1.

O recurso Soft-Delete ou exclusão suave é bem simples:  significa que ao invés de excluir os registros do banco de dados, nós vamos apenas 'marcar' os registros como excluídos. Para fazer isso geralmente criamos uma coluna chamada IsDeleted que recebe o valor true para indicar que o registro foi marcado para exclusão.

Assim, para implementar o Soft Delete temos que :

  1. Incluir uma coluna para indicar se o registro foi excluído logicamente;
  2. Alterar todas as consultas para usar essa coluna para filtrar o conjunto de resultados. Afinal você não vai querer exibir um registro que foi marcado para deleção;
  3. Substituir as instruções de exclusão pelas instruções de atualização;

Uma forma bem simples de fazer isso é criar uma coluna IsDeleted, e, toda vez que um registro tiver que ser excluído, na verdade atualizamos esta coluna com o valor true.

Vamos ver isso funcionando na prática.

Vamos supor que já temos um banco de dados chamado ABDemoDB e a tabela Alunos com a seguinte estrutura:

Observe a coluna IsDeleted definida com o tipo bit que vai receber os valores true(1) ou false(0).

Abaixo vemos o script SQL para criar a tabela Alunos com a restrição definida :

USE [ABDemoDB]
GO
CREATE TABLE [dbo].[Alunos](
      [AlunoId] [int] IDENTITY(1,1) NOT NULL,
      [Nome] [nvarchar](100) NOT NULL,
      [Curso] [nvarchar](50) NOT NULL,
      [Nota] [char](1) NOT NULL,
      [IsDeleted] [bit] NOT NULL,
CONSTRAINT [PK_Alunos] PRIMARY KEY CLUSTERED
    ([AlunoId] ASC)  
);

O valor padrão para a coluna IsDeleted é 0, ou seja, false : (1 é o valor para true)

Criando o projeto no VS 2017 Community

Abra o VS 2017 Community e crie um novo projeto .NET Core do tipo Console App(.NET Core) chamado EFCore_SofDelete.

Inclua no projeto a referência para o Microsoft.EntityFrameworkCore.SqlServer :

Observe que estou usando a versão 2.21, que é a versão mais estável, mas podemos usar a versão 2.1 ou superior.

Crie no projeto a pasta Model e nesta pasta inclua duas classes:

  1. Aluno  - classe de domínio
  2. AppDbContext - classe de contexto

1- Aluno

namespace EFCore_ConvertValue.Model
{
    public class Aluno
    {
        public int AlunoId { get; set; }
        public string Nome { get;  set; }
        public string Curso { get; set; }
        public char Nota { get; set; }
        public bool IsDeleted {get; set; } 
    }
}

Note que definimos uma propriedade IsDeleted do tipo bool que vai receber os valores true e false;

2- AppDbContext

using Microsoft.EntityFrameworkCore;
using System.Linq;
namespace EFCore_SoftDelete.Model
{
    public class AppDbContext : DbContext
    {
        public DbSet<Aluno> Alunos { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
          optionsBuilder.UseSqlServer(@"Data Source=Macoratti;Initial Catalog=ABDemoDB;Integrated Security=True");
        }
        public override int SaveChanges()
        {
            //Soft-Delete
            foreach (var item in ChangeTracker.Entries()
               .Where(e => e.State == EntityState.Deleted &&
               e.Metadata.GetProperties().Any(x => x.Name == "IsDeleted")))
               {
                   item.State = EntityState.Unchanged;
                   item.CurrentValues["IsDeleted"] = true;
               }
            return base.SaveChanges();
        }
    }
}

Neste código sobrescrevemos o método SaveChanges na classe de contexto que percorre o ChangeTracker verificando se o estado das entidade é Deleted, e, se o nome da propriedade é "IsDeleted", neste caso definimos o estado da entidade como Unchanged e atribuimos o valor true para a coluna IsDeleted.

Agora, no arquivo Program.cs inclua o código abaixo:

using EFCore_SoftDelete.Model;
using static System.Console;
namespace EFCore_SoftDelete
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var contexto = new AppDbContext())
            {
                DeletaAluno(contexto);
                ListaAluno(contexto);
                ReadLine();
            }
        }
        private static void ListaAluno(AppDbContext contexto)
        {
            foreach (var aluno in contexto.Alunos)
            {
                WriteLine($"{aluno.Nome,10} {aluno.Curso,-15} {aluno.Nota,-10}");
            }
        }
        private static void DeletaAluno(AppDbContext contexto)
        {
            var aluno = contexto.Alunos.Find(1);
            contexto.Remove(aluno);
            contexto.SaveChanges();
        }
    }
}

No código acima temos o método DeletaAluno() que vai localizar o aluno com Id igual a 1(Find(1)), e, a seguir, vai remover a entidade usando o método Remove(); para efetivar a remoção emitimos um SaveChanges.

Aqui na verdade vai entrar em cena o Soft Delete que implementamos, e o registro não será excluído, mas o valor da coluna IsDelete vai ser alterado para true ou 1.

Executando projeto e verificando a tabela Alunos no banco de dados temos o seguinte:

Note que o registro para AlunoId igual a 1 teve o valor da coluna IsDeleted alterado para 1.

Mas para não exibir os registros marcados para deleção temos que alterar a consulta.

Uma forma bem simples de fazer isso é usar o recurso Global Query Filter que eu já apresentei neste artigo: Entity Framework Core - Aplicando Global Query Filters

No nosso exemplo basta alterar o arquivo de contexto incluindo o código abaixo no método OnModelCreating:

         ....
         protected override void OnModelCreating(ModelBuilder modelBuilder)
         {
              modelBuilder.Entity<Aluno>()
                 .HasQueryFilter(aluno => EF.Property<bool>(aluno, "IsDeleted") == false);
         }
         ...

Agora ao executar o projeto vamos ver a lista de alunos exibindo apenas os alunos que não estão marcados para exclusão:

Pegue o código completo do projeto aqui:  EFCore_SoftDelete.zip

"Falou-lhes, pois, Jesus outra vez, dizendo: Eu sou a luz do mundo; quem me segue não andará em trevas, mas terá a luz da vida."
João 8:12

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti