Onde devo criar meus casos DbCommand?
-
12-09-2019 - |
Pergunta
I aparentemente tem duas opções:
- Faça minha classe implementar
IDisposable
. Criar meus casosDbCommand
como camposprivate readonly
, e no construtor, adicione os parâmetros que eles usam. Sempre que eu quero escrever para o banco de dados, se ligam a estes parâmetros (reutilizando as mesmas instâncias de comando), defina as propriedadesConnection
eTransaction
, entãoExecuteNonQuery
chamada. No métodoDispose
,Dispose
chamada em cada um desses campos. - Cada vez que eu quiser escrever para o banco de dados, gravação
using(var cmd = new DbCommand("...", connection, transaction))
ao redor do uso do comando, e adicionar parâmetros e se ligam a eles cada vez, bem como, antes de chamarExecuteNonQuery
. Eu suponho que eu não preciso de um novo comando para cada consulta, apenas um novo comando para cada vez que eu abrir o banco de dados (certo?).
Ambos parecem um tanto deselegante e possivelmente incorreto.
Por nº 1, é irritante para meus usuários que esta classe é agora IDisposable
só porque eu usei algumas DbCommand
s (que deve ser um detalhe de implementação que eles não se preocupam com). Eu também sou um pouco suspeito que manter uma instância DbCommand
redor pode inadvertidamente bloquear o banco de dados ou algo assim?
Para # 2, parece que eu estou fazendo um monte de trabalho (em termos de objetos .NET) cada vez que eu quero escrever para o banco de dados, especialmente com o parâmetro de agregação. Parece que eu criar o mesmo objeto de cada vez, o que só se sente como uma prática ruim.
Para referência, aqui está o meu código atual, usando # 1:
using System;
using System.Net;
using System.Data.SQLite;
public class Class1 : IDisposable
{
private readonly SQLiteCommand updateCookie = new SQLiteCommand("UPDATE moz_cookies SET value = @value, expiry = @expiry, isSecure = @isSecure, isHttpOnly = @isHttpOnly WHERE name = @name AND host = @host AND path = @path");
public Class1()
{
this.updateCookie.Parameters.AddRange(new[]
{
new SQLiteParameter("@name"),
new SQLiteParameter("@value"),
new SQLiteParameter("@host"),
new SQLiteParameter("@path"),
new SQLiteParameter("@expiry"),
new SQLiteParameter("@isSecure"),
new SQLiteParameter("@isHttpOnly")
});
}
private static void BindDbCommandToMozillaCookie(DbCommand command, Cookie cookie)
{
long expiresSeconds = (long)cookie.Expires.TotalSeconds;
command.Parameters["@name"].Value = cookie.Name;
command.Parameters["@value"].Value = cookie.Value;
command.Parameters["@host"].Value = cookie.Domain;
command.Parameters["@path"].Value = cookie.Path;
command.Parameters["@expiry"].Value = expiresSeconds;
command.Parameters["@isSecure"].Value = cookie.Secure;
command.Parameters["@isHttpOnly"].Value = cookie.HttpOnly;
}
public void WriteCurrentCookiesToMozillaBasedBrowserSqlite(string databaseFilename)
{
using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + databaseFilename))
{
connection.Open();
using (SQLiteTransaction transaction = connection.BeginTransaction())
{
this.updateCookie.Connection = connection;
this.updateCookie.Transaction = transaction;
foreach (Cookie cookie in SomeOtherClass.GetCookieArray())
{
Class1.BindDbCommandToMozillaCookie(this.updateCookie, cookie);
this.updateCookie.ExecuteNonQuery();
}
transaction.Commit();
}
}
}
#region IDisposable implementation
protected virtual void Dispose(bool disposing)
{
if (!this.disposed && disposing)
{
this.updateCookie.Dispose();
}
this.disposed = true;
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
~Class1()
{
this.Dispose(false);
}
private bool disposed;
#endregion
}
Solução
Domenic, Descartando uma SQLiteCommand simplesmente sinaliza seu leitor ativo para eliminá-lo do leitor e define os parâmetros e ligação referência a null.
Você não está em risco de fuga de recursos, armazenando um comando enquanto você descartar corretamente o leitor executado e fechar / dispose da conexão.
Assim, a reutilização de um comando em cache e simplesmente atribuir valores aos parâmetros é de longe a mais eficiente implementação.
.Prepare () é uma noop
em SQLiteCommand portanto, não há benefício a ser tido lá.
Outras dicas
Não há nenhum single "caminho certo" para lidar com o seu ciclo de banco de dados objeto vida. É tudo depende de suas necessidades de aplicação.
A minha preferência pessoal é manter o código o mais simples possível. I tendem a recriar objetos comando e parâmetro, conforme necessário. Isso permite que as minhas funções ser tão auto-suficientes quanto possível. Tem muito simplificada qualquer factoring re eu tinha que fazer.
Se você está preocupado com o hit de objetos recriando desempenho você deve carregar um profiler e ver onde seus gargalos são. Em aplicações eu construí, eu encontrei o tempo gasto na criação DbCommand objetos é tão inconsequente em comparação com o tempo gasto executar a consulta que ele realmente não fator no meu desempenho apps.
Eu encontrei com sqlserver se reutilizar o comando com o mesmo contexto, a sua muito mais rápido. Eu pretendo teste para diferentes conexões e transações, mas eles parecem ser mais rápido.