Commit d8125809 authored by Sam Saffron's avatar Sam Saffron

support for string lists

support for expando
parent 4ad839b3
......@@ -8,7 +8,7 @@
using System.Data;
using System.Reflection;
using Microsoft.SqlServer.Server;
using System.Security.Cryptography;
using System.Dynamic;
namespace SqlMapper
{
......@@ -32,6 +32,8 @@ static SqlMapper()
typeMap[typeof(Guid?)] = SqlDbType.UniqueIdentifier;
typeMap[typeof(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()
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; }
......@@ -53,7 +55,6 @@ public static ParamInfo Create(string name, SqlDbType type, object val)
private class Identity : IEquatable<Identity>
{
public Type Type { get { return type; } }
public string Sql { get { return sql; } }
......@@ -89,6 +90,12 @@ public bool Equals(Identity other)
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)
{
var identity = new Identity(sql, typeof(T));
......@@ -114,7 +121,11 @@ public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql,
object 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);
}
......@@ -137,6 +148,31 @@ public static List<T> ExecuteMapperQuery<T>(this SqlConnection cnn, string sql,
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)
{
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
if (info.Type == SqlDbType.Structured)
{
List<SqlDataRecord> items = new List<SqlDataRecord>();
SqlMetaData[] metadata = { new SqlMetaData("Id", SqlDbType.Int) };
foreach (int id in (IEnumerable<int>)info.Val)
SqlMetaData[] metadata;
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);
rec.SetInt32(0, id);
items.Add(rec);
var strList = info.Val as IEnumerable<string>;
if (strList != null)
{
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.TypeName = "int_list";
param.Value = items;
}
}
......
......@@ -6,61 +6,77 @@
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();
public void SelectListInt()
{
var items = connection.ExecuteMapperQuery<int>("select 1 union all select 2 union all select 3").ToList();
AssertEquals(items[0], 1);
AssertEquals(items[1], 2);
AssertEquals(items[2], 3);
connection.ExecuteMapperQuery<int>("select 1 union all select 2 union all select 3")
.IsSequenceEqual(new[] { 1, 2, 3 });
}
public void PassInIntArray()
{
var items = connection.ExecuteMapperQuery<int>("select * from @Ids", new {Ids = new int[] {1,2,3} }).ToList();
AssertEquals(items[0], 1);
AssertEquals(items[1], 2);
AssertEquals(items[2], 3);
connection.ExecuteMapperQuery<int>("select * from @Ids", new { Ids = new int[] { 1, 2, 3 } })
.IsSequenceEqual(new[] { 1, 2, 3 });
}
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()
{
AssertEquals(connection.ExecuteMapperQuery<bool>("select @b", new { b = false }).First(), false);
connection.ExecuteMapperQuery<bool>("select @b", new { b = false }).First()
.IsFalse();
}
public void TestStrings()
{
var strings = connection.ExecuteMapperQuery<string>(@"select 'a' a union select 'b'").ToList();
AssertEquals(strings[0], "a");
AssertEquals(strings[1], "b");
connection.ExecuteMapperQuery<string>(@"select 'a' a union select 'b'")
.IsSequenceEqual(new[] { "a", "b" });
}
public class Dog
......@@ -76,9 +92,36 @@ public class Dog
public void TestIntSupportsNull()
{
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