识别SqlConnection对象的底层sql连接

时间:2009-02-20 14:15:48

标签: c# sql-server sqlconnection spid

我可以使用GetHashCode()来识别对象,但有没有办法识别SqlConnection对象获得的实际sql连接?

我(仍在)尝试调试涉及池连接和应用程序角色的问题,如果我能够可靠地识别底层的sql连接,它可以帮助很多。

以下是一些可能说明问题的代码

SqlConnection c = new SqlConnection(myConnString);

c.Open();  // GetHashCode == "X"

c.Close(); // returns connection to pool

c.Open;  // GetHashCode() == "X" but possibly different pooled connection?

当我写这个问题时,我发现我可能想要的是连接的SPID。遗憾的是,由于我正在尝试解决的错误,当SQL连接被删除时,SPID不可用(所以在我最感兴趣的时候,我无法在该连接上运行命令来获取SPID)

还有其他好主意吗?

6 个答案:

答案 0 :(得分:2)

@Ed Guiness,

(我知道这是一个旧线程,但为了将来可能需要这个的人的利益)

; tldr回答:并非没有一些代码更改(主要是非破坏和合理的更改恕我直言)。

非常奇怪的是,一旦连接对象被打开,Microsoft就不会公开SPID。我可以看到例如SQL版本名称和其他SQL Server特定属性,我可以从SqlConnection对象而不是SPID获取。

详细说明:

为每个SQLConnection分配一个SPID。这是使用T-SQL Command @@ SPID获得的,但这在SQL Server端执行。 Trick将把它与SQL Server端正在完成的主要工作一起传递,并在C#端读取它。

需要SPID的四种可能方案。

  1. 您正在执行返回结果集的存储过程(减去spid)
  2. 您正在执行INS / UPD / DEL存储过程。
  3. 您正在从ADO.Net(yikes !!)动态运行Prepared SQL stms进行CRUD操作(即没有存储过程的场景1和2)
  4. 您正在使用SqlBulkCopy
  5. 将数据直接插入表中

    <强> 1。返回结果集的存储过程

    假设您有一个从主db返回Rows的SP(USP_GetDBDetails)。我们需要向Existing SQL Stmt添加一行代码以返回SPID,并使用Paramter类型在C#Side获取它以检索ReturnValue。也可以读取主要的Resultsets。

    存储过程是一件很美好的事情。 他们可以同时返回返回值和结果集以及OutPut参数。在这种情况下,在SP端我们只需要在ADO正在执行的SP末尾添加额外的返回值.Net使用SqlConnection.We执行此操作,如下面的T-SQL代码所示:

    CREATE Procedure [dbo].[USP_GetDBDetails] 
    AS
    BEGIN
    
        SELECT 
                database_id,
                name,
                create_date         
          FROM  [sys].[databases]
    
          Return @@SPID -- Line of Code that needs to be added to return the SPID
    
    END
    

    现在捕获C#端的SPID(根据需要修改连接字符串):

            using (SqlConnection conn = new SqlConnection(@"Data Source=(local);Initial Catalog=master;Persist Security Info=True;Integrated Security =SSPI;"))
            {
    
                string strSql = "USP_GetDBDetails";
    
                SqlCommand sqlcomm = new SqlCommand();
                sqlcomm.CommandText = strSql;
                sqlcomm.CommandType = CommandType.StoredProcedure;
                sqlcomm.Connection = conn;
    
                SqlParameter returnValueParam = sqlcomm.Parameters.Add("@ReturnValue", SqlDbType.Int);
                returnValueParam.Direction = ParameterDirection.ReturnValue;
    
                conn.Open();
    
    **// Reader Section**
                SqlDataReader rdr = sqlcomm.ExecuteReader();
                DataTable dt = new DataTable();
                dt.Load(rdr); // Get the Reultset into a DataTable so we can use it !
                rdr.Close();  // Important to close the reader object before reading the return value.
    
    // Lets get the return value which in this case will be the SPID for this connection.
               string spid_str = returnValueParam.Value.ToString();
               int spid = (int)sqlcomm.Parameters["@ReturnValue"].Value; // Another Way to get the return value.
    
               Console.WriteLine("SPID For this Conn = {0} ", spid);
    
    // To use the Reult Sets that was returned by the SP:
    
            foreach (DataRow dr in dt.Rows)
            {
                string dbName = dr["Name"].ToString();
                // Code to use the Other Columns goes here
    
            }
          }
    

    输出:

    SPID For this Conn = 66
    

    <强> 2。如果Connection对象正在执行处理INS / UPS / DEL

    的SP

    在负责INS / UPD / DEL的SP末尾添加RETURN @@ SPID,就像我们在场景1中所做的那样。

    在C#Side获取SPID ..除了阅读器部分之外,一切都与场景1中的相同。删除“读取器”部分下的4行,并替换为下面的这一行。 (显然,不需要迭代DataTable dt的foreach循环)

    sqlcomm.ExecuteNonQuery();
    

    第3。 INS / UPD / DEL使用内联SQL

    将这些stmts移动到存储过程并按照场景2的步骤操作。可能有办法做一些T-SQL杂技来注入@@ SPID并通过使用MultipleActiveResultSets选项返回它,但不是非常优雅的IMO。

    <强> 4。 SqlBulkCopy的即可。

    这将需要查询表以获取spid。由于没有Stored过程从SqlServer返回可以捕获的SPID。

    我们需要添加一个INT类型的额外列来保存SPID值,如下所示:

    ALTER TABLE dbo.TBL_NAME ADD
        SPID int NOT NULL Default( @@SPID )
    GO
    

    通过执行此操作,SQL Server将自动将SPID值插入新添加的列中。处理BulkCopy的C#ADO端不需要更改代码。典型的Bulkcopy ADO代码如下所示,它应该在上面的ALTER TABLE Stmt之后继续工作。

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
    
                using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
                {
    
                DataTable dt = new DataTable();
                dt.Columns.Add("Col1");
                dt.Columns.Add("Col2");
    
                string[] row = { "Col1Value", "Col2Value" };
    
                dt.Rows.Add(row);
                    bulkCopy.DestinationTableName = "TBL_NAME_GOES_HERE"; //TBL_NAME
    
                    try
                    {
                        // Write from the source to the destination.
                        bulkCopy.WriteToServer(dt);
                    }
                    catch (SqlException ex)
                    {
    
                      // Handle Exception
    
                    }
                }
         }
    

    因此要检查输出,请执行从dbo.TBL_NAME中选择不同的SPID

    多数民众赞成。希望能有所帮助。

答案 1 :(得分:1)

您可以在连接字符串中设置应用程序名称,然后在SQL Server中可以看到。通常它是默认的SQL客户端,但您可以覆盖:

"Integrated Security=true;Initial Catalog=Northwind;server=(local);Application Name=MyKeyword"

此属性可以通过SqlConnection实例的ConnectionString属性读回。

编辑:正如edg所指出的,连接字符串定义了哪个连接池,所以这可能不起作用。

答案 2 :(得分:0)

不是直接的答案,而是你应该注意的事情。对象的哈希码在其整个生命周期内不应更改。如果确实如此,那么你可以将一个对象放入一个哈希集合中,更改它的哈希码然后再无法从集合中检索它。

如果附加调试器并查看私有字段,是否可以将某种内部标识符转换为?如果是这样,您可以在调试期间通过反射访问它,如果必须的话。

答案 3 :(得分:0)

如果我正确理解您的问题,理查德的答案将无法帮助您,因为您正在寻找底层.NET连接池中的实际对象。我也不相信哈希会有所帮助,因为你正在查看基础池。

我本身没有答案,而是一个建议。获取Reflector(现在是RedGate产品)的副本并通过System.Data.DLL查看底层池中的内容存储方式。我不确定它会给你一个快速简单的答案,但如果有任何事情你可以反思以获得帮助调试你的问题的答案,它将会在那里。

顺便说一下,你想解决的问题是什么?

答案 4 :(得分:0)

你可以尝试的一件事是

SqlConnection.ClearPool();

SqlConnection.ClearAllPools();

看看你是否可以隔离你面临的任何问题。

答案 5 :(得分:0)

不是说这是不可能的,但我还没有找到任何办法。