识别SqlConnection对象的底层sql连接

发布于 2024-07-14 03:04:42 字数 530 浏览 5 评论 0原文

我可以使用 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) 。

还有其他好主意吗?

I can use GetHashCode() to identify an object but is there any way to identify the actual sql connection obtained by a SqlConnection object?

I'm (still) trying to debug a problem involving pooled connections and application roles and if I could reliably identify the underlying sql connection it could help a lot.

Here's some code that might illustrate the question

SqlConnection c = new SqlConnection(myConnString);

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

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

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

As I write this question it occurs to me that what I probably want is the SPID of the connection. Sadly, SPID isn't available when the connection is dropped by SQL due to the bug I'm trying to resolve (so at the point I'm most interested in I can't run a command on that connection to obtain the SPID).

Any other bright ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

诺曦 2024-07-21 03:04:42

@Ed Guness,

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

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

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

详细信息:

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

需要 SPID 的四种可能场景。

  1. 您正在执行一个返回结果集(减去 spid)的存储过程
  2. 您正在执行 INS/UPD/DEL 存储过程。
  3. 您正在从 ADO.Net 即时(内联)运行准备好的 SQL stms(哎呀!!)来执行 CRUD 操作(即没有存储过程的场景 1 和 2)
  4. 将数据直接插入到表中

您正在使用 SqlBulkCopy 1 。 返回结果集的存储过程

假设您有一个从主数据库返回行的 SP (USP_GetDBDetails)。 我们需要向现有 SQL Stmt 添加一行代码以返回 SPID,并使用 Paramter 类型在 C# 端获取它以检索 ReturnValue。 还可以读取主要结果集。

存储过程是一件美丽的事情。 它们可以同时返回一个返回值和一个结果集以及一个输出参数。在这种情况下,在 SP 端,我们只需要在 ADO 执行的 SP 末尾添加额外的返回值.Net 使用 SqlConnection。我们按照下面的 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# 端获取 SPID .. 除了阅读器部分之外,一切都与场景 1 中相同。 删除 Reader 部分下的 4 行并替换为下面的这一行。 (显然,迭代 DataTable dt 的 foreach 循环不是必需的)

sqlcomm.ExecuteNonQuery();

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

将这些 stmt 移动到存储过程中并按照场景 2 的步骤进行操作。 可能有一些方法可以执行一些 T-SQL 杂技来注入 @@SPID 并通过使用 MultipleActiveResultSets 选项返回它,但在我看来不是很优雅。


4. SqlBulkCopy。

这将需要查询表来获取 spid。 由于没有存储过程可以从 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

                }
            }
     }

因此,要检查输出,请执行 Select different SPID from dbo.TBL_NAME

就是这样。 希望对某人有帮助。

@Ed Guiness,

(I know this is an old thread but for the benefit of those who might need this in future)

;tldr answer: Not without some code changes ( mostly Non-Breaking and reasonable changes IMHO ).

Its very Strange that Microsoft does not expose the SPID once the connection object has been opened. I can see for example the SQL Version name and other SQL Server specific properties that I can get from the SqlConnection Object but not the SPID.

Details:

Every SQLConnection is assigned one SPID. This is obtained using the T-SQL Command @@SPID but this executes on the SQL Server side. Trick is to pass that along with the main work that is being done on SQL Server side and read it on the C# side.

Four possible scenarios for which SPID is needed.

  1. You Are executing a Stored Procedure that Returns a resultset (minus the spid)
  2. You are doing INS/UPD/DEL a Stored Procedure.
  3. You are running Prepared SQL stms On the fly (inline) from ADO.Net (yikes!!) to do CRUD operations ( i.e Scenarios 1 and 2 without Stored Procedures )
  4. You are inserting Data directly to a Table using SqlBulkCopy

1. Stored Procedure that Returns a resultset

Let say you have a SP (USP_GetDBDetails) that returns Rows from master db. We need to add a line of code to the Existing SQL Stmt to return the SPID and Get it on the C# Side using Paramter type for retrieving ReturnValue. The main Resultsets can also be read.

Stored Procedures are a beautiful thing. They can simultaneously return a return value AND a resultset AND a OutPut Parameter. In this case on the SP Side we only need to add the additional return value at the end of the SP that is being executed by ADO.Net using SqlConnection.We do this as shown in the T-SQL code below:

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

Now to capture the SPID on the C# side (modify connection string as needed) :

        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

        }
      }

Output :

SPID For this Conn = 66

2. If the Connection object is executing A SP that handles INS/UPS/DEL

Add the RETURN @@SPID at the end of the SP that is responsible for INS/UPD/DEL just as we did for Scenario 1.

And on the C# Side to get the SPID .. everything remains same as in Scenario 1 except the reader section. Delete the 4 lines under the Reader Section and replace with this line below. (and obviously the foreach loop to iterate the DataTable dt wont be necessary)

sqlcomm.ExecuteNonQuery();

3. INS/UPD/DEL Using Inline SQL

Move these stmts into a Stored Procedure and follow Steps for Scenario 2 . There might be ways to do some T-SQL acrobatics to inject the @@SPID and return it by perhaps utilizing MultipleActiveResultSets option but not very Elegant IMO.


4. SqlBulkCopy.

This will require querying the table to get the spid. Since there is no Stored procedure to return the SPID from SqlServer that can be captured.

We need to add an extra column of type INT to hold the SPID value like so:

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

By doing this SQL Server will automatically insert the SPID value into the newly added column. No Code change will be necessary on C# ADO side that handles the BulkCopy. A typical Bulkcopy ADO code looks like below and it should continue to work after the ALTER TABLE Stmt above.

        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

                }
            }
     }

So to check the output do a Select distinct SPID from dbo.TBL_NAME

Thats it . Hope that helps somebody.

兔小萌 2024-07-21 03:04:42

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

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

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

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

You could set the application name in the connection string, this is then visible in SQL Server. Normally it is defaulted the the SQL Client, but you can override:

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

This property can be read back by the ConnectionString property of the SqlConnection instance.

Edit: As noted by edg the connection string defines which connection pool, so this probably wouldn't work.

真心难拥有 2024-07-21 03:04:42

并不是说这是不可能的,但我还没有找到任何方法来做到这一点。

Not to say that this is impossible, but I've not yet found any way to do it.

许一世地老天荒 2024-07-21 03:04:42

不是直接答案,但您应该注意一些事情。 对象的哈希码在其整个生命周期中不应更改。 如果是这样,那么您可以将一个对象放入散列集合中,更改它的散列代码,然后无法再次从集合中检索它。

如果你附加一个调试器并查看私有字段,你不能打开某种内部标识符吗? 如果是这样,如果需要,您可以在调试期间通过反射访问它。

Not a direct answer, but something you should note. The hash code of an object shouldn't change throughout its lifetime. If it did, then you could put an object into a hashed collection, change it's hash code then then be unable to retrieve it from the collection again.

If you attach a debugger and view the private fields, can't you turn some kind of internal identifier up? If so, you could access that via reflection during your debugging if you had to.

一直在等你来 2024-07-21 03:04:42

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

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

顺便说一句,您要解决的错误是什么?

Richard's answer will not help you, if I am understanding your issue properly, as you are looking for the actual object in the underlying .NET connection pool. I am also not convinced that the hash will help, as you are looking at the underlying pool.

I do not have an answer, per se, but a suggestion. Get a copy of Reflector (now a RedGate product) and go through System.Data.DLL and look at how things are stored in the underlying pool. I am not sure it will give you a quick and easy answer, but if there is any thing you can reflect on to get an answer to help debug your problem, it is going to be there.

BTW, what is the bug you are trying to solve?

东北女汉子 2024-07-21 03:04:42

您可以尝试的一件事是

SqlConnection.ClearPool();

看看

SqlConnection.ClearAllPools();

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

One thing you could try is

SqlConnection.ClearPool();

or

SqlConnection.ClearAllPools();

to see if you can isolate any of the issues you are facing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文