EntityException: базовый поставщик не удалось открыть. Может ли один сервер, закрывающий соединение с БД, вызвать сбой при открытии другого сервера? - PullRequest
1 голос
/ 19 февраля 2020

Я испытываю ошибки подключения к базе данных с приложением ASP. NET, написанным на VB, работающем на трех серверах IIS. Основной базой данных является MS Access, который находится на общем сетевом устройстве. Он использует Entity Framework, первую реализацию кода и JetEntityFrameworkProvider.

Приложение работает стабильно. Но примерно 1 из 1000 попыток открыть соединение с базой данных завершается неудачно с одной из следующих двух ошибок:

06:33:50   DbContext  "Failed to open connection at 2/12/2020 6:33:50 AM +00:00 with error: 
Cannot open database ''.  It may not be a database that your application recognizes, or the file may be corrupt.

или

14:04:39   DbContext  "Failed to open connection at 2/13/2020 2:04:39 PM +00:00 with error: 
Could not use ''; file already in use.

Через одну секунду с обновлением (F5 ), ошибка исчезла и снова работает.

Подробная информация о среде и используемом коде.

Строка подключения

<add name="DbContext" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=x:\thedatabase.mdb;Jet OLEDB:Database Password=xx;OLE DB Services=-4;" providerName="JetEntityFrameworkProvider" />

Управление DbContext

Приложение использует свойство publi c для доступа к DbContext. DbContext хранится в коллекции HttpContext.Current.Items в течение всего срока действия запроса и располагается в его конце.

Public Shared ReadOnly Property Instance() As DbContext
    Get
        SyncLock obj
            If Not HttpContext.Current.Items.Contains("DbContext") Then
                HttpContext.Current.Items.Item("DbContext") = New DbContext()
            End If

            Return HttpContext.Current.Items.Item("DbContext")
        End SyncLock
    End Get
End Property

BasePage в единицах и располагает DbContext.

Protected Overrides Sub OnInit(e As EventArgs)
    MyBase.OnInit(e)
    DbContext = Data.DbContext.Instance
    ...
End Sub

Protected Overrides Sub OnUnload(e As EventArgs)
    MyBase.OnUnload(e)
    If DbContext IsNot Nothing Then DbContext.Dispose()
End Sub

То, что я пробовал

Многие из вопросов по SO, которые касаются вышеупомянутых сообщений об ошибках, обычно касаются невозможности установить sh соединение к базе данных - они не могут подключиться вообще. В этом случае все по-другому. Соединение работает в 99,99% случаев.

Кроме того, я проверил:

  • Разрешения: Полный доступ предоставляется для общего ресурса, где .mdb (база данных) и .ldb ( файл блокировки).
  • Сетевое подключение: нет проблем с подключением к общему устройству; это соединение Gigabit LAN
  • Максимальное число одновременных подключений не достигло 255
  • Максимальный размер базы данных не превышен (дБ имеет только 5 МБ)
  • Изменен параметр компиляции с «Любой процессор» - «x86», как предложено в этом MS Dev- Net post

Цитата: Я получаю то же самое «Не могу открыть базу данных» ошибка, но совершенно случайно (казалось). Файл MDB был меньше 1 МБ, поэтому с этой ошибкой не было проблем с лимитом в 2 ГБ, как уже упоминалось. Он работал на 100% на 32-битных версиях windows, но я обнаружил, что проблемы были на 64-битных установках. Приложение компилировалось как «Любой процессор». Я изменил параметр компиляции с «Любой процессор» на «x86», и проблема исчезла.

Пока ничего не помогло.

Чтобы собрать больше информации, я прикрепил регистратор Nlog к DbContext, который записывает все действия базы данных и запросы в файл журнала.

Shared Log As Logger = LogManager.GetLogger("DbContext")
Me.Database.Log = Sub(s) Log.Debug(s)

Исследуя журналы, я выяснил, что когда один из вышеуказанные ошибки произошли на одном сервере, другой сервер (всего 3) закрыл соединение с БД в одно и то же время. Вот два примера, которые соответствуют вышеуказанным ошибкам:

06:33:50   DbContext  "Closed connection at 2/12/2020 6:33:50 AM +00:00
14:04:39   DbContext  "Closed connection at 2/13/2020 2:04:39 PM +00:00

Предположение

Когда все соединения DbContext были закрыты, соответствующая запись удаляется из. LDB файл блокировки. При открытии соединения с БД запись будет добавлена ​​в файл блокировки. Когда эти два события происходят в одно и то же время на двух разных серверах, возникает конфликт записи в файл блокировки .ldb, что приводит к появлению указанных выше ошибок.

Вопрос

Может кто-нибудь подтвердить или доказать, что это неправильно? Кто-нибудь испытывал такое поведение? Может быть, я что-то упускаю. Буду признателен за ваш вклад и опыт в этом вопросе.

Если мое предположение верно, решение может заключаться в использовании вспомогательного класса для доступа к БД, который перехватывает и обрабатывает эту ошибку, ожидая минимального периода времени и пытаясь снова.

Но это кажется неправильным. Поэтому я также открыт для предложений по «правильному» решению.

РЕДАКТИРОВАТЬ:"Правильное" решение будет использовать сервер СУБД (как указано в комментариях ниже). Я знаю об этом. На данный момент, я должен иметь дело с этой ошибкой проекта, не будучи ответственным за это. Кроме того, я не могу изменить это в краткосрочной перспективе.

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

Я пишу это как ответ из-за недостатка места, но это не совсем ответ.

Это точно проблема поставщика OleDb.
Я думаю, что это проблема совместного использования. Вы можете сделать несколько попыток:

  • использовать более новый поставщик OleDb вместо Microsoft.Jet.OLEDB.4.0. (если у вас есть попытка 64 бит, вы уже можете попробовать другой провайдер, потому что Jet.OLEDB.4.0 только 32 бит) ваши тесты это, вероятно, не ваш случай. Я думаю, что Dispose не всегда работает должным образом на соединениях Jet.OLEDB.4.0. Я заметил это на тестах и ​​решил это с помощью другого механизма тестирования. Прежде чем сдаться, я использовал этот фрагмент кода
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); GC.WaitForPendingFinalizers();
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
    Как вы понимаете, читая этот код, они являются попытками, и последнее решение изменило механизм тестирования.
  • Если ваше приложение не слишком занято, вы можете попытаться заблокировать БД, используя другой механизм (например, используя файл блокировки). На самом деле это не отличается от new DbContext() повторов.

  • В конце 90-х я помню, что у меня была проблема, связанная с ОС для совместного использования дисков (я использовал Novel Netware). На самом деле у меня нет опыта использования файлов MDB на сетевом ресурсе. Вы можете попытаться переместить mdb в папку с общим доступом Windows
  • На самом деле я использую базы данных Access только для тестов. Если вам действительно нужно использовать одну файловую базу данных, вы можете попробовать другие решения: SQL Lite (вам нужна библиотека, также написанная мной, чтобы сначала применить код https://www.nuget.org/packages/System.Data.SQLite.EF6.Migrations/) или SQL Сервер CE
  • Использование сервера СУБД. Это наверняка лучшее решение. Как автор JetEntityFrameworkProvider, я думаю, что однофайловые базы данных отлично подходят для однопользовательских приложений (для этих приложений я предлагаю SQL Lite), для тестов (я думаю, что для тестов JetEntityFrameworkProvider отлично), для передачи данных или также для приложений только для чтения. В остальных случаях используйте сервер СУБД. Как вы знаете, с EF вы можете без труда перейти с JetEntityFrameworkProvider на SQL Сервер или на MySql.
0 голосов
/ 19 февраля 2020

Вы ошиблись на этапе проектирования: ядро ​​базы данных MS Access не подходит для ASP. Net сайтов, и это явно указано в нескольких местах, например, официальная страница загрузки под подробностями .

Распространяемый Access Database Engine 2016 не предназначен ... Используется ... программой, вызываемой из серверного веб-приложения, такого как ASP. NET

Если вам действительно нужно работать с базой данных Access, вы можете запустить вспомогательный класс, который повторяет попытку в случае распространенных ошибок. Но я не рекомендую это.

Правильное решение здесь использует другую СУБД, которая демонстрирует поведение без сохранения состояния. Я рекомендую SQL Сервер Express, который имеет ограничения, но если вы превысите их, вы будете намного выше того, что поддерживает Access, и не будете вызывать ошибки, подобные этой.

...