2

我正在使用 db4o 作为数据存储,使用 ASP.NET MVC 2 构建一个小型 Web 应用程序。

我已经添加了一个 HttpModule(根据此处的示例)来让应用程序访问 db4o 数据库,并且在我的 VS2008 ASP.NET 开发服务器下的开发机器上一切正常。

但是,当我将应用程序部署到我的网络主机并尝试访问它时,我DatabaseFileLockedException在 HttpModule 尝试打开数据库文件的那一行得到一个。但是应该没有其他东西可以访问该文件;实际上,在应用程序的第一次运行时,它只会在抛出此异常时才被创建。

Web 主机的服务器在 Windows Server 2008 上运行 IIS 7,并且应用程序在完全信任下运行。它是一个子应用程序,以防万一。

我无法弄清楚为什么在实时服务器上发生此错误,但在我的开发服务器本地却没有。谁能帮助我或建议我接下来应该做什么?

4

3 回答 3

3

这是示例代码中的错误。它假设 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
    }
}

作为替代方案,您可以使用 Global.apsx 中的 Application_Start,它肯定只调用一次。

于 2010-08-09T08:48:01.367 回答
2

你这里还有一个问题。

当 AppPools 重新启动时,旧 AppPool 正在完成请求而新 AppPool 正在为新请求提供服务时可能会发生重叠。

在此期间,您将有两个进程试图访问同一个 db4o 文件

为了解决这个问题,您可以使用下面的 hack 之类的东西。

注意使用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);
}

希望这可以帮助

于 2010-09-30T07:02:23.950 回答
0

如果它适用于开发,听起来像是权限问题。将记事本文件粘贴在同一目录中,并尝试使用一些基本文件代码打开它。我打赌你也会有同样的问题。

于 2010-08-09T02:35:35.763 回答