SQL Serverへの既存の接続を閉じて、そのデータベースで復元を実行できるようにします。私はエンティティフレームワークを使用しています。実行してみました
alter database YourDb
set single_user with rollback immediate
しかし、それから私はそれを言っている例外を受け取ります
接続が閉じられていません
接続が閉じられない理由がわかりませんか?
この画像は完全な例外を示しています
これが方法です、
public void dbQueueryExctr(string queuery)
{
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;
using (SqlConnection connectionx = new SqlConnection(CONNECTIONSTRING))
{
connectionx.Open();
//connectionx.Open(); // Removed
cmd.CommandText = queuery;
cmd.CommandType = CommandType.Text;
cmd.Connection = connectionx;
reader = cmd.ExecuteReader();
connectionx.Close();
}
編集:最初の.Open()を削除しました。今はOpen()しかありません
接続でOpen()
を2回呼び出すと、このエラーが発生します。作成するすべてのSqlConnectionオブジェクトをusing
ブロック内に作成し、それらを1回だけ開く必要があります。
「高速化するために」接続を再利用している場合、.NETはデフォルトで 接続プール を介してすでに接続を再利用していますが、接続オブジェクトを破棄して機能させる必要があります。
EntityFrameworkはデータベースへの接続を維持しているようです。実行されていることがわかりますsp_who2
SQL Server Management Studioで、Entity FrameworkがProgramNameの下にEntityFrameworkMUEとしてリストされています。
ただし、アクティブな接続を切断するために「生の」SQLステートメントを使用する必要はありませんが、次の方法でも解決できます。
Server server = new Server(".\\SQLEXPRESS");
Database database = new Database(server, dbName);
database.Refresh();
server.KillAllProcesses(dbName);
database.DatabaseOptions.UserAccess = DatabaseUserAccess.Single;
database.Alter(TerminationClause.RollbackTransactionsImmediately);
//restore.SqlRestore(server);
リーダー、コマンド、および接続を破棄する必要があります。あなたの読者は処分されません。このコードスニペットは、読み取りプロセス中に例外がスローされた場合でも、接続が閉じられることを保証します。
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "Command text.....";
using (var reader = cmd.ExecuteReader())
{
....
}
}
}
最初の問題(コードを投稿したので)は、openを2回呼び出すことです。
public void dbQueueryExctr(string queuery)
{
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;
using (SqlConnection connectionx = new SqlConnection(CONNECTIONSTRING))
{
//YOU CALL OPEN HERE
//DELETE THIS ONE!!!
connectionx.Open();
cmd.CommandText = queuery;
cmd.CommandType = CommandType.Text;
cmd.Connection = connectionx;
//AND OPEN HERE
connectionx.Open();
reader = cmd.ExecuteReader();
//You do not need connectionx.Close() here
//You have it within a using which will dispose the connection
//upon exiting the using scope.
connectionx.Close();
}
次に、問題が発生すると、データベースをリセットしてすべての接続を強制的に閉じる必要があります。すべての接続を閉じようとしているデータベースではなく、別の接続文字列を使用してMASTERに接続する必要があります。
alter database <data base>
set offline with rollback immediate
alter database <data base>
set online with rollback immediate
リセットが必要なデータベースに対してMASTERから上記のSQLを実行したら、必要なことは何でも実行できます。マスターに接続することを忘れないでください!!リセットしようとしているデータベースに接続すると、自分自身を含むすべての接続が閉じられてしまい、機能しなくなります。
カタログをマスターに変更します。
接続文字列の例( MSDNから ):
"Persist Security Info=False;Integrated Security=true;Initial Catalog=Master;server=(local)"
また、使用しているSQLユーザーがマスターするための完全な権限を持っていることを確認してください。これを行うには、Management Studioを開き、マスターの下のユーザーコレクションを確認します。
エラーはかなり明白です...そのようにLinqを使用すると、現在接続している接続を閉じることができません。私はこれを試していませんが、次のことがうまくいくと思います...データベースにストアドプロシージャを作成し、TableAdapterまたはSqlCommandのいずれかを使用してC#コードで実行してみてください(Linqも使用できます)。コードは、接続を切断しようとしているストアドプロシージャを実行しようとしていることを認識しないため、機能するはずです。
CREATE PROCEDURE [dbo].[sp_KillSpidsByDBName]
@dbname sysname = ''
AS
BEGIN
-- check the input database name
IF DATALENGTH(@dbname) = 0 OR LOWER(@dbname) = 'master' OR LOWER(@dbname) = 'msdb'
RETURN
DECLARE @sql VARCHAR(30)
DECLARE @rowCtr INT
DECLARE @killStmts TABLE (stmt VARCHAR(30))
-- find all the SPIDs for the requested db, and create KILL statements
-- for each of them in the @killStmts table variable
INSERT INTO @killStmts SELECT 'KILL ' + CONVERT (VARCHAR(25), spid)
FROM master..sysprocesses pr
INNER JOIN master..sysdatabases db
ON pr.dbid = db.dbid
WHERE db.name = @dbname
-- iterate through all the rows in @killStmts, executing each statement
SELECT @rowCtr = COUNT(1) FROM @killStmts
WHILE (@rowCtr > 0)
BEGIN
SELECT TOP(1) @sql = stmt FROM @killStmts
EXEC (@sql)
DELETE @killStmts WHERE stmt = @sql
SELECT @rowCtr = COUNT(1) FROM @killStmts
END
END
GO
これで、このストアドプロシージャをコードから実行でき、自分の接続でも開いている接続が強制終了されます。楽しい!
接続を開こうとする前に、接続が開いているかどうかを確認することをお勧めします。接続を開こうとする前に、次のようなチェックを追加してみてください。
using (SqlConnection connectionx = new SqlConnection(CONNECTIONSTRING))
{
if(connectionx.State != ConnectionState.Open
connectionx.Open();
cmd.CommandText = queuery;
cmd.CommandType = CommandType.Text;
cmd.Connection = connectionx;
reader = cmd.ExecuteReader();
connectionx.Close();
}
これにより、説明した問題を防ぐことができます。
この方法で試験すると、これが私のデータアクセス層のサンプルです。
public T ExecuteScalar<T>(SqlCommand cmd, params SqlParameter[] Params)
{
try
{
if (Transaction != null && Transaction != default(SqlTransaction))
cmd.Transaction = Transaction;
else
cmd.Connection = SqlConn;
if (Params != null && Params.Length > 0)
{
foreach (var param in Params)
cmd.Parameters.Add(param);
}
Open();
var retVal = cmd.ExecuteScalar();
if (retVal is T)
return (T)retVal;
else if (retVal == DBNull.Value)
return default(T);
else
throw new Exception("Object returned was of the wrong type.");
}
finally
{
Close();
}
}
SqlConnection.ClearAllPools および SqlConnection.ClearPool を使用して、.NETからのすべてまたは1つの接続を閉じることができます。
ClearPoolは、接続に関連付けられている接続プールをクリアします。接続に関連付けられた追加の接続が呼び出し時に使用されている場合、それらは適切にマークされ、Closeが呼び出されたときに(プールに戻されるのではなく)破棄されます。
ClearAllPoolsは、接続プールをリセット(または空)します。呼び出し時に使用中の接続がある場合、それらは適切にマークされ、Closeが呼び出されたときに(プールに戻されるのではなく)破棄されます。
たとえば:
using(var comm = new SqlConnection())
using(var comExecuteInsert = new SqlCommand())
{
comExecuteInsert.Connection = comm;
comExecuteInsert.CommandType = CommandType.StoredProcedure;
comExecuteInsert.CommandText = strProcedureName;
comExecuteInsert.ExecuteScalar();
comExecuteInsert.Parameters.Clear();
comm.Close();
}
SqlConnection.ClearAllPools();