这是一个完整的程序,它创建一个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);
}
}
}
答案 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变量中。