2

我想捕获一些关于使用 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块总是被执行。

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

4

2 回答 2

1

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

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

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

于 2013-03-22T20:54:20.953 回答
0

用一些具体的代码扩展亚历山大的答案。

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

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

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

于 2013-04-03T01:07:20.770 回答