Como trabalhar com Postgres SQL e NOSql juntos com Entity Framework Core?

Olá. Vou mostrar um passo a passo de como trabalhar com banco de dado relacional e não relacional ao mesmo tempo com Postgres e suporte para EFCore.

Postgres

Postgres é uma banco de dados objeto-relational que pertence a empresa Oracle, porém é um banco de dados open source com uma forte comunidade mantendo-o. Postgres é incrivelmente robusto e confiável, dado seu histórico de 30 anos no mercado.

Data Types

Primitives: Integer, Numeric, String, Boolean
Structured: Date/Time, Array, Range / Multirange, UUID
Document: JSON/JSONB, XML, Key-value (Hstore)
Geometry: Point, Line, Circle, Polygon
Customizations: Composite, Custom Types

JSON/JSONB - Trabalhando com NSql

Uma das maravilhas do banco de Postgres é a possibilidade de trabalhar de forma relacional e não relacional no mesmo banco, no mesmo objeto, na mesma tabela. SIM ! É possível, através do tipo de dado Document, que recebe dados Json e JsonBinary (JsonB).

Entity FrameWork Core ORM

Para podermos usar o EFcore nos nossos métodos usando o NSql, precisamos instalar um pacote nuget chamado npgsql.entityframeworkcore.postgresql ele vai permitir fazer os métodos CRUD de forma mais parecida com o EFCore ORM que estamos acostumados.

Prática - Configuração

Pacotes

npgsql.entityframeworkcore.postgresql
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Toll
EFCore.NamingConventions

StartUp

ConfigureServices Method

services.AddDbContext<PostgresNsql.MyContext>(option => option.UseNpgsql(Configuration.GetConnectionString("Nsql")));

DBContext

public class MyContext : DbContext
    {
        public MyContext(DbContextOptions options) :base(options)
        {

        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
            .UseNpgsql()
            .UseSnakeCaseNamingConvention();

        protected override void OnModelCreating(ModelBuilder modelBuilder)
            => modelBuilder
            .Entity<DataModel>()
            .Property(e => e.Id)
            .UseIdentityAlwaysColumn();

        public DbSet<DataModel> DataModels { get; set; }
    }

UseSnakeCaseNamingConvention() Snake Case pode ser necessário para lidar com JSONs que separam 2 palavras com _ underline. Dessa forma não tera problemas com serialização.

Definindo auto-increment na coluna ID
.Property(e => e.Id)
.UseIdentityAlwaysColumn();
Identity always column é um dos tipos de atribuição de valor que você pode dar, nessa modalidade você define que os valores serão gerados diretamente no banco, impossibilitando você setar o valor no momento de inserção no banco de dados.

Model

Na criação da model é que está o grande pulo do gato no uso de SQL e NSql juntos. Caso você tenha dados instáveis ainda e eles não são 100% mapeaveis e estruturados, você pode usar o DataType Json para receber a parte NSql dos seus dados.

Para isso, você precisa adicionar a biblioteca System.Text.Json e tipar a sua propriedade com a classe JsonDocument, pois ele já identificará que ali será recebido dados em JSON e facilitará a descerialização.

A model precisa ser IDisposable pois a classe JsonDocument é IDisposable também, melhorando o gerenciamento de memória.

public class DataModel : IDisposable
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public int idade { get; set; }

        public JsonDocument Data { get; set; }

        public void Dispose() => Data?.Dispose();
    }

Dados Estruturados

Para identificar a parte NSql da nossa model, é preciso adicionar a Data Notation [Column(TypeName = "jsonb")] para identificar o objeto Json e mapear as propriedades.

public class DataModel
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public int idade { get; set; }

        [Column(TypeName = "jsonb")]
        public Endereco Address { get; set; }
    }

public class Endereco // Mapeando os dados recebidos em Json
{
    public string Rua { get; set; }
    public int Numero { get; set; }
    public Casa[] Casas { get; set; }
}

public class Casa 
{
    public decimal Preco { get; set; }
    public string EnderecoCompleto { get; set; }
}

CRUD

Eu não usei um repositório ou alguma arquitetura mais complexa para facilitar o entendimento do artigo.

Mas mostrarei agora na Controller como podemos trabalhar com os dados do tipo JSON.

GetAll()

Percebam que nada muda na listagem de todas as colunas.

public List<DataModel> GetAll()
        {
            return _myContext.DataModels.ToList();
        }

GetPersonalidado

Uma dúvida que tinha quando começando a trabalhar com Postgres SQL e NSql junto é se dava pra fazer em uma consulta, juntar os dados SQL e NSql. Abaixo está um exemplo de um Get onde eu parametrizo que quero apenas resultados de pessoas com 12 anos E com o ultimo nome igual a "Paixao". Idade é um valor SQL da coluna idade e "lastname" é um item dos dados que recebemos em JSON.

public IEnumerable<string> Get()
        {
            var resultado = _myContext.DataModels;

            return resultado.Where(x => x.idade == 12 && x.Data.RootElement.GetProperty("lastName").GetString() == "Paixao").ToList();           
        }

Um outro exemplo que trouxe, é caso você quero exibir apenas 1 dos itens dos dados em JSON.

public IEnumerable<string> Get()
        {
            var resultado = _myContext.DataModels;

return resultado.Select(x=>x.Data.RootElement.GetProperty("lastName").GetString()).ToList();
        }

Elementos da classe JsonElement do código apresentado acima

RootElement é uma classe que vai pegar os dados JSON da propriedade Data permitindo que você então denomine com o método GetProperty("propriedade") qual propriedade do JSON você vai trabalhar e por final você deve colocar o método GetString() para converter o dados JSON que você filtrou em string.

Obrigado

mgpaixao image

Disclaimer

A VaiVoa incentiva seus Desenvolvedores em seu processo de crescimento e aceleração técnica. Os artigos publicados não traduzem a opinião da VaiVoa. A publicação obedece ao propósito de estimular o debate.

29