Commit 7910f25b authored by Marc Gravell's avatar Marc Gravell

When using AddDynamicParameters, watch out for duplicate parameters coming from templates

parent 2a94c2b2
...@@ -456,7 +456,7 @@ internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex) ...@@ -456,7 +456,7 @@ internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex)
/// <returns></returns> /// <returns></returns>
public Identity ForDynamicParameters(Type type) public Identity ForDynamicParameters(Type type)
{ {
return new Identity(sql, commandType, connectionString, this.type ,type, null, -1); return new Identity(sql, commandType, connectionString, this.type, type, null, -1);
} }
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes) internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)
...@@ -1132,7 +1132,7 @@ private static CacheInfo GetCacheInfo(Identity identity) ...@@ -1132,7 +1132,7 @@ private static CacheInfo GetCacheInfo(Identity identity)
#endif #endif
else else
{ {
info.ParamReader = CreateParamInfoGenerator(identity); info.ParamReader = CreateParamInfoGenerator(identity, false);
} }
} }
SetQueryCache(identity, info); SetQueryCache(identity, info);
...@@ -1367,6 +1367,29 @@ public static char ReadChar(object value) ...@@ -1367,6 +1367,29 @@ public static char ReadChar(object value)
if (s == null || s.Length != 1) throw new ArgumentException("A single-character was expected", "value"); if (s == null || s.Length != 1) throw new ArgumentException("A single-character was expected", "value");
return s[0]; return s[0];
} }
/// <summary>
/// Internal use only
/// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This method is for internal usage only", true)]
public static IDbDataParameter FindOrAddParameter(IDataParameterCollection parameters, IDbCommand command, string name)
{
IDbDataParameter result;
if (parameters.Contains(name))
{
result = (IDbDataParameter)parameters[name];
}
else
{
result = command.CreateParameter();
result.ParameterName = name;
parameters.Add(result);
}
return result;
}
/// <summary> /// <summary>
/// Internal use only /// Internal use only
/// </summary> /// </summary>
...@@ -1448,7 +1471,7 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn ...@@ -1448,7 +1471,7 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn
/// <summary> /// <summary>
/// Internal use only /// Internal use only
/// </summary> /// </summary>
public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity) public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates)
{ {
Type type = identity.parametersType; Type type = identity.parametersType;
bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text; bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text;
...@@ -1507,31 +1530,41 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn ...@@ -1507,31 +1530,41 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn
il.Emit(OpCodes.Dup); // stack is now [parameters] [parameters] il.Emit(OpCodes.Dup); // stack is now [parameters] [parameters]
il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [parameters] [command] il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [parameters] [command]
if (checkForDuplicates)
{
// need to be a little careful about adding; use a utility method
il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [command] [name]
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("FindOrAddParameter"), null); // stack is [parameters] [parameter]
}
else
{
// no risk of duplicates; just blindly add
il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);// stack is now [parameters] [parameters] [parameter] il.EmitCall(OpCodes.Callvirt, typeof(IDbCommand).GetMethod("CreateParameter"), null);// stack is now [parameters] [parameters] [parameter]
il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter]
il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [parameter] [parameter] [name] il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [parameters] [parameter] [parameter] [name]
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("ParameterName").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter]
}
if(dbType != DbType.Time) // https://connect.microsoft.com/VisualStudio/feedback/details/381934/sqlparameter-dbtype-dbtype-time-sets-the-parameter-to-sqldbtype-datetime-instead-of-sqldbtype-time if(dbType != DbType.Time) // https://connect.microsoft.com/VisualStudio/feedback/details/381934/sqlparameter-dbtype-dbtype-time-sets-the-parameter-to-sqldbtype-datetime-instead-of-sqldbtype-time
{ {
il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
EmitInt32(il, (int)dbType);// stack is now [parameters] [parameters] [parameter] [parameter] [db-type] EmitInt32(il, (int)dbType);// stack is now [parameters] [[parameters]] [parameter] [parameter] [db-type]
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("DbType").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
} }
il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
EmitInt32(il, (int)ParameterDirection.Input);// stack is now [parameters] [parameters] [parameter] [parameter] [dir] EmitInt32(il, (int)ParameterDirection.Input);// stack is now [parameters] [[parameters]] [parameter] [parameter] [dir]
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Direction").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [parameters] [parameter] [parameter] [typed-param] il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [[parameters]] [parameter] [parameter] [typed-param]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [parameters] [parameter] [parameter] [typed-value] il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [[parameters]] [parameter] [parameter] [typed-value]
bool checkForNull = true; bool checkForNull = true;
if (prop.PropertyType.IsValueType) if (prop.PropertyType.IsValueType)
{ {
il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [parameters] [parameter] [parameter] [boxed-value] il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [[parameters]] [parameter] [parameter] [boxed-value]
if (Nullable.GetUnderlyingType(prop.PropertyType) == null) if (Nullable.GetUnderlyingType(prop.PropertyType) == null)
{ // struct but not Nullable<T>; boxed value cannot be null { // struct but not Nullable<T>; boxed value cannot be null
checkForNull = false; checkForNull = false;
...@@ -1581,26 +1614,35 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn ...@@ -1581,26 +1614,35 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn
if (allDone != null) il.MarkLabel(allDone.Value); if (allDone != null) il.MarkLabel(allDone.Value);
// relative stack [boxed value or DBNull] // relative stack [boxed value or DBNull]
} }
il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty("Value").GetSetMethod(), null);// stack is now [parameters] [[parameters]] [parameter]
if (prop.PropertyType == typeof(string)) if (prop.PropertyType == typeof(string))
{ {
var endOfSize = il.DefineLabel(); var endOfSize = il.DefineLabel();
// don't set if 0 // don't set if 0
il.Emit(OpCodes.Ldloc_1); // [parameters] [parameters] [parameter] [size] il.Emit(OpCodes.Ldloc_1); // [parameters] [[parameters]] [parameter] [size]
il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [parameters] [parameter] il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [[parameters]] [parameter]
il.Emit(OpCodes.Dup);// stack is now [parameters] [parameters] [parameter] [parameter] il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter]
il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [parameters] [parameter] [parameter] [size] il.Emit(OpCodes.Ldloc_1); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size]
il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null);// stack is now [parameters] [parameters] [parameter] il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty("Size").GetSetMethod(), null); // stack is now [parameters] [[parameters]] [parameter]
il.MarkLabel(endOfSize); il.MarkLabel(endOfSize);
} }
if (checkForDuplicates)
{
// stack is now [parameters] [parameter]
il.Emit(OpCodes.Pop); // don't need parameter any more
}
else
{
// stack is now [parameters] [parameters] [parameter]
// blindly add
il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); // stack is now [parameters] il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod("Add"), null); // stack is now [parameters]
il.Emit(OpCodes.Pop); // IList.Add returns the new index (int); we don't care il.Emit(OpCodes.Pop); // IList.Add returns the new index (int); we don't care
} }
// stack is currently [command] }
// stack is currently [parameters]
il.Emit(OpCodes.Pop); // stack is now empty il.Emit(OpCodes.Pop); // stack is now empty
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
return (Action<IDbCommand, object>)dm.CreateDelegate(typeof(Action<IDbCommand, object>)); return (Action<IDbCommand, object>)dm.CreateDelegate(typeof(Action<IDbCommand, object>));
...@@ -2519,7 +2561,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -2519,7 +2561,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{ {
if (!paramReaderCache.TryGetValue(newIdent, out appender)) if (!paramReaderCache.TryGetValue(newIdent, out appender))
{ {
appender = SqlMapper.CreateParamInfoGenerator(newIdent); appender = SqlMapper.CreateParamInfoGenerator(newIdent, true);
paramReaderCache[newIdent] = appender; paramReaderCache[newIdent] = appender;
} }
} }
......
...@@ -1916,6 +1916,16 @@ public void TestWrongTypes_WithWrongTypes() ...@@ -1916,6 +1916,16 @@ public void TestWrongTypes_WithWrongTypes()
item.D.Equals(true); item.D.Equals(true);
} }
public void Test_AddDynamicParametersRepeatedShouldWork()
{
var args = new DynamicParameters();
args.AddDynamicParams(new { Foo = 123 });
args.AddDynamicParams(new { Foo = 123 });
int i = connection.Query<int>("select @Foo", args).Single();
i.IsEqualTo(123);
}
public class ParameterWithIndexer public class ParameterWithIndexer
{ {
public int A { get; set; } public int A { get; set; }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment