forked from ServiceStack/ServiceStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProfiledDbCommand.cs
More file actions
130 lines (114 loc) · 4.41 KB
/
ProfiledDbCommand.cs
File metadata and controls
130 lines (114 loc) · 4.41 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
#if !NETSTANDARD2_0
using System;
using System.Data.Common;
using System.Data;
using System.Reflection;
using System.Reflection.Emit;
#pragma warning disable 1591 // xml doc comments warnings
namespace ServiceStack.MiniProfiler.Data
{
public class ProfiledDbCommand : ProfiledCommand, ICloneable
{
private bool bindByName;
/// <summary>
/// If the underlying command supports BindByName, this sets/clears the underlying
/// implementation accordingly. This is required to support OracleCommand from dapper-dot-net
/// </summary>
public bool BindByName
{
get { return bindByName; }
set
{
if (bindByName != value)
{
if (_cmd != null)
{
var inner = GetBindByName(_cmd.GetType());
if (inner != null) inner(_cmd, value);
}
bindByName = value;
}
}
}
static Link<Type, Action<IDbCommand, bool>> bindByNameCache;
static Action<IDbCommand, bool> GetBindByName(Type commandType)
{
if (commandType == null) return null; // GIGO
Action<IDbCommand, bool> action;
if (Link<Type, Action<IDbCommand, bool>>.TryGet(bindByNameCache, commandType, out action))
{
return action;
}
var prop = commandType.GetProperty("BindByName", BindingFlags.Public | BindingFlags.Instance);
action = null;
ParameterInfo[] indexers;
MethodInfo setter;
if (prop != null && prop.CanWrite && prop.PropertyType == typeof(bool)
&& ((indexers = prop.GetIndexParameters()) == null || indexers.Length == 0)
&& (setter = prop.GetSetMethod()) != null
)
{
var method = new DynamicMethod(commandType.GetOperationName() + "_BindByName", null, new Type[] { typeof(IDbCommand), typeof(bool) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, commandType);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, setter, null);
il.Emit(OpCodes.Ret);
action = (Action<IDbCommand, bool>)method.CreateDelegate(typeof(Action<IDbCommand, bool>));
}
// cache it
Link<Type, Action<IDbCommand, bool>>.TryAdd(ref bindByNameCache, commandType, ref action);
return action;
}
public ProfiledDbCommand(DbCommand cmd, DbConnection conn, IDbProfiler profiler)
: base(cmd, conn, profiler)
{
}
protected override DbConnection DbConnection
{
get { return base.DbConnection; }
set
{
// TODO: we need a way to grab the IDbProfiler which may not be the same as the MiniProfiler, it could be wrapped
// allow for command reuse, it is clear the connection is going to need to be reset
if (MiniProfiler.Current != null)
{
_profiler = MiniProfiler.Current;
}
base.DbConnection = value;
}
}
#region Wrapper properties for backwards compatibility
protected DbCommand _cmd
{
get { return DbCommand; }
set { DbCommand = value; }
}
protected DbConnection _conn
{
get { return DbConnection; }
set { DbConnection = value; }
}
protected DbTransaction _tran
{
get { return DbTransaction; }
set { DbTransaction = value; }
}
protected IDbProfiler _profiler
{
get { return DbProfiler; }
set { DbProfiler = value; }
}
#endregion
public ProfiledDbCommand Clone()
{ // EF expects ICloneable
ICloneable tail = _cmd as ICloneable;
if (tail == null) throw new NotSupportedException("Underlying " + _cmd.GetType().FullName + " is not cloneable");
return new ProfiledDbCommand((DbCommand)tail.Clone(), _conn, _profiler);
}
object ICloneable.Clone() { return Clone(); }
}
}
#pragma warning restore 1591 // xml doc comments warnings
#endif