【问题标题】:How to close all existing connections to a DB programmatically如何以编程方式关闭与数据库的所有现有连接
【发布时间】:2015-07-17 12:30:24
【问题描述】:

我想关闭与 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()

【问题讨论】:

  • 您是否连接到数据库 :-),我希望您需要连接到数据库所在的 sql 服务器实例的主数据库才能运行和更改数据库命令。另见stackoverflow.com/questions/7197574/…
  • 你为什么要给.Open()打两次电话?
  • 如果你使用实体框架,你为什么使用SqlConnection
  • 现在只有在你应该得到一个不同的错误时才会调用 open 。我会拿走所有的开放,看看你得到什么错误。无论如何,您在此问题上发布的错误不是关闭连接的直接问题,而是执行查询的问题。如果您输入SELECT * FROM TABLE_NAME(其中 TABLE_NAME 是数据库中的一个表),您会遇到同样的问题。从简单开始,让它发挥作用,然后尝试增加复杂性。
  • 您能否恢复代码 sn-p 中的编辑?由于人们(像我一样)会寻找与他们相似的问题,这会带来混乱。如果我马上看到你曾经有 2 个 Open() 语句,我可以跳过这个问题。您不必删除错误即可显示所有修复,答案会显示修复。

标签: c# sql-server database entity-framework restore


【解决方案1】:

似乎 Entity Framework 保持与数据库的连接。您可以在 SQL Server Management Studio 中看到它正在执行 sp_who2,其中实体框架在 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);

【讨论】:

  • 这是 SQL 语句的另一个选项,当我发布我的答案时,我不知道我脑海中的语法。感谢您发布此内容,这是 OP 可以尝试的一个很好的附加选项。
  • 如果有人正在寻找服务器的命名空间:Microsoft.SqlServer.Management.Smo
【解决方案2】:

当您在连接上调用 Open() 两次时,您会收到该错误。您应该在 using 块内创建所有 SqlConnection 对象,并且只打开一次。

如果您正在重用连接“以使其更快”,.NET 已经默认通过 Connection Pooling 为您执行此操作,但您必须处理连接对象才能使其工作。

【讨论】:

  • 请多解释一下。我尝试清除连接池。但这对我没有帮助。
  • 您没有清除连接池,您的错误来自您的两个 Open() 调用。现在您摆脱了第二个 Open() ,您可能会遇到不同的错误。您应该回滚您的更改并针对您遇到的新错误提出一个新问题(如果您遇到相同的错误,您的程序中有另一个地方您调用 Open 两次,请检查堆栈跟踪。)
【解决方案3】:

您需要配置阅读器、命令和连接。你的读者没有被处置。这段代码 sn -p 会保证即使在读取过程中抛出异常也会关闭连接。

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

【讨论】:

  • 对不起,我认为这不是我想要的。
  • 这甚至没有理解 OP 的要求。是的,您应该这样做,但是当您想要恢复数据库或对其进行更改时需要它处于脱机状态时,处理您的连接并不能解决问题。这不是这个问题的答案。
【解决方案4】:

错误很明显...这样使用 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

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

【讨论】:

    【解决方案5】:

    您的第一个问题(现在您已经发布了代码)是您调用 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
    

    一旦你从 MASTER 对需要重置的数据库执行了上述 SQL,你应该可以做任何你需要做的事情。记住,连接到主人!如果您连接到您尝试重置的数据库,您最终会关闭所有连接,包括您自己,这将无法正常工作!

    将您的目录更改为主目录。

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

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

    还要确保您使用的 SQL 用户拥有完全掌握权限。您可以通过打开 management studio 并查看 master 下的 users 集合来做到这一点。

    【讨论】:

    • 更多细节会很棒...如果您打开 SQL Mgmt Studio 并运行该确切语句,它不会关闭所有连接吗?
    • 看到您发布了代码。 conn.Open() 然后 Conn.Open() 就是你爆炸的原因。修复使用我的查询,它会工作。
    • 在 SQL Mgmt Studio 中它会关闭所有连接。但是当我用 c# 执行它时,它并不像预期的那样。
    • 你在连接master吗?您将目录更改为 Master,并且您必须确保您使用的数据库用户具有连接到 master 的权限。如果这两件事都得到了正确解决,我今天早上验证了它会起作用。
    【解决方案6】:

    在尝试打开连接之前检查连接是否打开是一种很好的做法。在尝试打开连接之前尝试添加检查,如下所示:

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

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

    【讨论】:

    • 这甚至没有理解 OP 的要求。是的,您应该这样做,但是当您想要恢复数据库或对其进行更改时需要它处于脱机状态时,处理您的连接并不能解决问题。这不是这个问题的答案。
    【解决方案7】:

    您可以使用SqlConnection.ClearAllPoolsSqlConnection.ClearPool 从.NET 中关闭所有或一个连接。

    ClearPool 清除与连接关联的连接池。如果在调用时与 connection 关联的其他连接正在使用中,则它们会被适当地标记并在对其调用 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();
    

    【讨论】:

    • 所以现在使用 try/final 块。并通过连接代码设置尝试,例如打开、执行、添加参数等。在 finally 块中设置关闭代码以确保连接已经关闭!
    【解决方案8】:

    这样考试后,这是我的数据访问层示例:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-16
      • 2014-06-05
      • 2021-05-26
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      相关资源
      最近更新 更多