如何以编程方式关闭与数据库的所有现有连接

How to close all existing connections to a DB programmatically

我想关闭与 SQL 服务器的现有连接,以便我可以对该数据库进行恢复。我正在使用 entity framework。我尝试执行

alter database YourDb 
set single_user with rollback immediate

但后来我得到一个例外说

Connection was not closed

我不明白为什么不允许关闭连接?

这张图片显示了完整的例外情况

这是方法,

 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() 两次时,您会收到该错误。您应该使在 using 块内创建的所有 SqlConnection 对象只打开一次。

如果您正在重用连接 "to make it faster" .NET 已经默认通过 Connection Pooling 为您完成此操作,但您必须处理连接对象才能使其正常工作。

你的第一个问题(现在你已经发布了你的代码)是你调用了两次打开:

    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,你应该做好做任何你需要做的事情。记住,连接到 master!!如果你连接到你试图重置的数据库,你最终会关闭所有连接,包括你自己,这将不起作用!

将您的目录更改为 master。

示例连接字符串 (from MSDN):

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

还要确保您正在使用的 SQL 用户拥有完全的掌握权限。您可以通过打开管理工作室并查看 master 下的用户集合来执行此操作。

错误很明显...以这种方式使用 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

现在您可以从代码中 运行 这个存储过程,它会终止打开的连接,甚至是您自己的连接。享受吧!

您可以使用 SqlConnection.ClearAllPools and SqlConnection.ClearPool 关闭来自 .NET 的所有或一个连接。

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

Entity Framework 似乎确实保持了与数据库的连接。您可以看到它在 SQL Server Management Studio 中执行 sp_who2,其中 Entity Framework 在 ProgramName.

下列为 EntityFrameworkMUE

虽然您不必使用"raw" 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);

以这种方式考试后,这是我的数据访问层示例:

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

    }

您需要配置reader、命令和连接。您的 reader 未处理。此代码片段将保证连接关闭,即使在读取过程中抛出异常也是如此。

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

最好在尝试打开连接之前检查连接是否打开。在尝试打开连接之前尝试添加检查,如下所示:

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


}

这将有助于防止您描述的问题。