Como estruturar uma aplicação C # console para usar eficientemente os recursos de banco de dados IDisposable?

StackOverflow https://stackoverflow.com/questions/827884

Pergunta

Aqui está a minha (muito simplificado para ilustrar o espaço do problema) projeto proposto para uma aplicação C # console. As conexões de banco de dados implementar IDisposable, e esta solução não permite using os objetos de conexão de banco de dados. Alguém pode propor uma estrutura mais correto para um aplicativo de console? Esta é uma necessidade problema que resolver muitas vezes.

class Program 
{
    SQLiteConnection sourceConnection;
    SQLiteConnection destinationConnection;

    static void Main(string[] args)
    {
        Program shell = new Program();

        // get connection strings from command line arguments
        string sourceConnectionString = shell.getConnectionString(args);
        string destinationConnectionString = shell.getConnectionString(args);

        // call non-static methods that use
        shell.setUpConnections(sourceConnectionString, destinationConnectionString);

        shell.doDatabaseWork();
    }

    private void setUpConnections(string sourceConnectionString, string destinationConnectionString)
    {
        sourceConnection = new SQLiteConnection(sourceConnectionString);
        destinationConnection = new SQLiteConnection(destinationConnectionString);
    }

    private void doDatabaseWork()
    {
        // use the connections here
    }
}

Editar:

Algumas pessoas não conseguem descobrir por que eu iria querer-los como variáveis ??de membro. Aqui é o meu caso de uso (um pouco psuedocoded) do que iria em doDatabaseWork:

foreach (Row sourceRow in DBResultSet)
{
  string sourceXml = sourceRow.Columns["MyColumnName"].Value;
  string destinationXML = transformUsingXSLT(sourceXml);
  writeToDestination(destinationXml);
}

Veja como eu gostaria de manter essas conexões abertas para a vida deste loop?

Foi útil?

Solução

Eu acho que a melhor solução é extrair lógica principal da classe de programas. A classe Programa é algum tipo de arranque para o trabalho primário. E fornecendo wrappers para SqlConnections não é uma boa ideia, na verdade, porque eles são geridos os recursos já, envolvê-los é redundante. Assim, a minha solução parece com isso:

class ProgramCore : IDisposable
{
    internal ProgramCore(string sourceConnectionString, string destinationConnectionString)
    {
        setUpConnections(sourceConnectionString, destinationConnectionString);
    }

    internal void Execute()
    {
        // do whatever you want
        doDatabaseWork();
        // do whatever you want
    }

    public void Dispose()
    {
        if (_sourceConnection != null)
            _sourceConnection.Dispose();
        if (_destinationConnection != null)
            _destinationConnection.Dispose();
    }

    private void setUpConnections(string sourceConnectionString, string destinationConnectionString)
    {
        _sourceConnection = new SQLiteConnection(sourceConnectionString);
        _destinationConnection = new SQLiteConnection(destinationConnectionString);
    }

    private void doDatabaseWork()
    {
        // use the connections here
    }

    private SQLiteConnection _sourceConnection;
    private SQLiteConnection _destinationConnection;
}

class Program
{
    static void Main(string[] args)
    {
        // get connection strings from command line arguments
        string sourceConnectionString = GetConnectionString(args);
        string destinationConnectionString = GetConnectionString(args);

        using (ProgramCore core = new ProgramCore(sourceConnectionString, destinationConnectionString))
        {
            core.Execute();
        }
    }

    static string GetConnectionString(string[] args)
    {
        // provide parsing here
    }
}

Outras dicas

Como sobre a escrita de uma classe que implementa IDisposable.

Dentro do seu construtor da classe, você pode instanciar suas conexões de banco de dados.

Em seguida, dentro do seu Método IDisposable.Dispose, você escreve o seu lágrima pelo código para fechar suas conexões de banco de dados.

Aqui está um exemplo de código para demonstrar o que quero dizer:

public class DBWrapper : IDisposable
{
    public SqlConnection Connection1 { get; set; }
    public SqlConnection Connection2 { get; set; }

    public DBWrapper()
    {
        Connection1 = new SqlConnection();
        Connection1.Open();
        Connection2 = new SqlConnection();
        Connection2.Open();
    }
    public void DoWork()
    {
        // Make your DB Calls here
    }

    public void Dispose()
    {
        if (Connection1 != null)
        {
            Connection1.Dispose();
        }
        if (Connection2 != null)
        {
            Connection2.Dispose();
        }
    }
}

E, em seguida, de dentro de seu método principal de sua classe Programa:

class Program
{
    static void Main(string[] args)
    {
        using (DBWrapper wrapper = new DBWrapper())
        {
            wrapper.DoWork();
        }
    }
}

A resposta de Scott é uma maneira de fazê-lo. Você também pode considerar o uso de try {} finally vez?

static void Main(string[] args)
{
    Program shell = new Program();

    // get connection strings from command line arguments
    string sourceConnectionString = shell.getConnectionString(args);
    string destinationConnectionString = shell.getConnectionString(args);

    // call non-static methods that use
    shell.setUpConnections(sourceConnectionString, destinationConnectionString);
    try
    {
      shell.doDatabaseWork();
    }
    finally
    {
      if(sourceConnection != null)
        sourceConnection.Dispose();
      if(destinationConnection != null)
        destinationConnection.Dispose();
    }
}

Pessoalmente, eu acho que você está pensando sobre isso e os exemplos de código neste tópico são imho excessivamente complexa. Eu não tenho idéia por que as pessoas estão implementando IDisposable na sua classe Programa quer já que é descartado quando ele sai.

Eu não consigo pensar em uma única razão para não usar ou porque você não pode usar o usar () {} comunicado.

Você deseja abrir uma conexão e segurá-la? Por quê? Todas as ligações reais estão por trás das cenas em pool de conexão .net, então new'ing objetos de conexão não é um grande negócio. Basta abrir e fechar como você precisa deles e pool de conexão alças tudo o que nos bastidores.

Eu editei o meu exemplo para envolvê-lo em uma classe para que você possa ter o seu encapsulamento também.

class Program 
{
    static void Main(string[] args)
    {
        DBWorker worker = new DBWorker();
        worker.DoDatabaseWork();
    }
}

public class DBWorker 
{

    private void DoDatabaseWork()
    {
        using (SQLiteConnection sourceDB = new SQLiteConnection( GetConnectionString() ))
        {
            sourceDB.Open();
            using (SQLiteConnection destDB = new SQLiteConnection( GetConnectionString() ))
            {
                destDB.Open();
            }
        }
    }

}

Hmm, eu não vejo ninguém mencionou fazê-lo desta forma. Você não tem que ter as variáveis ??que são usadas na using declarado localmente.


class Program 
{
    SQLiteConnection sourceConnection;
    SQLiteConnection destinationConnection;

    static void Main(string[] args)
    {
        Program shell = new Program();

        // get connection strings from command line arguments
        string sourceConnectionString = shell.getConnectionString(args);
        string destinationConnectionString = shell.getConnectionString(args);

        using (sourceConnection = new SQLiteConnection(sourceConnectionString))
        using (destinationConnection = new SQLiteConnection(destinationConnectionString))
        {
            shell.doDatabaseWork();
        }
    }

    private void doDatabaseWork()
    {
        // use the connections here
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top