关闭连接后无法删除仅文件的SQL Server Express数据库

时间:2011-08-14 14:46:53

标签: c# file-io connection-string sql-server-express connection-pooling

这是一个完整的程序,它创建一个SQL Server Express数据库并将其从本地实例中删除,留下一个仅供文件使用的数据库。玩完之后,我想删除它。

Main方法开始时,我可以成功删除先前运行的db文件。

但是,如果我尝试在Main方法的末尾删除它们,则操作将失败并显示

  

进程无法访问文件'c:\ temp.mdf',因为它正在存在   被另一个过程使用。

根据http://blogs.msdn.com/b/sqlexpress/archive/2008/02/22/sql-express-behaviors-idle-time-resources-usage-auto-close-and-user-instances.aspx,对于SQL Server Express的默认设置为AUTO_CLOSE,在闲置300ms后,SQL Server Express应该释放对该文件的访问权限,但似乎没有发生这种情况。

有谁知道我怎么能让它工作,所以我可以自己清理?

TIA

using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace ConsoleApplication3 {
class Program {

    private const string ConnectionStringToFile = @"Data Source=.\SqlExpress;Integrated Security=True;AttachDbFileName={0};User Instance=True";
    private const string ConnectionStringToTempDb = @"Data Source=.\SqlExpress;Initial Catalog=TempDb;Integrated Security=True;User Instance=True;";

    private const string CreateDbSql = "CREATE DATABASE {0} ON PRIMARY (NAME='{0}', FILENAME='{1}');";
    private const string DetachDbSql = "EXEC sp_detach_db '{0}', 'true';";

    private static SqlConnection GetConnection(string connectionString) {
        var conn = new SqlConnection(connectionString);
        Debug.WriteLine("Created", "Connection");
        Debug.Indent();
        conn.StateChange += ConnectionStateChange;
        conn.InfoMessage += ConnectionInfoMessage;
        conn.Disposed += ConnectionDisposed;
        return conn;
    }

    private static void ConnectionDisposed(object sender, EventArgs e) {
        SqlConnection conn = (SqlConnection)sender;
        conn.StateChange -= ConnectionStateChange;
        conn.InfoMessage += ConnectionInfoMessage;
        conn.Disposed += ConnectionDisposed;
        Debug.Unindent();
        Debug.WriteLine("Disposed", "Connection");
    }

    private static void ConnectionInfoMessage(object sender, SqlInfoMessageEventArgs e) {
        Debug.WriteLine("InfoMessage: " + e.Message, "Connection");
    }

    private static void ConnectionStateChange(object sender, System.Data.StateChangeEventArgs e) {
        Debug.WriteLine("StateChange: from " + e.OriginalState + " to " + e.CurrentState, "Connection");
    }

    static void Main() {

        const string DbName = "temp";
        const string DbPath = "c:\\temp.mdf";
        const string DbLogFile = "c:\\temp_log.ldf";

        if (File.Exists(DbPath)) File.Delete(DbPath);
        if (File.Exists(DbLogFile)) File.Delete(DbLogFile);

        using (var conn = GetConnection(ConnectionStringToTempDb)) {
            conn.Open();
            using (var command = conn.CreateCommand()) {
                command.CommandText = string.Format(CreateDbSql, DbName, DbPath);
                command.ExecuteNonQuery();
                command.CommandText = string.Format(DetachDbSql, DbName);
                Debug.WriteLine("Detach result: " + command.ExecuteScalar(), "Database"); 
            }
        }

        using (var conn = GetConnection(string.Format(ConnectionStringToFile, DbPath))) {
            conn.Open();
            using (var command = conn.CreateCommand()) {
                command.CommandText = "PRINT 'Successfully connected to database.'";
                command.ExecuteNonQuery();
                command.CommandText = "CREATE TABLE temp (temp int)";
                command.ExecuteNonQuery();
                command.CommandText = "INSERT temp VALUES (1);";
                command.ExecuteNonQuery();
            }
        }

        // takes 300ms apparently: http://blogs.msdn.com/b/sqlexpress/archive/2008/02/22/sql-express-behaviors-idle-time-resources-usage-auto-close-and-user-instances.aspx
        Thread.Sleep(1000);
        if (File.Exists(DbPath)) File.Delete(DbPath);
        if (File.Exists(DbLogFile)) File.Delete(DbLogFile);
    }
}
}

2 个答案:

答案 0 :(得分:2)

我在搜索文档后找到了答案。在关闭与数据库的最后一个连接之前(或为此目的创建一个连接),使用SqlConnection.ClearPool(SqlConnection connection)清除连接池,如下所示:

using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace ConsoleApplication3 {
  class Program {

    private const string ConnectionStringToFile = @"Data Source=.\SqlExpress;Integrated Security=True;AttachDbFileName={0};User Instance=True";
    private const string ConnectionStringToTempDb = @"Data Source=.\SqlExpress;Initial Catalog=TempDb;Integrated Security=True;User Instance=True;";

    private const string CreateDbSql = "CREATE DATABASE {0} ON PRIMARY (NAME='{0}', FILENAME='{1}');";
    private const string DetachDbSql = "EXEC sp_detach_db '{0}', 'true';";

    private static SqlConnection GetConnection(string connectionString) {
      var conn = new SqlConnection(connectionString);
      Debug.WriteLine("Created", "Connection");
      Debug.Indent();
      conn.StateChange += ConnectionStateChange;
      conn.InfoMessage += ConnectionInfoMessage;
      conn.Disposed += ConnectionDisposed;
      return conn;
    }

    private static void ConnectionDisposed(object sender, EventArgs e) {
      SqlConnection conn = (SqlConnection)sender;
      conn.StateChange -= ConnectionStateChange;
      conn.InfoMessage += ConnectionInfoMessage;
      conn.Disposed += ConnectionDisposed;
      Debug.Unindent();
      Debug.WriteLine("Disposed", "Connection");
    }

    private static void ConnectionInfoMessage(object sender, SqlInfoMessageEventArgs e) {  
      Debug.WriteLine("InfoMessage: " + e.Message, "Connection");
    }

    private static void ConnectionStateChange(object sender, System.Data.StateChangeEventArgs e) {
      Debug.WriteLine("StateChange: from " + e.OriginalState + " to " + e.CurrentState, "Connection");
    }

    static void Main() {

      const string DbName = "temp";
      const string DbPath = "c:\\temp.mdf";
      const string DbLogFile = "c:\\temp_log.ldf";

      using (var conn = GetConnection(ConnectionStringToTempDb)) {
        conn.Open();
        using (var command = conn.CreateCommand()) {
          command.CommandText = string.Format(CreateDbSql, DbName, DbPath);
          command.ExecuteNonQuery();
          command.CommandText = string.Format(DetachDbSql, DbName);
          Debug.WriteLine("Detach result: " + command.ExecuteScalar(), "Database"); 
        }
      }

      using (var conn = GetConnection(string.Format(ConnectionStringToFile, DbPath))) {
        conn.Open();
        using (var command = conn.CreateCommand()) {
          command.CommandText = "PRINT 'Successfully connected to database.'";
          command.ExecuteNonQuery();
          command.CommandText = "CREATE TABLE temp (temp int)";
          command.ExecuteNonQuery();
          command.CommandText = "INSERT temp VALUES (1);";
          command.ExecuteNonQuery();
        }
        SqlConnection.ClearPool(conn);
      }

      // SqlExpress takes 300ms to go idle:
      // http://blogs.msdn.com/b/sqlexpress/archive/2008/02/22/sql-express-behaviors-idle-time-resources-usage-auto-close-and-user-instances.aspx
      Thread.Sleep(500); // wait for 500ms just in case (seems to work with 300 though).
      if (File.Exists(DbPath)) File.Delete(DbPath);
      if (File.Exists(DbLogFile)) File.Delete(DbLogFile);
    }
  }
}

答案 1 :(得分:0)

在deattaching之前,您可以尝试将数据库移动到单用户模式:

ALTER DATABASE [dbname] SET SINGLE_USER WITH ROLLBACK IMMEDIATE

此命令会立即关闭所有用户连接。 编辑:此命令应该在分离之前添加到DetachDbSql变量中。