Comment structurer une application console C # pour utiliser efficacement les ressources de base de données IDisposable?

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

Question

Voici la conception proposée (très simplifiée pour illustrer l'espace de problèmes) pour une application console C #. Les connexions à la base de données implémentent IDisposable et cette solution ne permet pas de utiliser les objets de connexion à la base de données. Quelqu'un peut-il proposer une structure plus correcte pour une application console? C'est un problème que je dois résoudre souvent.

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

Modifier:

Certaines personnes ne peuvent pas comprendre pourquoi je les voudrais comme variables de membre. Voici mon cas d'utilisation (un peu psuedocodé) de ce qui irait dans doDatabaseWork:

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

Vous voyez comment je voudrais garder ces connexions ouvertes pendant toute la vie de cette boucle?

Était-ce utile?

La solution

Je pense que la meilleure solution consiste à extraire la logique principale de la classe Program. La classe de programme est une sorte de débutant pour le travail primaire. Et fournir des wrappers pour SqlConnections n’est en fait pas une bonne idée, car ce sont déjà des ressources gérées, leur encapsulation est redondante. Ainsi ma solution ressemble à ceci:

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

Autres conseils

Pourquoi ne pas écrire une classe qui implémente IDisposable?

Dans le constructeur de votre classe, vous pouvez instancier vos connexions à la base de données.

Ensuite, dans votre méthode IDisposable.Dispose, vous écrivez votre code de démontage pour fermer vos connexions à la base de données.

Voici un exemple de code illustrant ce que je veux dire:

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

Et ensuite, depuis votre méthode principale de votre classe Program:

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

La réponse de Scott est un moyen de le faire. Vous pouvez également envisager d'utiliser try {} finally à la place?

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

Personnellement, je pense que vous réfléchissez trop et les exemples de code de ce fil sont trop complexes à mon humble avis. Je ne sais pas pourquoi les gens implémentent IDisposable sur leur classe Program non plus, car il est disposé dès sa sortie.

Je ne vois aucune raison de ne pas utiliser ou pourquoi vous ne pouvez pas utiliser l'instruction using () {}.

Vous souhaitez ouvrir une connexion et la conserver? Pourquoi? Toutes les connexions réelles sont dans les coulisses du regroupement de connexions .net, de sorte que les nouveaux objets Connection ne sont pas un gros problème. Il suffit d’ouvrir et de fermer selon vos besoins et le regroupement de connexions gère tout cela en coulisse.

J'ai modifié mon exemple pour l'envelopper dans une classe afin que vous puissiez également avoir votre encapsulation.

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, je vois que personne n'a mentionné le faire de cette façon. Les variables utilisées dans à l'aide de ne doivent pas être déclarées localement.


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
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top