Как программно закрыть все существующие подключения к БД

Я хочу закрыть существующие подключения к SQL Server, чтобы я мог выполнить восстановление в этой базе данных. Я использую Entity framework. Я попытался выполнить

alter database YourDb 
set single_user with rollback immediate

но тогда я получаю исключение, говоря, что

подключение не было закрыто

Я не могу понять, почему соединения не допускается закрывать?

Это изображение показывает полное исключение

enter image description here

это метод,

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

    }