Commit d8125809 authored by Sam Saffron's avatar Sam Saffron

support for string lists

support for expando
parent 4ad839b3
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
using System.Data; using System.Data;
using System.Reflection; using System.Reflection;
using Microsoft.SqlServer.Server; using Microsoft.SqlServer.Server;
using System.Security.Cryptography; using System.Dynamic;
namespace SqlMapper namespace SqlMapper
{ {
...@@ -32,6 +32,8 @@ static SqlMapper() ...@@ -32,6 +32,8 @@ static SqlMapper()
typeMap[typeof(Guid?)] = SqlDbType.UniqueIdentifier; typeMap[typeof(Guid?)] = SqlDbType.UniqueIdentifier;
typeMap[typeof(int[])] = SqlDbType.Structured; typeMap[typeof(int[])] = SqlDbType.Structured;
typeMap[typeof(List<int>)] = SqlDbType.Structured; typeMap[typeof(List<int>)] = SqlDbType.Structured;
typeMap[typeof(string[])] = SqlDbType.Structured;
typeMap[typeof(List<string>)] = SqlDbType.Structured;
} }
...@@ -43,7 +45,7 @@ private ParamInfo() ...@@ -43,7 +45,7 @@ private ParamInfo()
public static ParamInfo Create(string name, SqlDbType type, object val) public static ParamInfo Create(string name, SqlDbType type, object val)
{ {
return new ParamInfo { Name = name, Type = type, Val = val }; return new ParamInfo { Name = name, Type = type, Val = val};
} }
public SqlDbType Type { get; private set; } public SqlDbType Type { get; private set; }
...@@ -53,7 +55,6 @@ public static ParamInfo Create(string name, SqlDbType type, object val) ...@@ -53,7 +55,6 @@ public static ParamInfo Create(string name, SqlDbType type, object val)
private class Identity : IEquatable<Identity> private class Identity : IEquatable<Identity>
{ {
public Type Type { get { return type; } } public Type Type { get { return type; } }
public string Sql { get { return sql; } } public string Sql { get { return sql; } }
...@@ -89,6 +90,12 @@ public bool Equals(Identity other) ...@@ -89,6 +90,12 @@ public bool Equals(Identity other)
static Dictionary<Type, SqlDbType> typeMap; static Dictionary<Type, SqlDbType> typeMap;
public static List<dynamic> ExecuteMapperQuery (this SqlConnection cnn, string sql, object param = null, SqlTransaction transaction = null)
{
// TODO: get rid of casting hackery
return ExecuteMapperQuery<ExpandoObject>(cnn, sql, param, transaction).Select(s => s as dynamic).ToList();
}
public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql, object param = null, SqlTransaction transaction = null) public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql, object param = null, SqlTransaction transaction = null)
{ {
var identity = new Identity(sql, typeof(T)); var identity = new Identity(sql, typeof(T));
...@@ -114,7 +121,11 @@ public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql, ...@@ -114,7 +121,11 @@ public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql,
object oDeserializer; object oDeserializer;
if (!cachedSerializers.TryGetValue(identity, out oDeserializer)) if (!cachedSerializers.TryGetValue(identity, out oDeserializer))
{ {
if (typeof(T).IsClass && typeof(T) != typeof(string)) if (typeof(T) == typeof(ExpandoObject))
{
oDeserializer = GetDynamicDeserializer(reader);
}
else if (typeof(T).IsClass && typeof(T) != typeof(string))
{ {
oDeserializer = GetClassDeserializer<T>(reader); oDeserializer = GetClassDeserializer<T>(reader);
} }
...@@ -137,6 +148,31 @@ public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql, ...@@ -137,6 +148,31 @@ public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql,
return rval; return rval;
} }
private static object GetDynamicDeserializer(SqlDataReader reader)
{
List<string> colNames = new List<string>();
for (int i = 0; i < reader.FieldCount; i++)
{
colNames.Add(reader.GetName(i));
}
Func<SqlDataReader, ExpandoObject> rval =
r =>
{
IDictionary<string, object> row = new ExpandoObject();
int i = 0;
foreach (var colName in colNames)
{
var tmp = reader.GetValue(i);
row[colName] = tmp == DBNull.Value ? null : tmp;
i++;
}
return (ExpandoObject)row;
};
return rval;
}
private static Func<object, List<ParamInfo>> CreateParamInfoGenerator(Type type) private static Func<object, List<ParamInfo>> CreateParamInfoGenerator(Type type)
{ {
DynamicMethod dm = new DynamicMethod("ParamInfo" + Guid.NewGuid().ToString(), typeof(List<ParamInfo>), new Type[] { typeof(object) }, true); DynamicMethod dm = new DynamicMethod("ParamInfo" + Guid.NewGuid().ToString(), typeof(List<ParamInfo>), new Type[] { typeof(object) }, true);
...@@ -195,17 +231,41 @@ private static SqlDataReader GetReader<T>(SqlConnection cnn, SqlTransaction tran ...@@ -195,17 +231,41 @@ private static SqlDataReader GetReader<T>(SqlConnection cnn, SqlTransaction tran
if (info.Type == SqlDbType.Structured) if (info.Type == SqlDbType.Structured)
{ {
List<SqlDataRecord> items = new List<SqlDataRecord>(); List<SqlDataRecord> items = new List<SqlDataRecord>();
SqlMetaData[] metadata = { new SqlMetaData("Id", SqlDbType.Int) }; SqlMetaData[] metadata;
foreach (int id in (IEnumerable<int>)info.Val) var intList = info.Val as IEnumerable<int>;
if (intList != null)
{
metadata = new[] { new SqlMetaData("Id", SqlDbType.Int) };
foreach (int id in intList)
{
SqlDataRecord rec = new SqlDataRecord(metadata);
rec.SetInt32(0, id);
items.Add(rec);
}
param.TypeName = "int_list";
}
else
{ {
SqlDataRecord rec = new SqlDataRecord(metadata); var strList = info.Val as IEnumerable<string>;
rec.SetInt32(0, id); if (strList != null)
items.Add(rec); {
metadata = new[] { new SqlMetaData("Name", SqlDbType.NVarChar, 4000) };
foreach (var str in strList)
{
SqlDataRecord rec = new SqlDataRecord(metadata);
rec.SetString(0, str);
items.Add(rec);
}
param.TypeName = "string_list";
}
else
{
throw new NotImplementedException();
}
} }
param.Direction = ParameterDirection.Input; param.Direction = ParameterDirection.Input;
param.TypeName = "int_list";
param.Value = items; param.Value = items;
} }
} }
......
...@@ -6,61 +6,77 @@ ...@@ -6,61 +6,77 @@
namespace SqlMapper namespace SqlMapper
{ {
class Tests static class TestAssertions
{ {
void AssertEquals(object a, object b)
public static void IsEquals<T>(this T obj, T other)
{
if (!obj.Equals(other))
{
throw new ApplicationException(string.Format("{0} should be equals to {1}", obj, other));
}
}
public static void IsSequenceEqual<T>(this IEnumerable<T> obj, IEnumerable<T> other)
{ {
if (!a.Equals(b)) if (!obj.SequenceEqual(other))
{ {
throw new ApplicationException(string.Format("{0} should be equals to {1}",a,b)); throw new ApplicationException(string.Format("{0} should be equals to {1}", obj, other));
} }
} }
void AssertNull(object a) public static void IsFalse(this bool b)
{ {
if (a != null) if (b)
{ {
throw new ApplicationException(string.Format("{0} should be null", a)); throw new ApplicationException("Expected false");
} }
} }
public static void IsNull(this object obj)
{
if (obj != null)
{
throw new ApplicationException("Expected null");
}
}
}
class Tests
{
SqlConnection connection = Program.GetOpenConnection(); SqlConnection connection = Program.GetOpenConnection();
public void SelectListInt() public void SelectListInt()
{ {
var items = connection.ExecuteMapperQuery<int>("select 1 union all select 2 union all select 3").ToList(); connection.ExecuteMapperQuery<int>("select 1 union all select 2 union all select 3")
.IsSequenceEqual(new[] { 1, 2, 3 });
AssertEquals(items[0], 1);
AssertEquals(items[1], 2);
AssertEquals(items[2], 3);
} }
public void PassInIntArray() public void PassInIntArray()
{ {
var items = connection.ExecuteMapperQuery<int>("select * from @Ids", new {Ids = new int[] {1,2,3} }).ToList(); connection.ExecuteMapperQuery<int>("select * from @Ids", new { Ids = new int[] { 1, 2, 3 } })
.IsSequenceEqual(new[] { 1, 2, 3 });
AssertEquals(items[0], 1);
AssertEquals(items[1], 2);
AssertEquals(items[2], 3);
} }
public void TestDoubleParam() public void TestDoubleParam()
{ {
AssertEquals(connection.ExecuteMapperQuery<double>("select @d", new { d = 0.1d }).First(), 0.1d); connection.ExecuteMapperQuery<double>("select @d", new { d = 0.1d }).First()
.IsEquals(0.1d);
} }
public void TestBoolParam() public void TestBoolParam()
{ {
AssertEquals(connection.ExecuteMapperQuery<bool>("select @b", new { b = false }).First(), false); connection.ExecuteMapperQuery<bool>("select @b", new { b = false }).First()
.IsFalse();
} }
public void TestStrings() public void TestStrings()
{ {
var strings = connection.ExecuteMapperQuery<string>(@"select 'a' a union select 'b'").ToList(); connection.ExecuteMapperQuery<string>(@"select 'a' a union select 'b'")
AssertEquals(strings[0], "a"); .IsSequenceEqual(new[] { "a", "b" });
AssertEquals(strings[1], "b");
} }
public class Dog public class Dog
...@@ -76,9 +92,36 @@ public class Dog ...@@ -76,9 +92,36 @@ public class Dog
public void TestIntSupportsNull() public void TestIntSupportsNull()
{ {
var dog = connection.ExecuteMapperQuery<Dog>("select Age = @Age", new { Age = (int?)null }); var dog = connection.ExecuteMapperQuery<Dog>("select Age = @Age", new { Age = (int?)null });
AssertEquals(dog.Count(), 1);
AssertNull(dog.First().Age); dog.Count()
.IsEquals(1);
dog.First().Age
.IsNull();
}
public void TestExpando()
{
var rows = connection.ExecuteMapperQuery("select 1 A, 2 B union all select 3, 4");
((int)rows[0].A)
.IsEquals(1);
((int)rows[0].B)
.IsEquals(2);
((int)rows[1].A)
.IsEquals(3);
((int)rows[1].B)
.IsEquals(4);
} }
public void TestStringList()
{
connection.ExecuteMapperQuery<string>("select * from @strings", new {strings = new[] {"a","b","c"}})
.IsSequenceEqual(new[] {"a","b","c"});
}
} }
} }
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