Pular para o conteúdo principal

Entidades Dinâmicas no EFCore

Para atingir o seu objetivo de fazer com que os atributos do dicionário Atributos sejam referenciados diretamente no objeto (como produto.Titulo), a melhor abordagem em C# é utilizar o DynamicObject.

Ao herdar de DynamicObject, você pode interceptar as tentativas de ler ou escrever em propriedades que não existem estaticamente na classe e redirecionar essas operações para o seu dicionário.

Aqui está como você pode refatorar o seu código para que o exemplo produto.Titulo funcione perfeitamente:

Código Refatorado

C#
using System;
using System.Collections.Generic;
using System.Dynamic;

public abstract class ModeloBase : DynamicObject
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public bool Ativo { get; set; } = true;
    
    // O dicionário que guardará as propriedades dinâmicas
    public Dictionary<string, object> Atributos { get; set; } = new Dictionary<string, object>();

    // Intercepta a LEITURA da propriedade (ex: var x = produto.Titulo)
    public override bool TryGetMember(GetMemberBinder binder, out object? result)
    {
        // Se existir no dicionário, retorna o valor. Se não, retorna null (ou lançar exceção)
        if (Atributos.TryGetValue(binder.Name, out result))
        {
            return true;
        }
        
        result = null; 
        return true; // Retorna true para não quebrar a execução se a propriedade não existir
    }

    // Intercepta a ESCRITA da propriedade (ex: produto.Titulo = "...")
    public override bool TrySetMember(SetMemberBinder binder, object? value)
    {
        Atributos[binder.Name] = value;
        return true;
    }
}

// Supondo que ModeloAuditado herde de ModeloBase
public abstract class ModeloAuditado : ModeloBase 
{
    public DateTime DataCriacao { get; set; } = DateTime.UtcNow;
}

public class Produto : ModeloAuditado
{
    // Propriedades estáticas normais ainda funcionam aqui se você precisar delas
    public decimal? Preco Fixo { get; set; } 
}

Como usar no seu código

Para que o C# entenda que o objeto terá propriedades dinâmicas em tempo de execução, você precisa declarar a variável usando a palavra-chave dynamic:

C#
class Program
{
    static void Main()
    {
        // ATENÇÃO: É necessário tipar como 'dynamic' para habilitar a mágica
        dynamic produto = new Produto(); 
        
        // Atribuição direta (o TrySetMember entra em ação aqui)
        produto.Titulo = "Titulo do produto 1";
        produto.Preco = 99.90;

        // Leitura direta (o TryGetMember entra em ação aqui)
        Console.WriteLine($"ID: {produto.Id}"); // Propriedade estática normal
        Console.WriteLine($"Título: {produto.Titulo}"); // Propriedade dinâmica do dicionário
        Console.WriteLine($"Preço: {produto.Preco}");   // Propriedade dinâmica do dicionário

        // Se você olhar o dicionário por trás das cenas, os dados estão lá:
        Console.WriteLine($"No dicionário: {produto.Atributos["Titulo"]}");
    }
}

Pontos importantes para ficar de olho:

  1. Uso do dynamic: Se você declarar como Produto produto = new Produto();, o compilador do C# vai procurar a propriedade Titulo estática em tempo de compilação e vai dar erro. Usar dynamic produto avisa o compilador para "confiar em você" e resolver isso em tempo de execução.

  2. Performance: O uso de DynamicObject e dynamic tem um custo de performance ligeiramente maior do que propriedades normais por conta da resolução em tempo de execução (DLR - Dynamic Language Runtime), mas para cenários como entidades flexíveis/EAV (Entity-Attribute-Value), funciona muito bem.

Comentários

Postagens mais visitadas deste blog

UML e comunicação entre Componentes

(conteúdo gerado com I.A.) Antes de abordarmos o conceito de Componentes, vale a pena analisar como um programador iniciante costuma estruturar uma aplicação. Geralmente, a escolha é construir um monólito tradicional, agrupando todas as classes de domínio (como Categoria, Produto, Compra, Venda, Pagamento, etc.) em uma única pasta de Entidades. Da mesma forma, criam-se pacotes globais para Repositórios, Serviços e Controllers. O grande problema dessa abordagem é que ela gera um forte acoplamento entre as classes e as camadas do sistema. À medida que a aplicação cresce, isso desencadeia uma série de dificuldades: Manutenção e modelagem complexas: As responsabilidades se misturam. Rapidamente, o código passa a ter diversos trechos onde as classes assumem funções que não deveriam ter, tornando a evolução do sistema confusa. Escalabilidade ineficiente: Para escalar o backend, é necessário replicar a aplicação inteira e todo o banco de dados. Além de consumir mais recursos, isso traz o ri...

Quando escolher Flask ou Quart no Python

Atualmente , considerando o suporte assíncrono já presente no Flask, a escolha entre Flask e Quart para uma API Server depende de vários fatores técnicos. Vejamos alguns critérios: Fonte:  https://pt.stackoverflow.com/questions/51268/qual-a-diferen%C3%A7a-entre-comunica%C3%A7%C3%A3o-ass%C3%ADncrona-e-s%C3%ADncrona Suporte Assíncrono : Flask : O Flask oferece suporte assíncrono por meio de extensões de terceiros, como  flask[async] . No entanto, seu desempenho assíncrono é inferior aos frameworks nativamente assíncronos. Quart : O Quart é uma reimplantação do Flask baseada no padrão ASGI (Asynchronous Server Gateway Interface). Ele utiliza E/S assíncrona em todo o código e é mais adequado para bases de código predominantemente assíncronas. Desempenho : Flask : Como aplicação WSGI, o Flask usa um único worker para cada ciclo de solicitação/resposta. Quando uma solicitação chega a uma visualização assíncrona, o Flask inicia um loop de eventos em uma thread, executa a função da vi...

Metodo para criar relacionamento entre tabelas sem dar erro cíclico EFCore

Quando estamos modelando o banco de dados no Entity Framework Core (ou em qualquer outra ferramenta) é muito comum criarmos o erro cíclico. Este erro ocorre devido ao banco de dados não conseguir determinar onde o relacionamento termina, causando um erro de loop. Vejamos uma situação real onde foi gerado esse problema: Um Professor leciona em várias Turmas, Um Professor leciona uma ou mais Disciplinas, Um Professor leciona determinadas Disciplinas em determinadas Turmas, Uma Turma tem muitos Professores, Em todas as Turmas tem 9 Disciplinas. Em todas as turmas podem ter até 9 Professores. Só por essa descrição já é possível perceber que os relacionamentos podem ser complexos de entender. E tem-se também o problema da navegação em via dupla: Professor-Turmas, Turma-Professores. Então, como modelar essas tabelas sem que o banco entre em loop? Para resolver esse problema no Entity Framework Core e evitar o erro de "Multiple Cascade Paths" (caminhos múltiplos de cascata) comum n...