Yordi.EntityMultiSQL
1.3.2
dotnet add package Yordi.EntityMultiSQL --version 1.3.2
NuGet\Install-Package Yordi.EntityMultiSQL -Version 1.3.2
<PackageReference Include="Yordi.EntityMultiSQL" Version="1.3.2" />
<PackageVersion Include="Yordi.EntityMultiSQL" Version="1.3.2" />
<PackageReference Include="Yordi.EntityMultiSQL" />
paket add Yordi.EntityMultiSQL --version 1.3.2
#r "nuget: Yordi.EntityMultiSQL, 1.3.2"
#:package Yordi.EntityMultiSQL@1.3.2
#addin nuget:?package=Yordi.EntityMultiSQL&version=1.3.2
#tool nuget:?package=Yordi.EntityMultiSQL&version=1.3.2
Yordi.EntityMultiSQL
Framework .NET para mapeamento POCO → SQL, CRUD assíncrono, criação/atualização de tabelas e gerenciamento automático de índices (incluindo índices parciais).
⚠️ Posicionamento atual da biblioteca
Importante: a biblioteca não é mais especializada para MySQL.
Apesar do nomeMultiSQL, o design atual está orientado a um núcleo multibanco, com foco prático em SQLite e MySQL no runtime de conexão.
Principais recursos
- CRUD assíncrono com repositórios genéricos
- Resultado encapsulado por operação (
Result<T>/RepositorioResult<T>) — distingue sucesso, não-encontrado, conflito, bloqueio e erro - Criação e atualização de tabelas por reflexão de POCOs
- Gerenciamento automático de índices:
- simples
- compostos
- parciais (
WHERE)
- Suporte a atributos de mapeamento (
Key,Autoincrement, etc.) - Tratamento de concorrência no SQLite com:
BusyTimeout- WAL mode (
PRAGMA journal_mode=WAL) - lock de escrita coordenado por semáforo
- Checkpoint manual para cenários de pausa/continuidade de serviço
- Encerramento gracioso de conexão SQLite com checkpoint WAL no shutdown
Novidades da linha 1.2.x
A evolução recente consolidou o comportamento de concorrência e encerramento no SQLite.
1) SQLiteConnectionManager
Gerencia o ciclo de vida da conexão SQLite:
- criação de conexão com
BusyTimeoutautomático - habilitação idempotente de WAL mode
- conexão por operação: cada chamada a
CriarConexao()retorna uma novaSQLiteConnection; o pool nativo do SQLite reaproveita a conexão nativa — instanciar o objeto .NET é barato - serialização de escrita com
SemaphoreSlimestático - checkpoint manual sem alterar
journal_mode:CheckpointAsync(viaCheckpointSQLiteAsync):PRAGMA wal_checkpoint(TRUNCATE)+PRAGMA shrink_memory
- dois caminhos de encerramento:
EncerrarAsync(viaDispose/DisposeAsync): aguarda lock de escrita,PRAGMA wal_checkpoint(TRUNCATE),PRAGMA shrink_memory, fechamento e limpeza de poolLiberarLocksAsync(viaLiberarLocksSQLiteAsync): limpeza de pool, checkpoint,PRAGMA journal_mode=DELETE(remove arquivos-wal/-shm), segundo checkpoint,PRAGMA shrink_memory, fechamento e limpeza de pool
2) IBDConexao com controle operacional
Além de abrir conexão, agora expõe métodos explícitos para lock/shutdown:
AguardarLockEscritaAsync(...)LiberarLockEscrita()ResetarConexao()CheckpointSQLiteAsync()LiberarLocksSQLiteAsync()
3) BDConexao mais resiliente
- delega o comportamento SQLite ao
SQLiteConnectionManager - conexão por operação para SQLite: cada chamada a
ObterConexaoAsynccria uma novaSQLiteConnection— eliminaObjectDisposedExceptioncausado por compartilhamento de instância entre threads - mantém reset de conexão para recuperação em cenários de lock/estado inválido
- suporta descarte síncrono e assíncrono (
Dispose/DisposeAsync)
Requisitos
- .NET 8.0
Yordi.Tools(compatível com a versão definida no projeto)System.Data.SQLiteMySql.Data
Instalação
dotnet add package Yordi.EntityMultiSQL
Uso rápido
1) Configuração (DBConfig)
Exemplo típico para SQLite:
var config = new DBConfig
{
TipoDB = TipoDB.SQLite,
Local = @".\\Database",
Database = "Topcon.Service.db",
TryReconnect = 3,
SecondsWaitToTry = 1,
UsarSQLiteWALMode = true
};
IBDConexao conexao = new BDConexao(config);
2) Repositório
public class MeuRepositorio : RepositorioGenerico<MinhaEntidade>
{
public MeuRepositorio(IBDConexao bd) : base(bd) { }
}
3) Entidade POCO
[POCOtoDB(Tipo = POCOType.CADASTRO)]
public class MinhaEntidade
{
[Autoincrement]
public int Auto { get; set; }
[Key]
public string Codigo { get; set; } = string.Empty;
}
Resultado encapsulado (Result<T>)
Os métodos do repositório retornam Task<T?>, bool ou int. Esse retorno "cru" não distingue, por exemplo, "não encontrei" de "deu erro", nem "0 linhas afetadas" de "falha" — a informação ficava apenas no campo Mensagem (compartilhado e sujeito a corrida em uso concorrente).
RepositorioResult<T> é uma classe-base que herda de RepositorioAsyncAbstract<T> e oculta (via new) os métodos públicos de dados, trocando o retorno cru (Task<T?> / bool / int) por um Result<T> explícito. Toda a lógica de SQL permanece na base; aqui só há a tradução do desfecho, capturando erros pelos eventos da instância (sem parsing de string). Use instanciando-a diretamente ou herdando dela.
Status possíveis (StatusOperacao)
| Status | Significado |
|---|---|
Sucesso |
operou e teve efeito (achou / inseriu / atualizou) |
NaoEncontrado |
executou sem erro, mas sem resultado (SELECT vazio, 0 linhas afetadas) |
Conflito |
esperava um único registro e o WHERE casou com mais de um |
Bloqueado |
bloqueio transitório (timeout de lock ou database is locked) — candidato a retry |
Erro |
exceção do banco ou de mapeamento |
Uso
var repo = new RepositorioResult<MinhaEntidade>(conexao);
var r = await repo.Item(new MinhaEntidade { Codigo = "ABC" });
switch (r.Status)
{
case StatusOperacao.Sucesso: Usar(r.Valor!); break;
case StatusOperacao.NaoEncontrado: Avisar("não existe"); break;
case StatusOperacao.Bloqueado: AgendarRetry(); break; // transitório
case StatusOperacao.Erro: Logar(r.Erro); break; // r.Erro traz SQL e parâmetros em .Data
}
// atalhos: r.Sucesso · r.Falhou · r.Conflitou · r.Bloqueou · r.TemValor
// dados: r.Valor · r.LinhasAfetadas · r.Mensagem · r.Erro
Em Conflito, r.LinhasAfetadas traz a quantidade de registros que casaram o critério. Em falhas, r.Erro preserva o contexto de diagnóstico (SQL e parâmetros em Exception.Data).
Concorrência: a captura de erro assina os eventos da instância durante a chamada. Use uma instância por operação lógica (o tempo de vida transient/scoped normal de um repositório); não compartilhe a mesma instância entre operações concorrentes.
A API "crua" (RepositorioAsyncAbstract<T> / RepositorioGenerico<T>) permanece inalterada — quem quiser o retorno encapsulado usa as classes *Result. A adoção é incremental.
Herdeiros especializados
RepositorioGenericoResult<T> é o equivalente encapsulado de RepositorioGenerico<T>: mesmos atalhos (Lista(string), PorAutoMinMax), agora retornando Result<T>.
public class ClienteRepo : RepositorioGenericoResult<Cliente>
{
public ClienteRepo(IBDConexao bd) : base(bd) { }
}
Result<IEnumerable<Cliente>> r = await new ClienteRepo(conexao).Lista("silva");
Sobrescrevendo para tratar entidades estrangeiras
Os métodos Result<T> são virtual. Para orquestrar filhos/chaves estrangeiras, sobrescreva o método e chame base.Método (que executa o CRUD encapsulado da base), tratando as entidades relacionadas com outro repositório:
public class PedidoRepo : RepositorioResult<Pedido>
{
private readonly RepositorioResult<ItemPedido> _itens;
public PedidoRepo(IBDConexao bd) : base(bd) => _itens = new RepositorioResult<ItemPedido>(bd);
public override async Task<Result<Pedido>> Atualizar(Pedido p)
{
var r = await base.Atualizar(p); // SQL do pai (encapsulado)
if (r.Sucesso)
foreach (var item in p.Itens)
await _itens.Atualizar(item); // trata filhos/FK com outro repositório
return r;
}
}
Caveat do
newhiding: sobrescreva os métodosResult<T>(virtuais) — não os métodos crus deRepositorioAsyncAbstract<T>. Comobase.Xresolve estaticamente para a base, um override do método cru não seria visto pelo caminhoResult<T>. Por uma referência do tipo cru chamam-se as versões cruas; pelo tipo concreto (ouRepositorioResult<T>), as encapsuladas.
Ciclo de vida em Windows Service (OnPause / OnContinue / OnStop)
Exemplo recomendado para SQLite:
// OnPause: checkpoint leve, mantém WAL e operação para continuar depois
await conexao.CheckpointSQLiteAsync();
// OnContinue: retoma processamento normal
// OnStop/OnShutdown: encerramento forte + dispose
await conexao.LiberarLocksSQLiteAsync();
await conexao.DisposeAsync();
CheckpointSQLiteAsync não altera journal_mode, portanto é apropriado para pausa temporária.
LiberarLocksSQLiteAsync aplica PRAGMA journal_mode=DELETE, indicado para encerramento definitivo.
Índices automáticos e parciais
A biblioteca mantém suporte a índices definidos nas entidades via IPOCOIndexes e classe Chave (Yordi.Tools), inclusive com cláusula WHERE.
Para detalhes completos:
INDEX_MANAGEMENT_DOCUMENTATION.md
Shutdown gracioso (SQLite / WAL)
Em Host, Worker Service ou Windows Service, finalize explicitamente a conexão no encerramento:
await conexao.LiberarLocksSQLiteAsync();
await conexao.DisposeAsync();
LiberarLocksSQLiteAsync aplica PRAGMA journal_mode=DELETE, o que faz o SQLite remover os arquivos auxiliares (-wal, -shm). DisposeAsync executa o checkpoint WAL e limpa os pools de conexão.
Evolução (resumo)
- 1.3.2
- Novo: propagação de
ControleAlteracao.TipoAutornos mesmos fluxos em que o ORM já propagavaControleAlteracao.Usuario - Novo:
Insert/Updatepor coluna agora preenchemTipoAutorInclusaoeTipoAutorAlteracaoquando presentes no POCO - Novo: caminhos que materializam
ICommonColumnsem memória agora copiamTipoAutorInclusao/TipoAutorAlteracaode forma simétrica aUsuario - dependência:
Yordi.Tools1.0.23
- Novo: propagação de
- 1.3.1
- Atualização: dependência
Yordi.Toolspara1.0.23(comControleAlteracaopor cadeia de chamada viaAsyncLocal) - Compatibilidade: sem mudança de API pública no ORM; adoção do novo comportamento de autoria ocorre via pacote
Yordi.Tools
- Atualização: dependência
- 1.3.0
- Novo: resultado encapsulado
Result<T>+StatusOperacao(Sucesso,NaoEncontrado,Conflito,Bloqueado,Erro) — elimina a ambiguidade denull/false/0e a corrida do campoMensagemcompartilhado - Novo:
RepositorioResult<T>— classe-base que herda deRepositorioAsyncAbstracte oculta (vianew) os métodos públicos, retornando sempreResult<T>; o SQL permanece único na base - Novo:
RepositorioGenericoResult<T>— equivalente encapsulado deRepositorioGenerico<T>(atalhosLista(string)/PorAutoMinMaxretornandoResult<T>) - Novo:
ConflitoException(AtualizarOuIncluircomWHEREambíguo) eBloqueioException(timeout de lock), classificadas comoConflito/Bloqueado;database is lockeddo SQLite também é reconhecido comoBloqueado, permitindo retry - Novo: projeto de testes xUnit
Yordi.EntityMultiSQL.Tests(cobreConflito,Bloqueado,Erro,SucessoeNaoEncontrado) - Mudança de comportamento: timeout de lock agora dispara
ExceptionEvent(antesMessageEvent); a API existente permanece compatível - dependência:
Yordi.Tools1.0.22
- Novo: resultado encapsulado
- 1.2.5
- Fix:
AtualizaValorlançavaInvalidCastExceptionao processar propriedadesDateOnlymapeadas comoTipo.DATA— o cast direto(DateTime)c.Valorfoi substituído por um guardis DateOnly, deixando o valor passar sem modificação para normalização emCriaParameter - Fix:
Objeto(DataRow)—TimeOnlyeTimeSpanagora reconhecem o formato"0001-01-01 HH:mm:ss.fff"gravado peloCriaParameter, usandoDateTime.TryParsecomo fallback; formatos tradicionais ("HH:mm","HH:mm:ss","d.HH:mm:ss.fff") continuam suportados
- Fix:
- 1.2.4
- Fix:
CriaParameterpassa a usarDbType.StringparaTipo.HORA— elimina truncamento de segundos e milissegundos causado porDbType.TimenoSystem.Data.SQLite - Novo:
TimeSpaneTimeOnlysão serializados como"0001-01-01 HH:mm:ss.fff", formato DATETIME-like compatível com as funções nativas do SQLite (time(),strftime(), comparações e ordenação) - Novo:
DateOnlyé serializado como"yyyy-MM-dd"comDbType.Stringexplícito, evitando conflito comDbType.DateTimeque seria atribuído pelo mapeamentoTipo.DATAapós atualização doYordi.Tools - Compatibilidade: o formato ISO 8601 TEXT é lexicograficamente ordenável —
ORDER BYem colunas de data/hora funciona corretamente; sem quebra para tabelas já existentes gravadas pelo driver
- Fix:
- 1.2.3
- Fix: verificação de existência da coluna no
DataRowantes de acessar — evitaArgumentExceptionsilenciado para propriedades sem coluna correspondente - Fix: tipo valor não-anulável (
int,bool, etc.) com valor nulo no banco mantém odefaultdo tipo em vez de lançarArgumentException - Fix: conversão UTC→Local para campos de auditoria (
DataInclusao/DataAlteracao) reativada e corrigida — respeitaDateTime.Kindpara evitar dupla conversão quando o driver já retornaLocal(comportamento padrão doSystem.Data.SQLite) - Fix:
long→intcomcheckedcast para detectar overflow;booltratado via comparação com0/1(padrão SQLite) - Fix: condição invertida em
Datas()que impedia lançarArgumentExceptionquandoTnão herdava deCommonColumns - Novo: suporte a
DateOnly,TimeOnly,TimeSpaneDateTimeOffsetno mapeamento — converte a partir destring,DateTime,TimeSpane valores numéricos Unix timestamp (long/int) para total compatibilidade com SQLite - Novo: cache estático de
PropertyInfo[]por tipo (ConcurrentDictionary) — evita reflexão repetida em grandes volumes de dados
- Fix: verificação de existência da coluna no
- 1.2.2
- atualização e verificação de dependências: MySql.Data 9.7.0, SQLitePCLRaw 3.0.3, System.Data.SQLite 2.0.3, Yordi.Tools 1.0.19
- 1.2.1
- adicionado
CheckpointSQLiteAsyncpara checkpoint manual sem alterarjournal_mode - documentação de uso para ciclos
OnPause/OnContinue/OnStopem serviços
- adicionado
- 1.2.0
- consolidação dos recursos de concorrência SQLite
- melhorias para cenários de
database is locked - gerenciamento de WAL e locks no ciclo de vida da conexão
- 1.1.4
- ajustes de dependência (
ChaveemYordi.Tools)
- ajustes de dependência (
- 1.1.3
- DEPRECATED (não utilizar)
Contribuição
Contribuições são bem-vindas via issues e pull requests.
Licença
MIT.
Autor
Leopoldo Yordi (leoyordi).
| Product | Versions 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. |
-
net8.0
- MySql.Data (>= 9.7.0)
- SQLitePCLRaw.bundle_e_sqlite3 (>= 3.0.3)
- SQLitePCLRaw.core (>= 3.0.3)
- System.Data.SQLite (>= 2.0.3)
- Yordi.Tools (>= 1.0.23)
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.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 |
v1.3.2:
- Novo: propagação de ControleAlteracao.TipoAutor nos mesmos fluxos em que o ORM já propagava ControleAlteracao.Usuario.
- Insert/Update por coluna agora preenchem TipoAutorInclusao e TipoAutorAlteracao quando presentes no POCO.
- Fluxos que materializam ICommonColumns em memória agora copiam TipoAutorInclusao/TipoAutorAlteracao de forma simétrica a Usuario.
- Dependência mantida em Yordi.Tools 1.0.23.
v1.3.1:
- Bump de dependência para Yordi.Tools 1.0.23 (ControleAlteracao com escopo por cadeia de chamada via AsyncLocal).
- Compatibilidade preservada no ORM: sem alterações de API pública ou recompilação obrigatória para consumidores já compilados.
v1.3.0:
- Novo: resultado encapsulado Result<T> + StatusOperacao (Sucesso, NaoEncontrado, Conflito, Bloqueado, Erro), eliminando a ambiguidade de null/false/0 e a corrida do campo compartilhado _msg.
- Novo: RepositorioResult<T>, decorator sobre RepositorioAsyncAbstract que traduz cada operacao em Result<T>, capturando erros via eventos (sem parsing de string).
- Novo: ConflitoException (AtualizarOuIncluir com WHERE ambiguo) e BloqueioException (timeout de lock), classificadas como Conflito/Bloqueado.
- Mudanca de comportamento: timeout de lock agora dispara ExceptionEvent (antes MessageEvent); "database is locked" do SQLite reconhecido como Bloqueado.
- Bump Yordi.Tools 1.0.22. Novo projeto de testes xUnit (Yordi.EntityMultiSQL.Tests).
v1.2.5: Fix: AtualizaValor lançava InvalidCastException ao processar propriedades DateOnly (Tipo.DATA) — cast direto para DateTime substituído por guard 'is DateOnly'.
v1.2.4:
- Fix: CriaParameter agora usa DbType.String para Tipo.HORA, evitando truncamento de segundos/milissegundos causado por DbType.Time.
- Novo: valores TimeSpan e TimeOnly são serializados como texto ISO "0001-01-01 HH:mm:ss.fff", formato DATETIME compatível com SQLite.
- Novo: valores DateOnly são serializados como "yyyy-MM-dd" e forçam DbType.String, evitando conflito com DbType.DateTime atribuído via Tipo.DATA.
- Compatibilidade: formato ISO 8601 TEXT é lexicograficamente ordenável, não quebra tabelas existentes gravadas pelo System.Data.SQLite.
v1.2.3:
- Fix: verificação de existência da coluna no DataRow antes de acessar (evita ArgumentException silenciado).
- Fix: tipo valor não-anulável com valor nulo no banco mantém o default do tipo, sem tentar SetValue(null).
- Fix: conversão UTC→Local para campos de auditoria (DataInclusao/DataAlteracao) reativada e corrigida — respeita DateTime.Kind para evitar dupla conversão quando o driver já retorna Local.
- Fix: long→int com checked cast para detectar overflow de valores SQLite; bool tratado via comparação com 0/1 (padrão SQLite).
- Fix: condição invertida em Datas() que impedia lançar ArgumentException quando T não herdava de CommonColumns.
- Novo: suporte a conversão de DateOnly, TimeOnly, TimeSpan e DateTimeOffset a partir de string, DateTime, TimeSpan e valores numéricos Unix timestamp (long/int) para compatibilidade com SQLite.
- Novo: cache estático de PropertyInfo[] por tipo (ConcurrentDictionary) para evitar reflexão repetida em grandes volumes.
v1.2.2: Atualização e verificação de dependências: MySql.Data 9.7.0, SQLitePCLRaw.bundle_e_sqlite3 3.0.3, SQLitePCLRaw.core 3.0.3, System.Data.SQLite 2.0.3, Yordi.Tools 1.0.19.
v1.2.1: Adicionado CheckpointSQLiteAsync para checkpoint manual (sem alterar journal_mode), com orientação para uso em ciclos de serviço (OnPause/OnContinue) e manutenção da liberação forte para OnStop/OnShutdown.
v1.2.0: Lançamento oficial da versão 1.2.0.
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.