【问题标题】:What is causing this DatabaseFileLockedException when trying to open a db4o database in an ASP.NET MVC app?尝试在 ASP.NET MVC 应用程序中打开 db4o 数据库时导致此 DatabaseFileLockedException 的原因是什么?
【发布时间】:2010-08-08 19:05:00
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 你会发布你的代码吗?
  • 我的 HttpModule 代码与我在问题中链接到的示例完全相同。
  • 只是猜测......也许它是数据库文件的路径。您可以尝试指定完整路径而不是相对路径吗?快速浏览一下我们的代码异常处理看起来有点理智,但我不排除抛出错误异常的可能性。我也建议尝试使用您正在使用的路径打开一个 ObjectContainer,什么都不做。
  • 谢谢卡尔。我将代码更改为使用绝对路径,但仍然遇到相同的异常。但是,我暂时删除了 HttpModule(在 web.config 中注释掉)并按照您所说的做了,直接从我的控制器 Index 中打开一个 IObjectContainer 操作(具有相同的绝对路径),并且效果很好 - 我可以读取和写入数据没有问题。因此,示例 HttpModule 代码似乎存在问题,尽管我不确定是什么。有什么建议吗?

标签: c# asp.net-mvc db4o


【解决方案1】:

这是示例代码中的错误。它假定 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,它肯定只被调用一次。

【讨论】:

  • +1 谢谢,这是有道理的。另外一件事:在我做出您建议的更改后,第一个异常消失了,但随后我开始在 CloseSession 方法中获得 NullReferenceException。我围绕方法代码添加了一个空测试(我已经在您的答案中编辑了代码,因此它也反映了我的更改),现在一切似乎都运行良好。您能否解释一下为什么会发生那个异常,以便我更好地理解这一点?
  • 它已经在 close-method 中检查 null 了吗?你还在那里检查什么 null ?
  • 它检查 null now 因为我编辑了你的答案;我只是想更清楚地了解这些事件何时被触发。不过没问题,反正现在一切都很好,谢谢。
【解决方案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);
}

希望对你有帮助

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 好主意,但不幸的是这不是文件权限问题。我可以毫无问题地成功打开并读取同一文件夹中的文本文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-30
    相关资源
    最近更新 更多