/
SqlProfiler.cs
117 lines (104 loc) · 3.89 KB
/
SqlProfiler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#if !NETCORE
using System;
using System.Collections.Concurrent;
using System.Data.Common;
using System.Linq;
using ServiceStack.MiniProfiler.Data;
namespace ServiceStack.MiniProfiler
{
/// <summary>
/// Contains helper code to time sql statements.
/// </summary>
public class SqlProfiler
{
ConcurrentDictionary<Tuple<object, ExecuteType>, SqlTiming> inProgress = new ConcurrentDictionary<Tuple<object, ExecuteType>, SqlTiming>();
ConcurrentDictionary<DbDataReader, SqlTiming> inProgressReaders = new ConcurrentDictionary<DbDataReader, SqlTiming>();
/// <summary>
/// The profiling session this SqlProfiler is part of.
/// </summary>
public MiniProfiler Profiler { get; private set; }
/// <summary>
/// Returns a new SqlProfiler to be used in the 'profiler' session.
/// </summary>
public SqlProfiler(MiniProfiler profiler)
{
Profiler = profiler;
}
/// <summary>
/// Tracks when 'command' is started.
/// </summary>
public void ExecuteStartImpl(DbCommand command, ExecuteType type)
{
var id = Tuple.Create((object)command, type);
var sqlTiming = new SqlTiming(command, type, Profiler);
inProgress[id] = sqlTiming;
}
/// <summary>
/// Returns all currently open commands on this connection
/// </summary>
public SqlTiming[] GetInProgressCommands()
{
return inProgress.Values.OrderBy(x => x.StartMilliseconds).ToArray();
}
/// <summary>
/// Finishes profiling for 'command', recording durations.
/// </summary>
public void ExecuteFinishImpl(DbCommand command, ExecuteType type, DbDataReader reader = null)
{
var id = Tuple.Create((object)command, type);
var current = inProgress[id];
current.ExecutionComplete(isReader: reader != null);
SqlTiming ignore;
inProgress.TryRemove(id, out ignore);
if (reader != null)
{
inProgressReaders[reader] = current;
}
}
/// <summary>
/// Called when 'reader' finishes its iterations and is closed.
/// </summary>
public void ReaderFinishedImpl(DbDataReader reader)
{
SqlTiming stat;
// this reader may have been disposed/closed by reader code, not by our using()
if (inProgressReaders.TryGetValue(reader, out stat))
{
stat.ReaderFetchComplete();
SqlTiming ignore;
inProgressReaders.TryRemove(reader, out ignore);
}
}
}
/// <summary>
/// Helper methods that allow operation on SqlProfilers, regardless of their instantiation.
/// </summary>
public static class SqlProfilerExtensions
{
/// <summary>
/// Tracks when 'command' is started.
/// </summary>
public static void ExecuteStart(this SqlProfiler sqlProfiler, DbCommand command, ExecuteType type)
{
if (sqlProfiler == null) return;
sqlProfiler.ExecuteStartImpl(command, type);
}
/// <summary>
/// Finishes profiling for 'command', recording durations.
/// </summary>
public static void ExecuteFinish(this SqlProfiler sqlProfiler, DbCommand command, ExecuteType type, DbDataReader reader = null)
{
if (sqlProfiler == null) return;
sqlProfiler.ExecuteFinishImpl(command, type, reader);
}
/// <summary>
/// Called when 'reader' finishes its iterations and is closed.
/// </summary>
public static void ReaderFinish(this SqlProfiler sqlProfiler, DbDataReader reader)
{
if (sqlProfiler == null) return;
sqlProfiler.ReaderFinishedImpl(reader);
}
}
}
#endif