Как структурировать консольное приложение C # для эффективного использования ресурсов базы данных IDisposable? - PullRequest
5 голосов
/ 06 мая 2009

Вот мой предложенный (очень упрощенный, чтобы проиллюстрировать проблемное пространство) дизайн для консольного приложения C #. Соединения с базой данных реализуют IDisposable, и это решение не допускает using объектов соединения с базой данных. Может кто-нибудь предложить более правильную структуру для консольного приложения? Это проблема, которую мне нужно часто решать.

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:

Некоторые люди не могут понять, почему я хочу, чтобы они были переменными-членами. Вот мой вариант использования (немного psuedocoded) того, что будет в doDatabaseWork:

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

Посмотрите, как бы я хотел, чтобы эти соединения были открыты для жизни этого цикла?

Ответы [ 5 ]

6 голосов
/ 06 мая 2009

Как насчет написания класса, который реализует IDisposable.

Внутри вашего конструктора класса вы можете создавать экземпляры соединений с БД.

Затем в вашем методе IDisposable.Dispose вы пишете свой код разрыва для закрытия соединений с БД.

Вот пример кода, демонстрирующий, что я имею в виду:

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

А затем, из вашего основного метода класса вашей Программы:

class Program
{
    static void Main(string[] args)
    {
        using (DBWrapper wrapper = new DBWrapper())
        {
            wrapper.DoWork();
        }
    }
}
2 голосов
/ 06 мая 2009

Я думаю, что лучшее решение - извлечь основную логику из класса Program. Класс Programme является своего рода стартером для основной работы. А предоставление оболочек для SqlConnections на самом деле не очень хорошая идея, поскольку они уже являются управляемыми ресурсами, их обертывание избыточно. Таким образом, мое решение выглядит так:

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
    }
}
2 голосов
/ 06 мая 2009

Лично я думаю, что вы слишком обдумали это, и примеры кода в этой теме слишком сложны. Я понятия не имею, почему люди также реализуют IDisposable в своем классе Program, поскольку он удаляется при выходе.

Я не могу придумать ни одной причины, по которой не использовать, или почему вы не можете использовать оператор using () {}.

Вы хотите открыть Соединение и удерживать его? Зачем? Все реальные соединения находятся за кулисами в пуле соединений .net, так что новые объекты Соединений не имеют большого значения. Просто откройте и закройте, как вам нужно, и пул соединений обрабатывает все это за кадром.

Я отредактировал мой пример, чтобы обернуть его в классе, чтобы вы также могли иметь инкапсуляцию.

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

}
2 голосов
/ 06 мая 2009

Ответ Скотта - один из способов сделать это. Вы также можете рассмотреть возможность использования try {} finally вместо *

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();
    }
}
0 голосов
/ 08 мая 2009

Хм, я вижу, никто не упоминал, что делал это таким образом. Вам не нужно иметь переменные, которые используются в using, объявленном локально.


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