LocalDB何时解锁mdf文件?

时间:2016-03-06 19:15:20

标签: entity-framework integration-testing localdb

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

此时如果我尝试从文件系统中删除temp 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。

谢谢, 克里斯

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

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

3 个答案:

答案 0 :(得分:2)

以为我会发布我的解决方案......

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

  1. sqlexpress的VS2015版本将实例命名为“MSSQLLocalDB”而不是“v11.0”。
  2. 如果您的文件路径名很长(就像我一样),SQLExpress将内部数据库名称设置为GUID,后跟数据库文件路径的最右侧部分,,因此运行SET OFFLINE和db_detach数据库名称等于本地db文件路径的命令(如示例中所示)将无效
  3. 以下是使用Entity Framework上下文分离和删除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个数据库(!),没有一个会起作用,因为他们的文件都被删除了!这对我来说似乎有些怪异,事实上,当dbs附加到一个按需启动和停止的SQL实例时,我能够删除文件,这似乎有点傻。现在我理解它,它是有道理的,它适用于我正在使用它的东西,但我从来没有将它用于开发之外的任何东西。

答案 1 :(得分:0)

这是我的EntityFramework Core 1.0解决方案

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

# get unique score values and unique years
uniqScore = unique(dat$score)
uniqYear = unique(dat$year)
# assuming total number of countries remains constant
totalCountries = length(unique(dat$country))
# empty matrix to store results
store = matrix(NA, length(uniqYear), length(uniqScore))

# loop over unique scores
for (i in 1:length(uniqScore)) {
  # loop over unique years
  for (j in 1:length(uniqYear)) {
    # find the number of observations with a given year and score
    # subsequently divide it by the total number of possible countries
    # to obtain a percentage and save it in store
    store[j, i] = length(dat[dat$year == uniqYear[j] & 
                               dat$score == uniqScore[i], 1]) / 
      totalCountries
  }
}

# plot results
matplot(uniqYear, store, type = 'b', pch = 1:3, lty = 2, bty = 'n', las = 1,
        ylab = 'Percentage', xlab = 'Year')
legend('topright', legend = uniqScore, pch = 1:3, lty = 2, col = 1:3, bty = 'n')

# or to make it into a dataframe
df = data.frame(percentage = c(store), 
                score = rep(uniqScore, each = nrow(store)))

答案 2 :(得分:0)

分离本地数据库.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();
            }
        }
    }