【问题标题】:When does LocalDB unlock the mdf file?LocalDB 什么时候解锁 mdf 文件?
【发布时间】:2019-06-21 18:31:08
【问题描述】:

问题: 我创建了一个随机名称的 mdf 文件的副本。 我将该名称注入到 EF6 DbContext 使用的连接字符串中。 它打开得很好,我运行查询等,然后我处理上下文。

此时,如果我尝试从文件系统中删除临时 mdf 文件,我将无法删除它;我收到“文件正在被另一个进程使用”错误。

有谁知道在连接关闭时是否可以强制连接解除对 mdf 文件的锁定? 或者当 SqlExpress 引擎释放 localdb 文件锁时?

我试过用这个:

master.ExecuteCommand(@"ALTER DATABASE [{0}] SET OFFLINE WITH ROLLBACK IMMEDIATE", db);
master.ExecuteCommand(@"exec sp_detach_db '{0}'", db);

... 从这里: How to detach a LocalDB (SQL Server Express) file in code ...但它对我不起作用,因为我需要这个:

MultipleActiveResultSets=True

在我的连接字符串中,因此不能通过启用 MultipleActiveResultSets 的连接发出 ALTER DATABASE。

谢谢, 克里斯

背景(因为我知道有人会问):

我已经为我们的集成测试创建了一个框架,其中每个测试都会获取本地数据库的副本。这很好用,所有测试都可以并行运行 - 特别是如果所有 DB 的临时文件夹都是 RAMDisk 文件夹,它的速度非常快。不幸的是,如果我在所有测试运行之后(或在下一个测试开始之前)清理所有数据库,我正在推动 RAMDisk 上的空间限制,所以我希望在每个测试完成后删除每个数据库。看起来只要测试引擎/sqlexpress 引擎正在运行,文件就会保持锁定状态。当它结束时,锁被解除。

【问题讨论】:

  • LocalDB 是 SQL Server Express 的一个版本,它在您的应用程序启动时启动(独立或在 Visual Studio 中)并在您的应用程序停止后终止(通常它“徘徊”一段时间并且仅在几分钟后完全终止 - 为了避免在一分钟左右再次启动应用程序时再次启动)。您还可以使用 SqlLocalDB 实用程序手动停止实例
  • 但我连接到许多不同的本地数据库并将它们的文件锁定在一个应用程序运行中。您是说一旦 SQLExpress 创建了这些锁,就无法在不结束整个进程树的情况下释放这些锁?
  • 正如我所说:如果您想/需要使用 SqlLocalDB 实用程序,您可以手动终止实例 ....
  • 你也可以从 yoir 测试代码中使用这个库:github.com/martincostello/sqllocaldb

标签: entity-framework integration-testing localdb


【解决方案1】:

我想我会发布我的解决方案以备不时之需......

我在我的问题中发布了一个链接的答案确实是答案,但它适用于旧版本的 localdb,因此不适用于我,并且在不了解机制的情况下我没有意识到如何无需挖掘即可修复它。

  1. VS2015 版本的 sqlexpress 将实例命名为“MSSQLLocalDB”而不是“v11.0”。
  2. 如果您的文件有长路径名(如我所做的那样)SQLExpress 将内部数据库名称设置为 GUID,后跟数据库文件路径的最右侧部分,因此运行 SET OFFLINE 和 db_detach数据库名称等于本地 db 文件路径的命令(如示例中所示)将不起作用

这是使用实体框架上下文分离和删除 mdf 文件的代码,无需关闭 localdb\Sqlexpress 实例。您只需要确保与数据库的所有连接都已关闭。 注意:我的 db 文件名最右边的 50 个字符包含一个使名称唯一的 GUID!

    public void CleanupTempLocalDb(DbContext ctx)
    {
        if (ctx != null)
        {
            string dbFilename = new SqlConnectionStringBuilder(ctx.Database.Connection.ConnectionString).AttachDBFilename;

            ctx.Dispose();
            using (var master = new DbContext(@"Data Source=(LocalDB)\MSSQLLocalDB;Initial Catalog=master;MultipleActiveResultSets=False;Integrated Security=True"))
            {
                var results = master.Database.SqlQuery<string>(string.Format("SELECT name from sys.databases where name like '%{0}'"
                        , dbFilename.Substring(dbFilename.Length - 50)));
                string dbName = results.FirstOrDefault();
                if (dbName != null)
                {
                    master.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction,
                        string.Format("ALTER DATABASE [{0}] SET OFFLINE WITH ROLLBACK IMMEDIATE"
                        , dbName));
                    master.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, string.Format("exec sp_detach_db '{0}'"
                        , dbName));
                    System.IO.File.Delete(dbFilename);
                }
            }
        }
    }

...顺便说一句...

好的,所以(显然)我对 LocalDB 工作原理的理解是有限的。我是通过 VS2013 向我的一个项目添加一个 localdb 来介绍它的,并且从未真正考虑过机制。我没有意识到这只是本地sqlexpress的快捷方式;我认为每个附加的 localdb 都是一个完全独立的 SQL 实例——而不仅仅是一个附加到某个机器范围的 SQLExpress 实例的数据库,它有自己的持久主数据库。我从来没有在我的 SQL 对象浏览器中看到过 localdb 实例,因为我习惯于在 SSMS 中使用浏览器,而不是在 VS 中使用浏览器……结果我在服务器浏览器中列出了 400 多个数据库(!),这些都不行,因为他们的文件都被删除了!这对我来说似乎有点古怪,当数据库连接到按需启动和停止的 SQL 实例时,我能够删除文件这一事实似乎有点愚蠢。现在我理解了它,它是有道理的,它适用于我正在使用它的用途,但我永远不会将它用于开发之外的任何事情。

【讨论】:

    【解决方案2】:

    这是我的 EntityFramework Core 1.0 解决方案

    如您所见,数据库名称可以与其完整文件路径一起使用。

    var dbf = fileDlg.FileName;
    var options = new DbContextOptionsBuilder();
    options.UseSqlServer($@"Server=(localdb)\mssqllocaldb;Initial Catalog=master;MultipleActiveResultSets=False;Integrated Security=True");
    using (var master = new DbContext(options.Options))
    {
         master.Database.ExecuteSqlCommand($"ALTER DATABASE [{dbf}] SET OFFLINE WITH ROLLBACK IMMEDIATE");
         master.Database.ExecuteSqlCommand($"exec sp_detach_db '{dbf}'");
                }
    

    【讨论】:

      【解决方案3】:

      分离本地database.mdf的函数。它允许删除文件。

          public void DetachLocalDb(string dbFilename)
          {
              var connectionString = @"Data Source = (LocalDB)\MSSQLLocalDB; Initial Catalog = master; MultipleActiveResultSets = False; Integrated Security = True";
              var dbName = dbFilename.ToUpper();
              var exec1 = $"ALTER DATABASE[{dbName}] SET OFFLINE WITH ROLLBACK IMMEDIATE";
              var exec2 = $"exec sp_detach_db '{dbName}'";
      
              using (var connection = new SqlConnection(connectionString))
              {
                  connection.Open();
      
                  using (var sqlCommand = new SqlCommand(exec1, connection))
                  {
                      sqlCommand.ExecuteNonQuery();
                  }
      
                  using (var sqlCommand = new SqlCommand(exec2, connection))
                  {
                      sqlCommand.ExecuteNonQuery();
                  }
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-04
        • 1970-01-01
        • 2011-09-08
        • 2021-11-29
        相关资源
        最近更新 更多