Как найти и закрыть все открытые OleDbConnections в winforms?

Во-первых: я видел многочисленные обсуждения стека (и на других форумах) по этой теме. Это не дубликат. Я пробовал многочисленные рекомендации безрезультатно.

У меня есть приложение winforms С#, которое большое и сложное. Он устанавливает соединения OleDB с базой данных Access в разное время по разным причинам. В определенной функции нам нужно ПЕРЕМЕСТИТЬ (копировать + удалить) файл mdb, но это невозможно сделать, потому что он заблокирован. Я пробовал много разных вещей, чтобы разблокировать/выпустить файл mdb, и иногда это работает. Но в определенном 100% воспроизводимом сценарии его нельзя разблокировать. У нас есть 2 глобальные переменные соединения oledb, которые мы повторно используем везде, для эффективности и чтобы избежать однократных соединений везде. И эти две переменные соединения полезны, когда мы хотим ЗАКРЫТЬ соединения, чтобы мы могли удалить файл mdb.

Вот моя функция (которая обычно работает, но не в этом 1 случае), чтобы принудительно закрыть/освободить 2 соединения oledb из нашего приложения winforms:

public static void CloseOleDBConnections(bool forceReleaseAll = false) {
    if ( DCGlobals.Connection1 != null )
       DCGlobals.Connection1.Close();

    if ( DCGlobals.Connection2 != null )
       DCGlobals.Connection2.Close();

    if ( forceReleaseAll ) {
       DCGlobals.Connection1.Dispose();
       DCGlobals.Connection2.Dispose();
       OleDbConnection.ReleaseObjectPool();
       GC.Collect(GC.MaxGeneration);
       GC.WaitForPendingFinalizers();
    }
}

Я передаю true в вышеуказанную функцию.

Небольшая просьба: пожалуйста, не тратьте время на слова о том, что Access — это плохо, не масштабируется и т. д. Я знаю, что это плохо и не масштабируемо, но это устаревшее требование, и пока мы придерживаемся его. БЛАГОДАРЮ.

Еще одна мысль: конечно, мое приложение winforms знает обо всех открытых oledbconnections. Нет ли способа сказать С#, чтобы найти и перебрать все открытые соединения? Когда я закрываю/выхожу из своего приложения - poof - открытое соединение с mdb освобождается, и я могу удалить файл. Итак, что-то в .net знает о соединении и знает, как его разблокировать — так как же я могу использовать ту же логику, не выходя из приложения?

Есть идеи?


person HerrimanCoder    schedule 16.11.2016    source источник
comment
Просто используйте одно место для создания всех соединений, тогда у вас будут все соединения, используемые в вашем приложении, в одном месте.   -  person Fabio    schedule 16.11.2016
comment
Теперь вы видите опасность наличия глобальных связей. Идиоматический способ использования одноразовых ресурсов состоит в том, чтобы создавать их, использовать и избавляться от них.   -  person D Stanley    schedule 16.11.2016
comment
Другая идея (лучше) всегда создавать новое соединение (экземпляр OleDbConnection) для некоторого запроса и сразу же удалять его после завершения работы с ним.   -  person Fabio    schedule 16.11.2016
comment
Тем не менее, вы, кажется, знаете, как закрыть соединения, поэтому я не понимаю, в чем проблема. Каков сценарий, в котором это не работает?   -  person D Stanley    schedule 16.11.2016
comment
Фабио: мы создаем эти 2 объекта соединения в 1 месте, 1 центральную функцию. Стэнли: Изначально мы пошли по пути создания/использования/удаления, но мы запускаем сотни тысяч SQL-команд в определенных стеках вызовов, и при этом производительность ужасна. Стэнли: как уже упоминалось, то, как мы закрываем, обычно работает, но в определенной функции это не так. Не знаю, почему. Таким образом этот пост.   -  person HerrimanCoder    schedule 16.11.2016
comment
Я не понимаю, в чем проблема. Эта функция выдает ошибки? Вы уверены, что не открываете никакое другое соединение? Если этот метод не выдает ошибок, то есть вероятность, что где-то похоронено, что соединение открывается, а затем забывается. В конце концов, вы сами сказали, что приложение большое и сложное. РЕДАКТИРОВАТЬ: также вы можете попробовать поиграть со счетчиками производительности, я уверен, что есть один, который скажет вам, сколько у вас открытых соединений.   -  person s.m.    schedule 16.11.2016
comment
re: другая мысль. Вы видели этот вопрос?   -  person Gord Thompson    schedule 16.11.2016
comment
Кроме того, если шаги репликации включают отображение каких-либо форм, есть ли в этих формах какие-либо автоматически созданные объекты, такие как DataSource или TableAdapter, которые могут открывать свои собственные соединения?   -  person Gord Thompson    schedule 16.11.2016
comment
Можете ли вы добавить код, иллюстрирующий их использование в этом некоем 100% воспроизводимом сценарии. Проблема явно не в этом маленьком фрагменте, а в том, как используются объекты.   -  person Ňɏssa Pøngjǣrdenlarp    schedule 13.12.2016
comment
Поскольку вы управляете своими соединениями, вам не нужен неявный пул соединений. Dispose просто возвращает соединение обратно в пул (во-первых, неясно, почему вы не получаете выгоду от объединения). Я предлагаю вам отключить объединение и добавить, например, OLE DB Services=-2 в строку подключения, как описано здесь: blogs.msdn.microsoft.com/selvar/2007/11/10/   -  person Simon Mourier    schedule 13.12.2016
comment
Я пробую это (службы OLE DB = -2) и отчитаюсь. Спасибо Саймон.   -  person HerrimanCoder    schedule 13.12.2016
comment
Я был бы удивлен, если бы отключение объединения имело какой-либо эффект. Конны в пуле неактивны и доступны для использования приложением. Это неиспользуемые, у которых будет замок ... но мы не знаем этого кода.   -  person Ňɏssa Pøngjǣrdenlarp    schedule 15.12.2016
comment
@Plutonix - преимущество объединения заключается именно в том, чтобы поддерживать физические соединения в течение определенного периода времени, потому что установление соединения в целом обходится дорого. Удаление соединения не закрывает физическое соединение, если включено объединение.   -  person Simon Mourier    schedule 16.12.2016
comment
Отключение объединения не дало никакого эффекта.   -  person HerrimanCoder    schedule 16.12.2016
comment
Это грязно, но если только в качестве теста вы пытались просто удалить файл блокировки? Вы увидите, что он создан при блокировке *.laccdb   -  person Paul Zahra    schedule 16.12.2016
comment
Также убедитесь, что есть открытые соединения, которые вызывают проблему, а не что-то еще stackoverflow.com/questions/11876202/   -  person Paul Zahra    schedule 16.12.2016
comment
Также еще одна возможная ошибка: какую аутентификацию вы используете? Как правило, пул соединений создается для каждого варианта строки подключения, но я считаю, что если вы используете встроенную безопасность, а обычная или Windows-аутентификация приведет к созданию нового пула соединений для каждого пользователя.   -  person Paul Zahra    schedule 16.12.2016
comment
Вы пытались установить для DCGlobals.Connection1 и DCGlobals.Connection2 значение null после удаления (но до сбора GC)? Кроме того, возможно ли, что есть проблема с тайм-аутом? Что происходит в вашем специальном сценарии через несколько секунд после запуска CloseOleDBConnections? через 10 секунд? через 20 секунд? минута? Это каждый работает через некоторое время?   -  person JuanR    schedule 19.12.2016


Ответы (3)


Удалены IDataReaders?

Вы правильно отключаете все объекты IDataReader? Они могут помешать правильному закрытию соединения.

Решение для отслеживания

В любом случае нужно как минимум лучше отслеживать все свои связи. Звучит как очень большой проект. Вы должны быть абсолютно уверены, что все соединения удалены.

<сильный>1. Новый объект TrackedOleDbConnection

Создайте объект TrackedOleDbConnection, который наследуется от OleDbConnection, но добавляет статический ConcurrentList с именем StillOpen. Когда TrackedOleDbConnection создан, добавьте его в список, когда он будет удален (переопределите эту функцию), удалите его.

public class TrackedOleDbConnection: OleDbConnection
{
    public TrackedOleDbConnection() : base()
    {
    }

    public TrackedOleDbConnection(string ConnectionString) : base(ConnectionString)
    {
    }

    //You don't need to create a constructor for every overload of the baseclass, only for overloads your project uses
    ConcurrentList<TrackedOleDbConnection> ActiveConnections = new ConcurrentList<TrackedOleDbConnection>();
    void AddActiveConnection()
    {
        ActiveConnections.Add(this);
    }

    override void Dispose()
    {
        ActiveConnections.RemoveIfExists(this); //Pseudo-function
        GC.SuppressFinalise(this);
    }

    //Destructor, to ensure the ActiveConnection is always removed, if Dispose wasn't called
    ~TrackedOleDbConnection()
    {
        //TODO: You should log when this function runs, so you know you still have missing Dispose calls in your code, and then find and add them.
        Dispose();
    }
}

<сильный>2. Больше не ссылайтесь напрямую на OleDbConnection

Затем выполните простой поиск и замену в своем решении, чтобы использовать TrackedOleDbConnection.

Затем, наконец, во время вашей функции CloseOleDBConnections вы можете получить доступ к TrackedOleDbConnection.StillOpen, чтобы увидеть, есть ли у вас где-то проблема с неотслеживаемым соединением.

Везде, где вы обнаружите такие неотслеживаемые проблемы, не используйте единственные центральные ссылки, а вместо этого using убедитесь, что ваше соединение правильно удалено.

person Todd    schedule 17.12.2016

Наверное, если единственное, что вам нужно, это скопировать файл, вероятно, нет необходимости возиться с соединениями. Пожалуйста, взгляните на это:

https://www.raymond.cc/blog/copy-locked-file-in-use-with-hobocopy/

person a-man    schedule 17.12.2016

Весьма вероятно, что ADOX не освобождает соединение с базой данных. Убедитесь, что вы:

  • явно вызвать «Закрыть» объекты подключения ADOX
  • вызовите «Утилизировать» их
  • вызов System.Runtime.InteropServices.Marshal.FinalReleaseComObject(db.ActiveConnection);
  • вызов System.Runtime.InteropServices.Marshal.Marshal.FinalReleaseComObject(db);
  • установите для них значение Nothing/null

Кроме того, когда что-то вызывает close для дескриптора файла, запрос на закрытие ставится в очередь для обработки ядром. Другими словами, даже закрытие простого файла не происходит мгновенно. Для этого вам, возможно, придется установить цикл с ограничением по времени, чтобы проверить, удален ли файл .LDB... хотя это в конечном итоге потребует от пользователя ожидания. Ищите любую другую альтернативу этому подходу, хотя в прошлом это было необходимо для других форматов/подключений IME.

person Paul Bruce    schedule 19.12.2016