В чем причина этого исключения DatabaseFileLockedException при попытке открыть базу данных db4o в приложении ASP.NET MVC? - PullRequest
2 голосов
/ 08 августа 2010

Я создаю небольшое веб-приложение с ASP.NET MVC 2, используя db4o в качестве хранилища данных.

Я добавил HttpModule - согласно приведенному здесь примеру - чтобы датьдоступ приложения к базе данных db4o, и все прекрасно работает на моей машине разработки под сервером разработки ASP.NET VS2008.

Однако, когда я развертываю приложение на своем веб-хосте и пытаюсь получить к нему доступ, яполучите DatabaseFileLockedException в строке, где HttpModule пытается открыть файл базы данных.Но больше ничего не должно быть доступ к файлу;действительно, при первом запуске приложения оно будет создано только тогда, когда возникнет это исключение.

Серверы веб-хоста работают под управлением IIS 7 в Windows Server 2008, а приложение работает в режиме полного доверия.Это вспомогательное приложение, на случай, если что-то изменится.

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

Ответы [ 3 ]

3 голосов
/ 09 августа 2010

Это ошибка в примере кода. Предполагается, что HttpModule.Init вызывается только один раз, что не всегда верно. В зависимости от того, как настроено ваше приложение, оно может вызываться несколько раз. Чтобы это исправить, проверьте в HttpModule-Handler, если экземпляр уже существует:

using System;
using System.Configuration;
using System.Web;
using Db4objects.Db4o;

namespace Db4oDoc.WebApp.Infrastructure
{
    public class Db4oProvider : IHttpModule
    {
        private const string DataBaseInstance = "db4o-database-instance";
        private const string SessionKey = "db4o-session";

        // #example: open database when the application starts
        public void Init(HttpApplication context)
        {
            if (null==HttpContext.Current.Application[DataBaseInstance])
            {
                HttpContext.Current.Application[DataBaseInstance] = OpenDatabase();
            }
            RegisterSessionCreation(context);
        }

        private IEmbeddedObjectContainer OpenDatabase()
        {
            string relativePath = ConfigurationSettings.AppSettings["DatabaseFileName"];
            string filePath = HttpContext.Current.Server.MapPath(relativePath);
            return Db4oEmbedded.OpenFile(filePath);
        }
        // #end example

        // #example: close the database when the application shuts down
        public void Dispose()
        {
            IDisposable toDispose = HttpContext.Current.Application[DataBaseInstance] as IDisposable;
            if (null != toDispose)
            {
                toDispose.Dispose();
            }
        }
        // #end example

        // #example: provide access to the database
        public static IObjectContainer Database
        {
            get { return (IObjectContainer)HttpContext.Current.Items[SessionKey]; }
        }
        // #end example

        // #example: A object container per request
        private void RegisterSessionCreation(HttpApplication httpApplication)
        {
            httpApplication.BeginRequest += OpenSession;
            httpApplication.EndRequest += CloseSession;
        }

        private void OpenSession(object sender, EventArgs e)
        {
            IEmbeddedObjectContainer container =
                (IEmbeddedObjectContainer)HttpContext.Current.Application[DataBaseInstance];
            IObjectContainer session = container.OpenSession();
            HttpContext.Current.Items[SessionKey] = session;
        }

        private void CloseSession(object sender, EventArgs e)
        {
            if (HttpContext.Current.Items[SessionKey] != null)
            {
                IObjectContainer session = (IObjectContainer)HttpContext.Current.Items[SessionKey];
                session.Dispose();
            }
        }
        // #end example
    }
}

В качестве альтернативы вы можете использовать Application_Start из Global.apsx, который вызывается только один раз.

2 голосов
/ 30 сентября 2010

У вас здесь другая проблема.

При перезапуске AppPools может возникнуть совпадение, когда старый AppPool завершает запрос, а новый AppPool обслуживает новые запросы.

В течение этого времени у вас будет два процесса, пытающихся получить доступ к одному и тому же файлу db4o

Чтобы обойти это, вы можете использовать что-то вроде хака ниже.

Обратите внимание на использование Db4oFactory.OpenServer вместо Db4oEmbedded.OpenFile. Это позволяет использовать транзакции на более мелкозернистой основе.

public IObjectServer OpenServer()
{
    Logger.Debug("Waiting to open db4o server.");
    var attempts = 0;
    do
    {
        try
        {
            return Db4oFactory.OpenServer(fileName, 0);
        }
        catch (DatabaseFileLockedException ex)
        {
            attempts++;
            if (attempts > 10)
            {
                throw new Exception("Couldn't open db4o server. Giving up!", ex);
            }

            Logger.Warn("Couldn't open db4o server. Trying again in 5sec.");
            Thread.Sleep(5.Seconds());
        }
    } while (true);
}

Надеюсь, это поможет

0 голосов
/ 09 августа 2010

Похоже, проблемы с разрешениями, если он работает на dev.Поместите файл блокнота в тот же каталог и попробуйте открыть его с помощью какого-нибудь кода.Могу поспорить, у вас будет такая же проблема.

...