Come strutturare un'applicazione console C # per utilizzare in modo efficiente le risorse del database IDisposable?

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

Domanda

Ecco il mio progetto proposto (molto semplificato per illustrare lo spazio problematico) per un'applicazione console C #. Le connessioni al database implementano IDisposable e questa soluzione non consente di utilizzare gli oggetti di connessione al database. Qualcuno può proporre una struttura più corretta per un'applicazione console? Questo è un problema che devo risolvere spesso.

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

Modifica

Alcune persone non riescono a capire perché le vorrei come variabili membro. Ecco il mio caso d'uso (un po 'psuedocoded) di cosa sarebbe andato in doDatabaseWork:

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

Vedi come vorrei mantenere aperte queste connessioni per la vita di questo ciclo?

È stato utile?

Soluzione

Penso che la soluzione migliore sia estrarre la logica principale dalla classe Program. La classe Program è una sorta di antipasto per il lavoro primario. E fornire involucri per SqlConnections non è una buona idea, poiché sono già risorse gestite, il loro confezionamento è ridondante. Quindi la mia soluzione è simile a questa:

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

Altri suggerimenti

Che ne dici di scrivere una classe che implementa IDisposable.

All'interno del costruttore della classe, è possibile creare un'istanza delle connessioni DB.

Quindi all'interno del tuo metodo IDisposable.Dispose, scrivi il tuo codice di abbattimento per chiudere le tue connessioni DB.

Ecco un esempio di codice per dimostrare cosa intendo:

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 poi, all'interno del tuo metodo principale della tua classe di programma:

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

La risposta di Scott è un modo per farlo. Puoi anche prendere in considerazione l'utilizzo di 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, penso che tu stia pensando troppo e gli esempi di codice in questo thread sono imho eccessivamente complessi. Non ho idea del perché le persone stiano implementando IDisposable nella loro classe Program sia perché è disposta quando esce.

Non riesco a pensare a un singolo motivo per non utilizzare o perché non è possibile utilizzare l'istruzione using () {}.

Vuoi aprire una connessione e tenerla? Perché? Tutte le connessioni reali sono dietro le quinte nel pool di connessioni .net, quindi i nuovi oggetti Connection non sono un grosso problema. Basta aprire e chiudere quando ne hai bisogno e il pool di connessioni gestisce tutto ciò dietro le quinte.

Ho modificato il mio esempio per inserirlo in una classe in modo da poter avere anche l'incapsulamento.

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, vedo che nessuno ha menzionato farlo in questo modo. Non è necessario che le variabili utilizzate in using siano dichiarate 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
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top