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

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

Вопрос

Вот мой предложенный (очень упрощенный, чтобы проиллюстрировать проблемное пространство) дизайн для консольного приложения на 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
    }
}

Редактировать:

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

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

Видите, как бы я хотел сохранить эти соединения открытыми на протяжении всего срока действия этого цикла?

Это было полезно?

Решение

Я думаю, что лучшее решение - извлечь основную логику из класса Program.Программный класс - это своего рода стартер для первичной работы.И предоставление оболочек для 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
    }
}

Другие советы

Как насчет написания класса, который реализует 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();
        }
    }
}

Ответ Скотта - это один из способов сделать это.Вы также могли бы рассмотреть возможность использования вместо этого 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();
    }
}

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

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

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

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

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

}

Хм, я вижу, никто не упоминал, что делает это таким образом.Вам не обязательно иметь переменные, которые используются в 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
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top