Comment fermer par programme toutes les connections existantes à une database

Je veux fermer les connections existantes à un server SQL afin que je puisse effectuer une restauration sur cette database. J'utilise le entity framework. J'ai essayé d'exécuter

alter database YourDb set single_user with rollback immediate 

mais ensuite je reçois une exception en disant que

La connection n'a pas été fermée

Je ne peux pas comprendre pourquoi les connections ne sont pas autorisés à fermer?

Cette image montre l'exception complète

entrez la description de l'image ici

c'est la méthode,

  public void dbQueueryExctr(ssortingng 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(); } 

Edit: j'ai supprimé le premier .Open (). Maintenant, j'ai seulement Open ()

Vous obtenez cette erreur lorsque vous appelez Open() sur une connection deux fois. Vous devez créer tous les objects SqlConnection que vous créez à l' using blocs et ne les ouvrir qu'une seule fois.

Si vous réutilisez des connections «pour le rendre plus rapide», NET le fait déjà par défaut via Connection Pooling mais vous devez disposer de l'object connection pour le faire fonctionner.

Il semble que Entity Framework conserve une connection à la database. Vous pouvez voir qu'il exécute sp_who2 dans SQL Server Management Studio où Entity Framework est répertorié comme EntityFrameworkMUE sous ProgramName.

Vous n'avez pas besoin d'utiliser des instructions sql "raw" pour déconnecter les connections actives, cela peut aussi être résolu de la façon suivante:

 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); 

Vous devez disposer du lecteur, de la command et de la connection. Votre lecteur n'est pas disposé. Cet extrait de code garantit que la connection est fermée même si des exceptions sont levées pendant le process de lecture.

 using (var conn = new SqlConnection("...")) { conn.Open(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "Command text....."; using (var reader = cmd.ExecuteReader()) { .... } } } 

L'erreur est assez claire … en utilisant Linq de cette façon, vous ne pouvez pas fermer la connection sur laquelle vous êtes actuellement. Je n'ai pas essayé cela mais je pense que ce qui suit fonctionnerait … essayez de créer une procédure stockée dans votre database et exécutez-la dans votre code C # en utilisant un TableAdapter ou SqlCommand (vous pouvez toujours utiliser Linq). Votre code ne saura pas que vous êtes sur le point d'exécuter une procédure stockée sur le sharepoint l'interrompre, cela devrait donc fonctionner.

 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 

Maintenant, vous pouvez exécuter cette procédure stockée à partir du code et il va tuer les connections ouvertes même les vôtres. Prendre plaisir!

Votre premier problème (maintenant que vous avez posté votre code) est que vous appelez ouvert deux fois:

  public void dbQueueryExctr(ssortingng 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(); } 

Ensuite, votre problème vous requestra de réinitialiser la database pour forcer la fermeture de toutes les connections. Vous devrez utiliser une string de connection séparée pour vous connecter à MASTER et non à la database à laquelle vous essayez de fermer toutes les connections.

  alter database <data base> set offline with rollback immediate alter database <data base> set online with rollback immediate 

Une fois que vous avez exécuté le SQL ci-dessus de MASTER contre la database qui a besoin d'être réinitialisée, vous devriez faire ce que vous devez faire. Rappelez-vous, connectez-vous au maître !! Si vous vous connectez à la database que vous essayez de réinitialiser vous finissez par fermer toutes les connections, y compris vous-même, ce qui ne fonctionnera pas!

Changez votre catalogue en master.

Exemple de string de connection (à partir de MSDN ):

 "Persist Security Info=False;Integrated Security=true;Initial Catalog=Master;server=(local)" 

Assurez-vous également que l'user SQL que vous utilisez dispose des permissions complètes à maîsortingser. Vous faites ceci en ouvrant le studio de gestion et en regardant la collection d'users sous le maître.

Il est recommandé de vérifier si la connection est ouverte avant d'essayer de l'ouvrir. Essayez d'append une vérification avant d'essayer d'ouvrir votre connection, quelque chose comme ceci:

 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(); } 

Cela aidera à prévenir le problème que vous avez décrit.

Vous pouvez utiliser SqlConnection.ClearAllPools et SqlConnection.ClearPool pour fermer tout ou une connection à partir de .NET.

ClearPool efface le pool de connections associé à la connection. Si des connections supplémentaires associées à la connection sont utilisées au moment de l'appel, elles sont marquées de manière appropriée et sont supprimées (au lieu d'être renvoyées au pool) lorsque Close est appelé.

ClearAllPools réinitialise (ou vide) le pool de connections. S'il existe des connections utilisées au moment de l'appel, elles sont marquées de manière appropriée et seront rejetées (au lieu d'être renvoyées dans le pool) lorsque Close est appelé.

pour des exemples:

 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(); 

Une fois l'examen effectué de cette façon, voici mes exemples de couches d'access aux données:

  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(); } }