Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow dictionaries to be passed with parameters #623

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 111 additions & 1 deletion PetaPoco.Tests.Unit/Utilities/ParametersHelperTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
using PetaPoco.Internal;
using Moq;
using PetaPoco.Internal;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using Xunit;

namespace PetaPoco.Tests.Unit.Utilities
Expand Down Expand Up @@ -36,5 +43,108 @@ public void ReplaceParamPrefix_ShouldWork(string prefix)
var output = input.ReplaceParamPrefix(prefix);
output.ShouldBe(expected);
}

[Fact]
public void ProcessQueryParams_PositionalParams_ShouldWork()
{
var sql = "select * from foo where a = @0 and b = @1";
var args_src = new object[] { 5, "bird" };
var args_dest = new List<object>();
var output = ParametersHelper.ProcessQueryParams(sql, args_src, args_dest);

output.ShouldBe(sql);
args_dest.ShouldBe(args_src.ToList());
}

private void NamedQueryParamsTestHelper(object[] args_src)
{
var sql = "select * from foo where a = @first and b = @second";
var args_dest = new List<object>();

var expected_sql = "select * from foo where a = @0 and b = @1";
var expected_args = new List<object>() { 87, "Bob" };

var output = ParametersHelper.ProcessQueryParams(sql, args_src, args_dest);

output.ShouldBe(expected_sql);
args_dest.ShouldBe(expected_args);
}

[Fact]
public void ProcessQueryParams_ObjectWithProperties_ShouldWork()
{
var args_src = new[] { new { second = "Bob", first = 87 } };
NamedQueryParamsTestHelper(args_src);
}

[Fact]
public void ProcessQueryParams_Dictionary_ShouldWork()
{
var args_src = new[] { new Dictionary<string, object> { ["second"] = "Bob", ["first"] = 87 } };
NamedQueryParamsTestHelper(args_src);
}

[Fact]
public void ProcessQueryParams_DictionaryAndObject_ShouldWork()
{
var args_src = new object[] { new Dictionary<string, object> { ["second"] = "Bob" }, new { first = 87 } };
NamedQueryParamsTestHelper(args_src);
}

[Fact]
public void ProcessQueryParams_MissingParam_ShouldThrow()
{
var args_src = new[] { new {first = 87 } };
Action act = () => NamedQueryParamsTestHelper(args_src);

var ex = act.ShouldThrow<ArgumentException>();
ex.Message.ShouldMatch(@"^Parameter '@second' specified");
}

private void NamedProcParamsTestHelper(object[] args_src)
{
Action<IDbDataParameter, object, PropertyInfo> setAction = (p, o, pi) => p.Value = o;
var cmd = new SqlCommand();

var expected = new [] { new SqlParameter("foo", 42), new SqlParameter("bar", "Dirk Gently") };
var output = ParametersHelper.ProcessStoredProcParams(cmd, args_src, setAction)
.Cast<IDbDataParameter>()
.ToArray();

output.Count().ShouldBe(2);
output[0].ParameterName.ShouldBe(expected[0].ParameterName);
output[0].Value.ShouldBe(expected[0].Value);
output[1].ParameterName.ShouldBe(expected[1].ParameterName);
output[1].Value.ShouldBe(expected[1].Value);

}

[Fact]
public void ProcessStoredProcParams_Parameter_ShouldWork()
{
var args_src = new object[] { new SqlParameter("foo", 42), new SqlParameter("bar", "Dirk Gently") };
NamedProcParamsTestHelper(args_src);
}

[Fact]
public void ProcessStoredProcParams_ObjectWithProperties_ShouldWork()
{
var args_src = new object[] { new { foo = 42, bar = "Dirk Gently" } };
NamedProcParamsTestHelper(args_src);
}

[Fact]
public void ProcessStoredProcParams_Dictionary_ShouldWork()
{
var args_src = new[] { new Dictionary<string, object>() { ["foo"] = 42, ["bar"] = "Dirk Gently" } };
NamedProcParamsTestHelper(args_src);
}

[Fact]
public void ProcessStoredProcParams_DictionaryAndObject_ShouldWork()
{
var args_src = new object[] { new Dictionary<string, object>() { ["foo"] = 42 }, new { bar = "Dirk Gently" } };
NamedProcParamsTestHelper(args_src);
}
}
}
50 changes: 42 additions & 8 deletions PetaPoco/Utilities/ParametersHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
Expand Down Expand Up @@ -49,11 +50,27 @@ public static string ProcessQueryParams(string sql, object[] args_src, List<obje
}
else
{
// Look for a property on one of the arguments with this name
bool found = false;
arg_val = null;

foreach (var o in args_src)
{
if (o is IDictionary dict)
{
Type[] arguments = dict.GetType().GetGenericArguments();

if (arguments[0] == typeof(string))
{
var val = dict[param];
if (val != null)
{
found = true;
arg_val = val;
break;
}
}
}

var pi = o.GetType().GetProperty(param);
if (pi != null)
{
Expand Down Expand Up @@ -100,15 +117,27 @@ public static object[] ProcessStoredProcParams(IDbCommand cmd, object[] args, Ac

void ProcessArg(object arg)
{
if (arg.IsEnumerable())
if (arg is IDictionary dict)
{
Type[] arguments = dict.GetType().GetGenericArguments();

if (arguments[0] == typeof(string))
{
foreach (string key in dict.Keys)
{
AddParameter(key, dict[key]);
}
}
}
else if (arg.IsEnumerable())
{
foreach (var singleArg in (arg as System.Collections.IEnumerable))
{
ProcessArg(singleArg);
}
}
else if (arg is IDbDataParameter)
result.Add((IDbDataParameter) arg);
result.Add((IDbDataParameter)arg);
else
{
var type = arg.GetType();
Expand All @@ -117,14 +146,19 @@ void ProcessArg(object arg)
var readableProps = type.GetProperties().Where(p => p.CanRead);
foreach (var prop in readableProps)
{
var param = cmd.CreateParameter();
param.ParameterName = prop.Name;
setParameterProperties(param, prop.GetValue(arg, null), null);
result.Add(param);
}
AddParameter(prop.Name, prop.GetValue(arg, null));
}
}
}

void AddParameter(string name, object value)
{
var param = cmd.CreateParameter();
param.ParameterName = name;
setParameterProperties(param, value, null);
result.Add(param);
}

foreach (var arg in args)
{
ProcessArg(arg);
Expand Down