C# 在使用池时,如何测量SqlConnection上的登录时间?

C# 在使用池时,如何测量SqlConnection上的登录时间?,c#,sql-server,sqlconnection,sqlclient,C#,Sql Server,Sqlconnection,Sqlclient,我想捕获一些关于使用SqlConnection的应用程序的统计信息,特别是它在物理上登录服务器的时间 简单的方法如下: using (SqlConnection connection = ...) { Stopwatch loginTimer = Stopwatch.StartNew(); connection.Open() loginTimer.Stop(); } 问题是我也在使用连接池,不想关闭它。因此,我的指标会出现偏差,因为对.Open()的大多数调用实际上只是

我想捕获一些关于使用SqlConnection的应用程序的统计信息,特别是它在物理上登录服务器的时间

简单的方法如下:

using (SqlConnection connection = ...)
{
    Stopwatch loginTimer = Stopwatch.StartNew();
    connection.Open()
    loginTimer.Stop();
}
问题是我也在使用连接池,不想关闭它。因此,我的指标会出现偏差,因为对
.Open()
的大多数调用实际上只是从池中获取一个现有的、打开的物理连接,因此我将看到:

00:00:01.39
00:00:00.02
00:00:00.02
00:00:00.02
...
该应用程序使用了足够多的连接,目标是SqlAzure,因此我确实希望看到物理登录足够频繁

在尝试之前,我已尝试测试连接:

if (sqlConnection.State != ConnectionState.Open)
{
    // Time and call .Open()
}
不幸的是,逻辑SqlConnection不能反映物理连接的状态,因此总是执行
if


我知道可以创建自己的连接池,从中提取并执行连接,但从不关闭,因此我可以通过逻辑连接的状态跟踪物理连接的实际状态,但我真的不希望这样做。

可能可以使用ClientConnectionId属性跟踪sql连接。 它在每次创建新的物理连接时重新生成,并在从池返回连接时保留。但它只能从.NET4.5开始使用

另一种可能是只跳过部分连接的连接池, 只是为了测量物理时间,为其他连接保留池

例如,您可以使用静态计数器,它将不断递增。
对于每个可被10整除的值,您可以将Pooling='false'添加到连接字符串中,以跳过将其添加到池中。这将打开新的连接,您可以测量物理时间。

用一些具体代码扩展Alexander的答案

在对所有连接使用池的限制范围内,可以通过跟踪
SqlConnection.ClientConnectionId
为每个物理登录生成的
SqlClient
值来测量物理登录服务器/数据库所花费的时间。下面是这次测量和报告的扩展方法示例。要使用它,请将
SqlConnection.Open()
的调用更改为
SqlConnection.Login(out openTime)
,如果发生登录,则结果为true,否则为false

internal static class SqlConnectionExtension
{
    private static readonly PropertyInfo _clientConnectionIdPropertyInfo = typeof(SqlConnection).GetProperty("ClientConnectionId");
    private static readonly HashSet<Guid> _clientConnectionIds = new HashSet<Guid>();

    /// <summary>
    /// Method that calls <see cref="SqlConnection.Open()"/>, measuring the time it takes.
    /// </summary>
    /// <param name="sqlConnection">The <see cref="SqlConnection"/> to open.</param>
    /// <param name="openTime">The total time that the call to <see cref="SqlConnection.Open()"/> took.</param>
    /// <returns>True if a login took place; false if a connection was returned from a connection pool.</returns>
    public static bool Login(this SqlConnection sqlConnection, out TimeSpan openTime)
    {
        Stopwatch loginTimer = Stopwatch.StartNew();
        sqlConnection.Open();
        loginTimer.Stop();

        openTime = loginTimer.Elapsed;

    #if NET_4_0_3
        Guid clientConnectionId = sqlConnection.ClientConnectionId;
    #else
        Guid clientConnectionId = Guid.Empty;
        if (_clientConnectionIdPropertyInfo != null)
        {
            clientConnectionId = (Guid)_clientConnectionIdPropertyInfo.GetValue(sqlConnection, null);
        }
    #endif
        if (clientConnectionId != Guid.Empty && !_clientConnectionIds.Contains(clientConnectionId))
        {
            lock (_clientConnectionIds)
            {
                if (_clientConnectionIds.Add(clientConnectionId))
                {
                    return true;
                }
            }
        }
        return false;
    }
}
内部静态类SqlConnectionExtension
{
私有静态只读属性info\u clientConnectionIdPropertyInfo=typeof(SqlConnection).GetProperty(“ClientConnectionId”);
私有静态只读哈希集_clientconnectionId=new HashSet();
/// 
///方法调用,测量所需时间。
/// 
///门打开了。
///呼叫所用的总时间。
///如果发生登录,则为True;如果从连接池返回连接,则为false。
公共静态bool登录(此SqlConnection SqlConnection,out TimeSpan openTime)
{
Stopwatch LoginTime=Stopwatch.StartNew();
sqlConnection.Open();
loginTimer.Stop();
openTime=loginTimer.appeased;
#如果网络4\u 0\u 3
Guid clientConnectionId=sqlConnection.clientConnectionId;
#否则
Guid clientConnectionId=Guid.Empty;
如果(_ClientConnectiondPropertyInfo!=null)
{
clientConnectionId=(Guid)\ u clientConnectionIdPropertyInfo.GetValue(sqlConnection,null);
}
#恩迪夫
if(clientConnectionId!=Guid.Empty&!\u clientConnectionId.Contains(clientConnectionId))
{
锁定(_客户端连接ID)
{
if(_clientConnectionId.Add(clientConnectionId))
{
返回true;
}
}
}
返回false;
}
}

在我自己的环境中,我们仍然使用VS2010,并且不是所有的客户端都有4.0.3多目标包,因此
#if NET_4_0_3
部分。

因为它只在最长时间的Open()调用上物理登录,为什么不忽略所有最短的时间呢?保持平均次数,低于平均次数的就丢弃。