Как программно закрыть все существующие подключения к БД
Я хочу закрыть существующие подключения к SQL Server, чтобы я мог выполнить восстановление в этой базе данных. Я использую Entity framework. Я попытался выполнить
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();
}
Edit: Я снял первый .Открытый.)( Теперь у меня только Open ()
8 ответов
Вы получаете эту ошибку, когда вы называете Open()
на связь дважды. Вы должны сделать все объекты SqlConnection, которые вы создаете внутри using
блоки и открывать их только один раз.
Если вы повторно используете соединения", чтобы сделать его быстрее " .NET уже делает это по умолчанию для вас через Объединение Подключений но вы должны избавиться от объекта подключения, чтобы заставить его работать.
Кажется, что Entity Framework сохраняет соединение с базой данных. Вы можете видеть, что он выполняет sp_who2
в SQL Server Management Studio, где Entity Framework указан как 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())
{
....
}
}
}
ошибка довольно ясно...используя Linq таким образом, вы не можете закрыть соединение, которое вы сейчас используете. Я не пробовал это, но я думаю, что следующее сработает...попробуйте создать хранимую процедуру в базе данных и запустить ее в коде C# с помощью TableAdapter или SqlCommand(вы также можете использовать 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
теперь вы можете запустить это сохраненное процедура из кода, и она убьет открытые соединения даже ваши собственные. Наслаждайтесь!
ваша первая проблема (теперь, когда вы опубликовали свой код) - вы дважды вызываете open:
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
после того, как вы выполнили вышеуказанный SQL от MASTER против базы данных, нуждающейся в сбросе, вы должны хорошо делать все, что вам нужно делать. Помните, подключиться к master!! Если вы подключаетесь к базе данных, которую вы пытаетесь сбросить, вы закрываете все соединения, включая себя, которые не будут работать!
измените каталог на master.
Пример Строки Подключения (из MSDN):
"Persist Security Info=False;Integrated Security=true;Initial Catalog=Master;server=(local)"
также убедитесь, что пользователь SQL, который вы используете, имеет полные разрешения на освоение. Для этого откройте management studio и просмотрите коллекцию пользователей в разделе master.
рекомендуется проверить, открыто ли соединение, прежде чем пытаться его открыть. Попробуйте добавить чек перед попыткой открыть соединение, что-то вроде этого:
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();
}
это поможет предотвратить описанную вами проблему.
можно использовать SqlConnection.ClearAllPools и SqlConnection.ClearPool чтобы закрыть все или одно соединение .Сеть.
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();
после экзамена таким образом, это мои образцы слоя доступа к данным:
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();
}
}