代码之家  ›  专栏  ›  技术社区  ›  Ed Guiness

标识SQLConnection对象的基础SQL连接

  •  1
  • Ed Guiness  · 技术社区  · 17 年前

    我可以使用gethashcode()来标识一个对象,但是是否有任何方法来标识由sql connection对象获得的实际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 回复  |  直到 10 年前
        1
  •  2
  •   objectNotFound    10 年前

    埃德吉尼斯

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

    ;tldr答案:没有一些代码更改(大多数是不中断的和合理的更改imho)。

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

    细节:

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

    需要SPID的四种可能情况。

    1. 您正在执行一个返回结果集(减去SPID)的存储过程。
    2. 您正在执行INS/UPD/DEL存储过程。
    3. 您正在从ADO.NET(Yikes)动态(内联)运行准备好的SQL STM。!)执行CRUD操作(即没有存储过程的场景1和场景2)
    4. 您正在使用sqlbackcopy将数据直接插入表中

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

    假设您有一个从master db返回行的sp(usp_getdbdetails)。我们需要在现有的SQL stmt中添加一行代码来返回SPID,并使用参数类型来检索返回值,从而在C端获取它。还可以读取主要结果集。

    存储过程很漂亮。 它们可以同时返回返回值、结果集和输出参数。 在这种情况下,在sp端,我们只需要在由ADO.NET使用sqlconnection执行的sp的末尾添加额外的返回值。我们这样做如下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。如果连接对象正在执行处理ins/ups/del的sp

    在负责ins/upd/del的sp末尾添加return@@spid,就像在场景1中那样。

    然后在C边取下套管。除了 阅读器部分 . 删除读卡器部分下的4行,并替换为下面的这一行。(显然,迭代数据表dt的foreach循环是不必要的)

    sqlcomm.ExecuteNonQuery();
    

    三。使用内联SQL的ins/upd/del

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


    4。文件复制 .

    这将需要查询表以获取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
    
                    }
                }
         }
    

    所以要检查输出,请从dbo.tbl_name中选择一个不同的spid

    就是这样。希望能帮助别人。

        2
  •  1
  •   Richard    17 年前

    您可以在连接字符串中设置应用程序名称,然后在SQL Server中可见。通常它是SQL客户机的默认值,但您可以重写:

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

    此属性可由sqlconnection实例的connectionString属性读取。

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

        3
  •  0
  •   Drew Noakes    17 年前

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

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

        4
  •  0
  •   Gregory A Beamer    17 年前

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

    我本身没有答案,只是一个建议。获取Reflector(现在是Redgate产品)的副本,并浏览system.data.dll,查看底层池中存储内容的方式。我不确定它会给你一个快速而简单的答案,但是如果有什么事情你可以反省得到一个答案来帮助调试你的问题,它就会出现。

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

        5
  •  0
  •   yfeldblum    17 年前

    你可以尝试的一件事是

    SqlConnection.ClearPool();
    

    SqlConnection.ClearAllPools();
    

    看看你是否能把你面临的任何问题隔离开来。

        6
  •  0
  •       16 年前

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

    推荐文章