¿Cómo estructuro una aplicación de consola C # para usar eficientemente los recursos de la base de datos IDisposable?

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

Pregunta

Aquí está mi diseño propuesto (muy simplificado para ilustrar el espacio del problema) para una aplicación de consola C #. Las conexiones de la base de datos implementan IDisposable, y esta solución no permite usar los objetos de conexión de la base de datos. ¿Alguien puede proponer una estructura más correcta para una aplicación de consola? Este es un problema que necesito resolver a menudo.

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
    }
}

Edición :

Algunas personas no pueden entender por qué los quiero como variables miembro. Aquí está mi caso de uso (un poco psuedocoded) de lo que iría en doDatabaseWork:

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

¿Ves cómo me gustaría mantener estas conexiones abiertas durante la vida de este ciclo?

¿Fue útil?

Solución

Creo que la mejor solución es extraer la lógica principal de la clase Program. La clase del programa es una especie de iniciador para el trabajo primario. Y proporcionar envoltorios para SqlConnections no es una buena idea, ya que ya son recursos administrados, envolverlos es redundante. Por lo tanto, mi solución se ve así:

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
    }
}

Otros consejos

¿Qué tal escribir una clase que implemente IDisposable?

Dentro de su constructor de clases, puede crear instancias de sus conexiones de base de datos.

Luego, dentro de su Método IDisposable.Dispose, escriba su código de desmontaje para cerrar sus conexiones DB.

Aquí hay una muestra de código para demostrar lo que quiero decir:

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();
        }
    }
}

Y luego, desde su método principal de su clase de Programa:

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

La respuesta de Scott es una forma de hacerlo. También podrías considerar usar try {} finalmente?

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();
    }
}

Personalmente, creo que estás pensando demasiado en esto y los ejemplos de código en este hilo son demasiado complejos. Tampoco tengo idea de por qué las personas están implementando IDisposable en su clase de Programa, ya que está dispuesto cuando sale.

No puedo pensar en una sola razón para no usar o por qué no puede usar la declaración using () {}.

¿Desea abrir una conexión y mantenerla? ¿Por qué? Todas las conexiones reales están detrás de escena en la agrupación de conexiones .net, por lo que los nuevos objetos de conexión no son un gran problema. Simplemente abra y cierre cuando los necesite y la agrupación de conexiones maneja todo eso detrás de escena.

Edité mi ejemplo para envolverlo en una clase para que también pueda tener su encapsulación.

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, veo que nadie ha mencionado hacerlo de esta manera. No es necesario que las variables que se usan en using se declaren 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top