Yordi.EntityMultiSQL 1.2.0-rc3

This is a prerelease version of Yordi.EntityMultiSQL.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Yordi.EntityMultiSQL --version 1.2.0-rc3
                    
NuGet\Install-Package Yordi.EntityMultiSQL -Version 1.2.0-rc3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Yordi.EntityMultiSQL" Version="1.2.0-rc3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Yordi.EntityMultiSQL" Version="1.2.0-rc3" />
                    
Directory.Packages.props
<PackageReference Include="Yordi.EntityMultiSQL" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Yordi.EntityMultiSQL --version 1.2.0-rc3
                    
#r "nuget: Yordi.EntityMultiSQL, 1.2.0-rc3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Yordi.EntityMultiSQL@1.2.0-rc3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Yordi.EntityMultiSQL&version=1.2.0-rc3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Yordi.EntityMultiSQL&version=1.2.0-rc3&prerelease
                    
Install as a Cake Tool

Yordi.EntityMultiSQL

Descrição

Yordi.EntityMultiSQL é um framework para criar instruções SQL para SQLite, MySQL e MSSQL. Ele permite realizar operações CRUD, criar tabelas e campos com base nos objetos POCO, além de gerenciar automaticamente índices (incluindo índices parciais com cláusula WHERE).

Características

  • Suporte para SQLite, MySQL e MSSQL
  • Operações CRUD (Create, Read, Update, Delete)
  • Criação automática de tabelas e campos com base em objetos POCO
  • Gerenciamento automático de índices (simples, compostos e parciais)
  • Suporte para índices parciais com cláusula WHERE (SQLite 3.8+ e MySQL 8.0+)
  • Suporte para atributos personalizados para controle de mapeamento de colunas
  • Detecção e atualização automática de mudanças em estruturas de tabelas
  • Suporte para triggers em MySQL
  • 🆕 Tratamento automático de "database is locked" no SQLite (v1.2.0+)
  • 🆕 Suporte a WAL mode e BusyTimeout para melhor concorrência (v1.2.0+)

Requisitos

  • .NET 8.0
  • Yordi.Tools 1.0.14+ (classe Chave movida para este pacote)
  • SQLite 3.8.0+ (para índices parciais)
  • MySQL 8.0.13+ (para índices parciais)

Instalação

Para instalar o pacote, adicione a seguinte referência ao seu projeto:

dotnet add package Yordi.EntityMultiSQL

Evolução

  • 1.2.0 - 🆕 Tratamento de "database is locked" no SQLite
    • Adicionado SQLiteRetryHelper para retry automático com backoff exponencial
    • Configuração automática de BusyTimeout=30000 (30 segundos) na connection string
    • Habilitação automática de PRAGMA journal_mode=WAL para melhor concorrência
    • Métodos ResetarConexao() e LiberarLocksSQLiteAsync() para liberação manual de locks
    • Classe SQLiteLockStatus para diagnóstico do estado de bloqueio do banco
  • 1.1.4 - Atualização de dependências. Classe Chave movida para Yordi.Tools v1.0.14
  • 1.1.3 - ⚠️ DEPRECATED/NÃO UTILIZAR - Versão com problemas de dependência. Use 1.1.4 ou superior
  • 1.1.2 - Correção de bug na criação de tabelas SQLite com campos do tipo Guid. Agora trata como BLOB
  • 1.1.1 - Correção de mensagem de log para inclusão de registros com o atributo Verbose
  • 1.1.0 - Acréscimo de atributo Verbose, definido em configuração (DBConfig), para descrever em log a maioria dos CRUD (exceto R)
  • 1.0.3 - Mudança de biblioteca de comunicação com SQLite. Voltamos para System.Data.SQLite
  • 1.0.2 - Correção de bugs
  • 1.0.1 - Correção de bugs
  • 1.0.0 - Versão inicial

⚠️ Nota Importante sobre Versão 1.1.3

A versão 1.1.3 está DEPRECATED e não deve ser utilizada. Esta versão contém objetos com dependências incorretas que impedem sua aplicabilidade prática.

Mudanças na versão 1.1.4:

  • A classe Chave foi movida para o pacote Yordi.Tools v1.0.14
  • Todas as dependências foram corrigidas
  • Utilize sempre a versão 1.1.4 ou superior

🆕 Tratamento de "Database is Locked" (v1.2.0+)

A partir da versão 1.2.0, o framework oferece tratamento automático para o erro database is locked no SQLite, que ocorre quando múltiplas operações tentam acessar o banco simultaneamente.

O Problema

O SQLite tem limitações com escrita simultânea. Quando uma conexão mantém um lock por muito tempo, outras operações falham com a mensagem:

SQLiteException: database is locked

A Solução

O framework agora implementa três camadas de proteção:

1. BusyTimeout Automático

A connection string do SQLite é automaticamente configurada com BusyTimeout=30000 (30 segundos), fazendo o SQLite aguardar até 30 segundos antes de falhar com "database is locked".

Valor Tempo
1000 1 segundo
30000 30 segundos
60000 1 minuto
2. WAL Mode (Write-Ahead Logging)

O modo WAL é habilitado automaticamente ao abrir a conexão, permitindo melhor concorrência entre leituras e escritas:

PRAGMA journal_mode=WAL;
3. Retry Automático com SQLiteRetryHelper

Quando ocorre um lock, o sistema tenta novamente com backoff exponencial:

// Configuração padrão
SQLiteRetryHelper.DefaultMaxRetries = 3;      // Máximo de tentativas
SQLiteRetryHelper.DefaultRetryDelayMs = 500;  // Delay inicial em ms

Uso Manual do SQLiteRetryHelper

Para operações customizadas, você pode usar o helper diretamente:

using Yordi.EntityMultiSQL;

// Executar operação com retry automático
var resultado = await SQLiteRetryHelper.ExecuteWithRetryAsync(
    async () => await meuComando.ExecuteNonQueryAsync(),
    maxRetries: 3,
    retryDelayMs: 500,
    onRetry: (tentativa, ex) => Console.WriteLine($"Tentativa {tentativa}: {ex.Message}")
);

Verificar Status de Lock

Para diagnóstico, você pode verificar o status do banco:

var status = await SQLiteRetryHelper.VerificarStatusLockAsync(connectionString);

Console.WriteLine(status.Conectado);      // true/false
Console.WriteLine(status.PodeEscrever);   // true/false  
Console.WriteLine(status.JournalMode);    // "wal", "delete", etc.
Console.WriteLine(status.WalBlocked);     // true/false

// Ou simplesmente:
Console.WriteLine(status.ToString());
// Output: "Conectado | Escrita: OK | Journal: wal | WAL Blocked: False | WAL Pages: 0/0"

Liberar Locks Manualmente

Em casos extremos, você pode forçar a liberação de locks:

// Via IBDConexao
await conexao.LiberarLocksSQLiteAsync();

// Ou via helper estático
SQLiteRetryHelper.LimparPoolsConexao();
await SQLiteRetryHelper.TentarLiberarLocksAsync(connectionString);

Resetar Conexão

Se a conexão estiver corrompida ou travada:

conexao.ResetarConexao();

Métodos que Suportam Retry Automático

Todos os métodos de escrita do RepositorioAsyncAbstract agora suportam retry automático para SQLite:

Método Retry Automático
Incluir(T obj)
Incluir(IEnumerable<T>)
Atualizar(T obj)
Atualizar(IEnumerable<T>)
AtualizarOuIncluir(T obj)
AtualizarOuIncluir(IEnumerable<T>)
Upsert(T obj)
Excluir(T obj)
Excluir(IEnumerable<T>)
ExecuteSQL(string sql)

Compatibilidade com Transações

O retry é compatível com transações. Se você usar BeginTransaction, o retry acontece apenas na execução do comando, preservando a transação:

using (var transaction = conexao.BeginTransaction())
{
    // O retry acontece aqui, dentro da mesma transação
    await repositorio.Incluir(objeto);
    
    // Se falhar após todas as tentativas, você ainda pode fazer rollback
    await transaction.CommitAsync();
}

Uso

Configuração

Primeiro, configure a conexão com o banco de dados implementando a interface IBDConexao:

public class MinhaConexao : IBDConexao 
{ 
    // Implementação dos métodos e propriedades da interface IBDConexao 
}

Repositório

Crie uma classe de repositório que herda de RepositorioAsyncAbstract<T> ou RepositorioGenerico:

public class MeuRepositorio : RepositorioGenerico<POCOclass> 
{ 
    public MeuRepositorio(IBDConexao bd) : base(bd) { }
    // Métodos específicos do repositório
}

Entidade

Defina suas entidades POCO com os atributos necessários:

[POCOtoDB(Tipo = POCOType.CADASTRO)]
public class POCOclass 
{
    [Autoincrement] 
    public int Id { get; set; }
    
    [Key] 
    public string KeyProperty { get; set; }
    
    // Outros campos
}

Gerenciamento de Índices

Para habilitar o gerenciamento automático de índices, implemente a interface IPOCOIndexes:

Nota: A classe Chave agora está disponível no pacote Yordi.Tools (v1.0.14+).

using Yordi.Tools; // Chave agora está neste namespace

public class Usuario : IPOCOIndexes
{
    [Key]
    public int Id { get; set; }
    public string Login { get; set; }
    public string Email { get; set; }
    public bool Ativo { get; set; }
    public DateTime UltimoAcesso { get; set; }

    public IEnumerable<Chave> GetIndexes()
    {
        return new List<Chave>
        {
            // Índice simples
            new Chave 
            { 
                Campo = "Login", 
                Parametro = "IX_Usuario_Login" 
            },
            
            // Índice composto
            new Chave 
            { 
                Campo = "Email", 
                Parametro = "IX_Usuario_Email_Ativo" 
            },
            new Chave 
            { 
                Campo = "Ativo", 
                Parametro = "IX_Usuario_Email_Ativo" 
            },
            
            // Índice parcial (apenas usuários ativos)
            new Chave 
            { 
                Campo = "UltimoAcesso", 
                Parametro = "IX_Usuario_UltimoAcesso_Ativos" 
            },
            new Chave 
            { 
                Parametro = "Ativo",      // Campo da condição WHERE
                Valor = true,             // Valor da condição
                Operador = Operador.IGUAL,
                Tipo = Tipo.BOOL
            }
        };
    }
}

SQL Gerado (SQLite):

CREATE INDEX IF NOT EXISTS IX_Usuario_Login ON Usuario (Login);
CREATE INDEX IF NOT EXISTS IX_Usuario_Email_Ativo ON Usuario (Email, Ativo);
CREATE INDEX IF NOT EXISTS IX_Usuario_UltimoAcesso_Ativos ON Usuario (UltimoAcesso) WHERE Ativo = 1;

Exemplo Completo

using Yordi.EntityMultiSQL;
using Yordi.Tools; // Para usar a classe Chave

class Teste : EventBaseClass
{
    async Task Test()
    {
        IBDConexao conexao = new MinhaConexao();
        IEnumerable<Type> types = conexao.Tabelas; // new List<Type>() { typeof(POCOclass) }
        
        TableCheckByType bllCheckTable = new TableCheckByType(conexao, debug: true);
        
        foreach (var type in types)
        {
            // Cria/atualiza tabela e gerencia índices automaticamente
            if (!await bllCheckTable.CriaTabela(type, false))
                Message($"Verificação da tabela {type.Name} resultou em erro");
        }
        
        var repositorio = new MeuRepositorio(conexao);
        var entidade = new POCOclass { KeyProperty = "Exemplo" }; 
        await repositorio.Insere(entidade);
    }
}

Recursos Avançados

Índices Parciais

Índices parciais (partial indexes) incluem apenas um subconjunto de linhas baseado em uma condição WHERE. São úteis para:

  • Reduzir o tamanho do índice
  • Melhorar performance de queries específicas
  • Economizar espaço em disco

Exemplo:

// Índice apenas para pedidos pendentes
new Chave { Campo = "DataPedido", Parametro = "IX_Pedidos_Pendentes" },
new Chave 
{ 
    Parametro = "Status",
    Valor = "Pendente",
    Operador = Operador.IGUAL,
    Tipo = Tipo.STRING
}

SQL Gerado:

CREATE INDEX IX_Pedidos_Pendentes ON Pedidos (DataPedido) WHERE Status = 'Pendente';

Gerenciamento Automático

O sistema automaticamente:

  • ✅ Cria índices novos quando a tabela é criada ou atualizada
  • ✅ Remove índices obsoletos que não estão mais definidos
  • ✅ Recria índices quando as colunas são modificadas
  • ✅ Suporta múltiplas condições WHERE (AND)
  • ✅ Usa formatação SQL correta para cada tipo de banco de dados

Para mais detalhes, consulte a documentação completa de índices.

Documentação Adicional

Contribuição

Contribuições são bem-vindas! Sinta-se à vontade para abrir issues e pull requests no repositório GitHub.

Licença

Este projeto está licenciado sob a MIT License.

Autores

  • Leopoldo Yordi (leoyordi)

Agradecimentos

Agradecemos a todos os contribuidores e usuários do projeto!

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.2 152 8/3/2026
1.3.1 101 8/3/2026
1.2.5 127 5/12/2026
1.2.4 113 5/12/2026
1.2.3 106 5/12/2026
1.2.2 115 5/10/2026
1.2.1 105 5/6/2026
1.2.0 139 3/16/2026
1.2.0-rc3 125 2/3/2026
1.2.0-rc2 131 12/31/2025
1.2.0-rc1 209 12/24/2025
1.1.4 469 12/8/2025
1.1.3 798 12/8/2025 1.1.3 is deprecated.
1.1.2 280 9/21/2025
1.1.1 236 6/25/2025
1.1.0 235 6/25/2025
1.0.4 242 3/17/2025
1.0.3 210 2/25/2025
1.0.1 198 2/15/2025
1.0.0 229 2/14/2025 1.0.0 is deprecated because it has critical bugs.

v1.2.0-rc3: Acréscimo de recursos para lidar com 'database is locked' no SQLite.
v1.2.0-rc2: Correção de bugs menores. Atualização da biblioteca Yordi.Tools para v1.0.16.
v1.2.0-rc1: Release Candidate 1.
v1.1.4: Atualização de dependências - Classe Chave movida para Yordi.Tools v1.0.14. IMPORTANTE: version 1.1.3 is DEPRECATED.
v1.1.3: [DEPRECATED] Não utilizar - problemas de dependência.