Wie strukturiere ich eine C # Konsolenanwendung, um effizient IDisposable Datenbank-Ressourcen zu nutzen?

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

Frage

Hier ist mein Vorschlag (sehr vereinfacht, um das Problem Raum zu zeigen) Entwurf für eine C # Konsolenanwendung. Die Datenbankverbindungen implementieren IDisposable, und diese Lösung erlaubt es nicht, für using die Datenbank Verbindungsobjekte. Kann jemand eine richtige Struktur für eine Konsolenanwendung vorschlagen? Dies ist ein Problem, das ich oft lösen muß.

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

Edit:

Manche Menschen können nicht herausfinden, warum ich sie als Mitglied Variablen wollen würde. Hier ist mein Anwendungsfall (ein wenig psuedocoded) von dem, was in doDatabaseWork gehen würde:

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

Sehen Sie, wie ich diese Verbindungen für das Leben dieser Schleife offen halten wollen würde?

War es hilfreich?

Lösung

Ich denke, dass die beste Lösung ist Haupt-Logik von Program-Klasse zu extrahieren. Die Programmklasse ist eine Art Starter für die primäre Arbeit. Und Wrapper für SqlConnections Bereitstellung ist keine gute Idee, in der Tat, weil sie Ressourcen bereits verwaltet werden, wickeln sie überflüssig ist. So meine Lösung sieht wie folgt aus:

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

Andere Tipps

Wie wäre es, eine Klasse zu schreiben, die IDisposable implementiert.

In Ihrem Klassenkonstruktor können Sie Ihre DB-Verbindungen instanziiert.

Dann in Ihrer IDisposable.Dispose Methode, schreiben Sie Ihre abzureißen Code für Ihre DB-Verbindungen zu schließen.

Hier ist ein Codebeispiel zu zeigen, was ich meine:

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

Und dann, aus Ihrer Haupt-Methode Ihrer Programmklasse:

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

Scotts Antwort ist eine Möglichkeit, es zu tun. Sie könnten auch erwägen, versuchen Sie mit {} finally statt?

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

Ich persönlich glaube, Sie sind über das Denken und die Codebeispiele in diesem Thread sind zu komplex imho. Ich habe keine Ahnung, warum die Leute IDisposable auf ihre Programmklasse implementieren entweder da es angeordnet ist, wenn es austritt.

kann ich nicht denken Sie an einen einzigen Grund, nicht zu verwenden oder warum können Sie nicht die Verwendung von () {} Anweisung verwenden.

Sie möchten eine Verbindung öffnen und es halten? Warum? Alle realen Anschlüsse sind hinter den Kulissen in .net Verbindungspooling, so new'ing Connection-Objekte ist keine große Sache. Einfach öffnen und schließen, wie Sie benötigen sie und Connection Pooling behandelt, dass alle hinter den Kulissen.

bearbeiten ich mein Beispiel in einer Klasse zu wickeln, so dass Sie Ihre Verkapselung auch haben können.

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, ich sehe niemand erwähnt hat es auf diese Weise zu tun. Sie müssen nicht die Variablen, die in der using verwendet werden lokal deklariert.


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
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top