在单元测试之间恢复数据库:数据库仍在使用中

本文关键字:数据库 恢复 单元测试 之间 | 更新日期: 2023-09-27 18:29:07

我们得到了一些使用SQL Server数据库的单元测试。为了使至少每个测试夹具都是唯一的并且独立于其他测试夹具,我尝试在每个测试夹具开始之前恢复数据库。每个测试在其例程中打开和关闭连接和数据库。

在第一个测试夹具之前恢复数据库非常有效,但在第二个测试夹具(打开和关闭连接和数据库之后)之前恢复数据库则不然。

我概括并孤立了这个问题。以下是两个示例测试(NewUnitTest1将首先执行):

using NUnit.Framework;
using System.Data;
using System.Data.SqlClient;
    [TestFixture]
    class UnitTest1 : BaseTest
    {
        [Test]
        public void NewUnitTest1()
        {
            string conString = ConnectionStringHelper.GetConnectionString(SCADADatabases.ConfigurationDatabase); // Helper method to optain connection string, is correct
            using (SqlConnection dbConn = new SqlConnection(conString))
            {
                SqlCommand cmd = dbConn.CreateCommand();
                cmd.CommandText = "SELECT * FROM TB_PV";
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet();
                da.Fill(ds);
            } // Dispose should close connection and database ...
            Assert.IsTrue(true);
        }
    }
    [TestFixture]
    class UnitTest2 : BaseTest
    {
        [Test]
        public void NewUnitTest2()
        {
            Assert.IsTrue(true);
        }
    }

基础测试类将在每个测试夹具之前完成恢复工作:

using My.Core.Helper;
using My.Core.UnitTest.Properties;
using My.DBRestore.Core;
using My.Domain;
using Microsoft.SqlServer.Management.Smo;
using NUnit.Framework;
using System;
using System.Data;
using System.IO;
    /// <summary>
    /// Base test class any test class can inherit from. 
    /// </summary>
    public abstract partial class BaseTest
    {
        /// <summary>
        /// Executes before all tests of this class start. 
        /// </summary>
        [TestFixtureSetUp]
        public virtual void FixtureSetUp()
        {
            Console.WriteLine("Recover database ... ");
            restoreDatabase("MYDATABASE", @"D:'MYBACKUP.BAK", "");
            Console.WriteLine("Run tests in " + this.GetType() + " ...");
        }
        private void restoreDatabase(string destinationDatabase, string backupFile, string dbPath)
        {
            Microsoft.SqlServer.Management.Smo.Server sqlServer = new Microsoft.SqlServer.Management.Smo.Server(Properties.Settings.Default.SQLInstance);
            Microsoft.SqlServer.Management.Smo.Restore restore = new Microsoft.SqlServer.Management.Smo.Restore();
            restore.Action = Microsoft.SqlServer.Management.Smo.RestoreActionType.Database;
            restore.Devices.Add(new Microsoft.SqlServer.Management.Smo.BackupDeviceItem(backupFile, Microsoft.SqlServer.Management.Smo.DeviceType.File));
            System.Data.DataTable dt = restore.ReadBackupHeader(sqlServer);
            restore.FileNumber = Convert.ToInt32(dt.Rows[dt.Rows.Count - 1]["Position"]);
            dt = restore.ReadFileList(sqlServer);
            int indexMdf = dt.Rows.Count - 2;
            int indexLdf = dt.Rows.Count - 1;
            Microsoft.SqlServer.Management.Smo.RelocateFile dataFile = new Microsoft.SqlServer.Management.Smo.RelocateFile();
            string mdf = dt.Rows[indexMdf][1].ToString();
            dataFile.LogicalFileName = dt.Rows[indexMdf][0].ToString();
            dataFile.PhysicalFileName = Path.Combine(dbPath, destinationDatabase + Path.GetExtension(mdf));

            Microsoft.SqlServer.Management.Smo.RelocateFile logFile = new Microsoft.SqlServer.Management.Smo.RelocateFile();
            string ldf = dt.Rows[indexLdf][1].ToString();
            logFile.LogicalFileName = dt.Rows[indexLdf][0].ToString();
            logFile.PhysicalFileName = Path.Combine(dbPath, destinationDatabase + Path.GetExtension(ldf));
            restore.RelocateFiles.Add(dataFile);
            restore.RelocateFiles.Add(logFile);
            restore.Database = destinationDatabase;
            restore.ReplaceDatabase = true;
            restore.SqlRestore(sqlServer); // <- this is where the FailedOperationException is thrown on the second execution
        }
}

如前所述,第一次修复效果很好。FailedOperationException第二次声明:无法独占访问数据库,因为数据库当前正在使用中。RESTORE DATABASE将因错误而停止。(我手动翻译)

我们使用的是最新的NUnit 2版本(2.6.4)。为什么数据库仍在使用,我如何正确地关闭它?

在单元测试之间恢复数据库:数据库仍在使用中

在恢复之前,您需要终止所有连接到数据库的进程:

sqlServer.KillAllProcesses(destinationDatabase)

查看文档以了解更多详细信息。