-
Notifications
You must be signed in to change notification settings - Fork 610
Expand file tree
/
Copy pathProfiledDbDataAdapter.cs
More file actions
195 lines (172 loc) · 7.37 KB
/
ProfiledDbDataAdapter.cs
File metadata and controls
195 lines (172 loc) · 7.37 KB
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using System;
using System.Data;
using System.Data.Common;
namespace StackExchange.Profiling.Data
{
/// <summary>
/// Provides a wrapper around a native <see cref="DbDataAdapter"/>, allowing a profiled Fill operation.
/// </summary>
public class ProfiledDbDataAdapter : DbDataAdapter
{
/// <summary>
/// This static variable is simply used as a non-null placeholder in the MiniProfiler.ExecuteFinish method.
/// </summary>
private static readonly DbDataReader TokenReader = new DataTableReader(new DataTable());
private readonly IDbProfiler? _profiler;
private IDbCommand? _selectCommand, _insertCommand, _updateCommand, _deleteCommand;
/// <summary>
/// Gets the underlying adapter. Useful for when APIs can't handle the wrapped adapter (e.g. CommandBuilder).
/// </summary>
public IDbDataAdapter InternalAdapter { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ProfiledDbDataAdapter"/> class.
/// </summary>
/// <param name="wrappedAdapter">The wrapped adapter.</param>
/// <param name="profiler">The profiler.</param>
/// <exception cref="ArgumentNullException">Throws when the <paramref name="wrappedAdapter"/> is <c>null</c>.</exception>
public ProfiledDbDataAdapter(IDbDataAdapter wrappedAdapter, IDbProfiler? profiler = null)
{
InternalAdapter = wrappedAdapter ?? throw new ArgumentNullException(nameof(wrappedAdapter));
_profiler = profiler ?? MiniProfiler.Current;
InitCommands(wrappedAdapter);
}
private void InitCommands(IDbDataAdapter wrappedAdapter)
{
if (wrappedAdapter.SelectCommand != null)
{
_selectCommand = wrappedAdapter.SelectCommand;
}
if (wrappedAdapter.DeleteCommand != null)
{
_deleteCommand = wrappedAdapter.DeleteCommand;
}
if (wrappedAdapter.UpdateCommand != null)
{
_updateCommand = wrappedAdapter.UpdateCommand;
}
if (wrappedAdapter.InsertCommand != null)
{
_insertCommand = wrappedAdapter.InsertCommand;
}
}
/// <inheritdoc cref="DbDataAdapter.FillSchema(DataSet, SchemaType)"/>
public new DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType) => InternalAdapter.FillSchema(dataSet, schemaType);
/// <inheritdoc cref="DbDataAdapter.Fill(DataSet)"/>
public new int Fill(DataSet dataSet)
{
/*
* The SqlDataAdapter type requires that you use a SqlDataCommand for the various adapter commands and will throw an
* exception if you attempt to pass it a ProfiledDbCommand instead. This method is a simple wrapper around the existing
* Fill method and assumes that a single ExecuteReader method will eventually be called on the SelectCommand. This is
* somewhat of a hack but appears to be working to give rough timings.
*
* While I have not tested this with an oracle DataAdapter, I would guess that it works in much the same way as the
* SqlDataAdapter type and would thus work fine with this workaround.
*/
if (_profiler?.IsActive != true || _selectCommand is not DbCommand)
{
return InternalAdapter.Fill(dataSet);
}
int result;
var cmd = (DbCommand)_selectCommand;
_profiler.ExecuteStart(cmd, SqlExecuteType.Reader);
try
{
result = InternalAdapter.Fill(dataSet);
}
catch (Exception e)
{
_profiler.OnError(cmd, SqlExecuteType.Reader, e);
throw;
}
finally
{
_profiler.ExecuteFinish(cmd, SqlExecuteType.Reader, null);
}
return result;
}
/// <inheritdoc cref="DbDataAdapter.Fill(DataTable)"/>
public new int Fill(DataTable dataTable)
{
var dbDataAdapter = InternalAdapter as DbDataAdapter
?? throw new InvalidOperationException("This function is only supported when profiling a DbDataAdapter object. If you are using an adapter which implements IDbDataAdapter but does not inherit from DbDataAdapter then you cannot use this function.");
if (_profiler?.IsActive != true || _selectCommand is not DbCommand)
{
return dbDataAdapter.Fill(dataTable);
}
int result;
var cmd = (DbCommand)_selectCommand;
_profiler.ExecuteStart(cmd, SqlExecuteType.Reader);
try
{
result = dbDataAdapter.Fill(dataTable);
}
catch (Exception e)
{
_profiler.OnError(cmd, SqlExecuteType.Reader, e);
throw;
}
finally
{
_profiler.ExecuteFinish(cmd, SqlExecuteType.Reader, TokenReader);
}
return result;
}
/// <inheritdoc cref="DbDataAdapter.GetFillParameters()"/>
public new IDataParameter[] GetFillParameters() => InternalAdapter.GetFillParameters();
/// <inheritdoc cref="IDataAdapter.MissingMappingAction"/>
public new MissingMappingAction MissingMappingAction
{
get => InternalAdapter.MissingMappingAction;
set => InternalAdapter.MissingMappingAction = value;
}
/// <inheritdoc cref="IDataAdapter.MissingSchemaAction"/>
public new MissingSchemaAction MissingSchemaAction
{
get => InternalAdapter.MissingSchemaAction;
set => InternalAdapter.MissingSchemaAction = value;
}
/// <inheritdoc cref="IDataAdapter.TableMappings"/>
public new ITableMappingCollection TableMappings => InternalAdapter.TableMappings;
/// <inheritdoc cref="DbDataAdapter.SelectCommand"/>
public new IDbCommand? SelectCommand
{
get => _selectCommand;
set
{
_selectCommand = value;
InternalAdapter.SelectCommand = value is ProfiledDbCommand cmd ? cmd.WrappedCommand : value;
}
}
/// <inheritdoc cref="DbDataAdapter.InsertCommand"/>
public new IDbCommand? InsertCommand
{
get => _insertCommand;
set
{
_insertCommand = value;
InternalAdapter.InsertCommand = value is ProfiledDbCommand cmd ? cmd.WrappedCommand : value;
}
}
/// <inheritdoc cref="DbDataAdapter.UpdateCommand"/>
public new IDbCommand? UpdateCommand
{
get => _updateCommand;
set
{
_updateCommand = value;
InternalAdapter.UpdateCommand = value is ProfiledDbCommand cmd ? cmd.WrappedCommand : value;
}
}
/// <inheritdoc cref="DbDataAdapter.DeleteCommand"/>
public new IDbCommand? DeleteCommand
{
get => _deleteCommand;
set
{
_deleteCommand = value;
InternalAdapter.DeleteCommand = value is ProfiledDbCommand cmd ? cmd.WrappedCommand : value;
}
}
}
}