Commit 27b41cc5 authored by Sam Saffron's avatar Sam Saffron

Add support for DynamicParams to append objects

EG:

  DynamicParameters p = new DynamicParameters();
  p.AddDynamicParams(new { A = 1, B = 2 });
  p.AddDynamicParams(new { C = 3, D = 4 });

Expose some more of dapper's internals
parent c873a24d
......@@ -23,7 +23,7 @@ public static partial class SqlMapper
{
public interface IDynamicParameters
{
void AddParameters(IDbCommand command);
void AddParameters(IDbCommand command, Identity identity);
}
static Link<Type, Action<IDbCommand, bool>> bindByNameCache;
static Action<IDbCommand, bool> GetBindByName(Type commandType)
......@@ -279,7 +279,7 @@ private static DbType LookupDbType(Type type, string name)
throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type));
}
internal class Identity : IEquatable<Identity>
public class Identity : IEquatable<Identity>
{
internal Identity ForGrid(Type primaryType, int gridIndex)
{
......@@ -291,6 +291,11 @@ internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex)
return new Identity(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);
}
public Identity ForDynamicParameters(Type type)
{
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)
: this(sql, commandType, connection.ConnectionString, type, parametersType, otherTypes, 0)
{ }
......@@ -324,12 +329,12 @@ public override bool Equals(object obj)
{
return Equals(obj as Identity);
}
internal readonly string sql;
internal readonly CommandType? commandType;
internal readonly int hashCode, gridIndex;
public readonly string sql;
public readonly CommandType? commandType;
public readonly int hashCode, gridIndex;
private readonly Type type;
internal readonly string connectionString;
internal readonly Type parametersType;
public readonly string connectionString;
public readonly Type parametersType;
public override int GetHashCode()
{
return hashCode;
......@@ -777,7 +782,7 @@ private static CacheInfo GetCacheInfo(Identity identity)
{
if (typeof(IDynamicParameters).IsAssignableFrom(identity.parametersType))
{
info.ParamReader = (cmd, obj) => { (obj as IDynamicParameters).AddParameters(cmd); };
info.ParamReader = (cmd, obj) => { (obj as IDynamicParameters).AddParameters(cmd,identity); };
}
else
{
......@@ -1040,7 +1045,7 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn
return parameters.Where(p => Regex.IsMatch(sql, "[@:]" + p.Name + "([^a-zA-Z0-9_]+|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline));
}
private static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity)
public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity)
{
Type type = identity.parametersType;
bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text;
......@@ -1660,7 +1665,10 @@ public void Dispose()
}
public class DynamicParameters : SqlMapper.IDynamicParameters
{
static Dictionary<SqlMapper.Identity, Action<IDbCommand, object>> paramReaderCache = new Dictionary<SqlMapper.Identity, Action<IDbCommand, object>>();
Dictionary<string, ParamInfo> parameters = new Dictionary<string, ParamInfo>();
List<object> templates;
class ParamInfo
{
......@@ -1675,21 +1683,31 @@ class ParamInfo
public DynamicParameters() { }
public DynamicParameters(object template)
{
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
if (template != null)
{
foreach (PropertyInfo prop in template.GetType().GetProperties(bindingFlags))
{
if (!prop.CanRead) continue;
var idx = prop.GetIndexParameters();
if (idx != null && idx.Length != 0) continue;
Add(prop.Name, prop.GetValue(template, null), null, ParameterDirection.Input, null);
}
foreach (FieldInfo field in template.GetType().GetFields(bindingFlags))
{
Add(field.Name, field.GetValue(template), null, ParameterDirection.Input, null);
}
AddDynamicParams(template);
}
}
/// <summary>
/// Append a whole object full of params to the dynamic
/// EG: AddParams(new {A = 1, B = 2}) // will add property A and B to the dynamic
/// </summary>
/// <param name="param"></param>
public void AddDynamicParams(
#if CSHARP30
object param
#else
dynamic param
#endif
)
{
object obj = param as object;
if (obj != null)
{
templates = templates ?? new List<object>();
templates.Add(obj);
}
}
......@@ -1720,8 +1738,28 @@ static string Clean(string name)
return name;
}
void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command)
void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
if (templates != null)
{
foreach (var template in templates)
{
var newIdent = identity.ForDynamicParameters(template.GetType());
Action<IDbCommand, object> appender;
lock (paramReaderCache)
{
if (!paramReaderCache.TryGetValue(newIdent, out appender))
{
appender = SqlMapper.CreateParamInfoGenerator(newIdent);
paramReaderCache[newIdent] = appender;
}
}
appender(command, template);
}
}
foreach (var param in parameters.Values)
{
var p = command.CreateParameter();
......
......@@ -11,10 +11,8 @@
namespace SqlMapper
{
class Tests
{
SqlConnection connection = Program.GetOpenConnection();
public void SelectListInt()
......@@ -757,7 +755,7 @@ public IntDynamicParam(IEnumerable<int> numbers)
this.numbers = numbers;
}
public void AddParameters(IDbCommand command)
public void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity)
{
var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure;
......@@ -1051,5 +1049,32 @@ public void TestWithNonPublicConstructor()
var output = connection.Query<WithPrivateConstructor>("select 1 as Foo").First();
output.Foo.IsEqualTo(1);
}
public void TestAppendingAnonClasses()
{
DynamicParameters p = new DynamicParameters();
p.AddDynamicParams(new { A = 1, B = 2 });
p.AddDynamicParams(new { C = 3, D = 4 });
var result = connection.Query("select @A a,@B b,@C c,@D d", p).Single();
((int)result.a).IsEqualTo(1);
((int)result.b).IsEqualTo(2);
((int)result.c).IsEqualTo(3);
((int)result.d).IsEqualTo(4);
}
public void TestAppendingAList()
{
DynamicParameters p = new DynamicParameters();
var list = new int[] {1,2,3};
p.AddDynamicParams(new { list });
var result = connection.Query<int>("select * from (select 1 A union all select 2 union all select 3) X where A in @list", p).ToList();
result[0].IsEqualTo(1);
result[1].IsEqualTo(2);
result[2].IsEqualTo(3);
}
}
}
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