Como usar Entity Framework In-Memory no C# Console .NET Core

Este artigo é um passo a passo para demostrar como utilizar o Entity Framework In-Memory em uma aplicação Console usando .NET Core.

Criando a aplicação

Crie uma aplicação de console C# .net core chamada EntityFrameworkSample. O visual estúdio já deixa aberto para você a classe principal.

using System;

namespace EntityFrameworkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Adicionar uma classe na pasta Models

Model é um conjunto de classes que representam os dados que o aplicativo gerencia. O Model para esta aplicação é uma única classe GeometricShape.

No Solution Explorer, clique com o botão direito do mouse no projeto.
Selecione Adicionar > Nova Pasta. Nomeie a pasta “Models”.

Clique com o botão direito na pasta Models e selecione
Adicionar > Classe, Nomeie a classe “GeometricShape” e selecione Adicionar.

Substitua o código do modelo pelo seguinte:

namespace EntityFrameworkSample.Models
{
    public class GeometricShape
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsRound { get; set; }
    }
}

Adicionar um contexto de banco de dados

O contexto do banco de dados é a classe principal que coordena a funcionalidade do Entity Framework para um modelo de dados. Essa classe é criada derivando da classe Microsoft.EntityFrameworkCore.DbContext.

Para isso vamos Adicionar pacotes NuGet

  • No menu Ferramentas, selecione
    Gerenciador de pacotes NuGet > Gerenciar pacotes NuGet para solução.
  • Selecione a guia Procurar e digite Microsoft.EntityFrameworkCore.SqlServer na caixa de pesquisa.
  • Selecione Microsoft.EntityFrameworkCore.SqlServer no painel esquerdo.
  • Marque a caixa de seleção Projeto no painel direito (EntityFrameworkSample) e selecione Instalar.

    Use as instruções anteriores para adicionar o pacote NuGet Microsoft.EntityFrameworkCore.InMemory.

Adicione o contexto do banco de dados GeometricContext

Clique com o botão direito na pasta Models e selecione
Adicionar > Classe. Nomeie a classe GeometricContext e clique em Adicionar.

Deixe a classe como a apresentada abaixo

using Microsoft.EntityFrameworkCore;

namespace EntityFrameworkSample.Models
{
    public class GeometricContext : DbContext
    {
        public GeometricContext(DbContextOptions<GeometricContext> options)
            : base(options)
        {
        }

        public DbSet<GeometricShape> GeometricShapes { get; set; }
    }
}

Registre o contexto do banco de dados

Agora a estrutura básica de banco está criada, a injeção de dependência será implementada.

Na raiz do projeto selecione
Adicionar > Classe. Nomeie a classe ServiceDbContext e clique em Adicionar.

Deixe a classe como a apresentada abaixo

using EntityFrameworkSample.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace EntityFrameworkSample
{
    class ServiceDbContext
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<GeometricContext>(opt => opt.UseInMemoryDatabase("GeometricDB"));
        }
    }
}

Nota: UseInMemoryDatabase, configura o contexto para se conectar a um banco de dados na memória. O banco de dados na memória é compartilhado em qualquer lugar com o mesmo nome, mas apenas para um determinado provedor de serviços.

Utilizando nosso banco de dados

Precisamos ajustar a inicialização da aplicação, neste caso como é uma aplicação console, no Program.cs (se fosse uma API, seria em outro arquivo).

Segue o Program.cs, com a chamada dos métodos onde são gerados os dados e consumidos.

using EntityFrameworkSample.Models;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace EntityFrameworkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("__BEGIN__");

            var collection = new ServiceCollection();
            ServiceDbContext.ConfigureServices(collection);
            var serviceProvider = collection.BuildServiceProvider();
            var dbContext = serviceProvider.GetService<GeometricContext>();

            AddShapesToDatabase(dbContext);

            foreach (var geometricShape in dbContext.GeometricShapes.ToList())
            {
                Console.WriteLine(JsonSerializer.Serialize(geometricShape));
            }

            Console.WriteLine("__END__");
        }

        public static void AddShapesToDatabase(GeometricContext dbContext)
        {
            dbContext.GeometricShapes.Add(new GeometricShape() {Name = "Triangle", IsRound = false});
            dbContext.GeometricShapes.Add(new GeometricShape() {Name = "Square", IsRound = false});
            dbContext.GeometricShapes.Add(new GeometricShape() {Name = "Cincle", IsRound = true});
            dbContext.SaveChanges();
        }
    }
}

Ao executar a aplicação acima você vai ter a seguinte saída impressa em seu console.

__BEGIN__
{"Id":1,"Name":"Triangle","IsRound":false}
{"Id":2,"Name":"Square","IsRound":false}
{"Id":3,"Name":"Cincle","IsRound":true}
__END__

[SYSTEM_PATH]EntityFrameworkSample.exe (process 8656) exited with code 0.
Press any key to close this window . . .

Nota: “exited with code 0” não é um erro. Exit code 0 quer dizer que a aplicação rodou sem problemas.

Erro comum

Existe um erro que é comum no Entity Framework

Exception: The instance of entity type ‘INSTANCE_TYPE’ cannot be tracked because another instance with the same key value for {‘ID’} is already being tracked.

Seu contexto de banco de dados está sendo compartilhado por várias solicitações, o que significa que a entidade que você está editando já foi rastreada. Por isso do erro.

Para resolver o problema você deve “desatachar” o objeto do Contexto do E.F. e então realizar a ação de update (que é onde mais vemos esse problema)

Uma função simples para resolver esse problema é esta abaixo.

        public void DetachedTrackerFromRequest(GeometricContext dbContext, GeometricShape geoContext)
        {
            var local = dbContext.Set<GeometricShape>().Local.FirstOrDefault(entry => entry.Id.Equals(geoContext.Id));
            dbContext.Entry(local).State = EntityState.Detached;
        }

Qualquer dúvida ou dicas, entre em contato: leandrolt@gmail.com

Principais referências

– https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.inmemorydbcontextoptionsextensions.useinmemorydatabase?view=efcore-5.0
– https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-web-api?view=aspnetcore-5.0&tabs=visual-studio
– https://stormpath.com/blog/tutorial-entity-framework-core-in-memory-database-asp-net-core
– https://docs.microsoft.com/pt-br/ef/core/providers/in-memory/?tabs=dotnet-core-cli
– https://medium.com/@marcionizzola/utilizando-entity-framework-in-memory-no-c-9865b1183c84
– https://wilsonsantosnet.medium.com/e-f-core-detach-local-bd3d161ecefc
– https://entityframework.net/knowledge-base/48202403/instance-of-entity-type-cannot-be-tracked-because-another-instance-with-same-key-value-is-tracked
– https://entityframeworkcore.com/knowledge-base/50987635/the-instance-of-entity-type–item–cannot-be-tracked-because-another-instance-with-the-same-key-value-for—id—is-already-being-tracked
– https://stackoverflow.com/questions/36856073/the-instance-of-entity-type-cannot-be-tracked-because-another-instance-of-this-t

Leave a Reply

Your email address will not be published. Required fields are marked *