Невозможно удалить только файловую базу данных SQL Server Express после закрытия соединения - PullRequest
1 голос
/ 14 августа 2011

Вот полная программа, которая создает базу данных 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, с AUTO_CLOSE, как по умолчанию для SQL Server Express, после 300 мс бездействия 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 ]

2 голосов
/ 15 августа 2011

Я нашел ответ после просмотра документации.Непосредственно перед закрытием последнего соединения с базой данных (или создания его для этой цели) очистите пул соединений, используя 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);
    }
  }
}
0 голосов
/ 15 августа 2011

Перед отключением вы можете попробовать перевести базу данных в однопользовательский режим:

ALTER DATABASE [dbname] SET SINGLE_USER WITH ROLLBACK IMMEDIATE

эта команда немедленно закрывает все пользовательские подключения. РЕДАКТИРОВАТЬ: эта команда должна быть добавлена ​​в вашу переменную DetachDbSql ДО отсоединения.

...