Commit 9d7e6589 authored by Sam Saffron's avatar Sam Saffron

dynamic multi deserialization for extra awesome

bug fix around null objects
parent 20b9ec35
...@@ -227,8 +227,25 @@ private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sq ...@@ -227,8 +227,25 @@ private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sq
} }
} }
info.Deserializer = GetDeserializer<T>(identity, reader, start, length); // dynamic comes back as object ...
info.Deserializer2 = GetDeserializer<U>(identity, reader, start + length); if (typeof(T) == typeof(object))
{
info.Deserializer = GetDeserializer<ExpandoObject>(identity, reader, start, length);
}
else
{
info.Deserializer = GetDeserializer<T>(identity, reader, start, length);
}
if (typeof(U) == typeof(object))
{
info.Deserializer2 = GetDeserializer<ExpandoObject>(identity, reader, start + length,returnNullIfFirstMissing:true);
}
else
{
info.Deserializer2 = GetDeserializer<U>(identity, reader, start + length, returnNullIfFirstMissing: true);
}
queryCache[identity] = info; queryCache[identity] = info;
} }
...@@ -259,22 +276,22 @@ private static CacheInfo GetCacheInfo(object param, Identity identity) ...@@ -259,22 +276,22 @@ private static CacheInfo GetCacheInfo(object param, Identity identity)
} }
static class DynamicStub class DynamicStub
{ {
public static Type Type = typeof(DynamicStub); public static Type Type = typeof(DynamicStub);
} }
static Func<IDataReader, T> GetDeserializer<T>(Identity identity, IDataReader reader, int startBound = 0, int length = -1) static Func<IDataReader, T> GetDeserializer<T>(Identity identity, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false)
{ {
object oDeserializer; object oDeserializer;
if (typeof(T) == DynamicStub.Type || typeof(T) == typeof(ExpandoObject)) if (typeof(T) == DynamicStub.Type || typeof(T) == typeof(ExpandoObject))
{ {
oDeserializer = GetDynamicDeserializer(reader); oDeserializer = GetDynamicDeserializer(reader,startBound, length, returnNullIfFirstMissing);
} }
else if (typeof(T).IsClass && typeof(T) != typeof(string)) else if (typeof(T).IsClass && typeof(T) != typeof(string))
{ {
oDeserializer = GetClassDeserializer<T>(reader, startBound, length); oDeserializer = GetClassDeserializer<T>(reader, startBound, length, returnNullIfFirstMissing: returnNullIfFirstMissing);
} }
else else
{ {
...@@ -285,10 +302,16 @@ static class DynamicStub ...@@ -285,10 +302,16 @@ static class DynamicStub
return deserializer; return deserializer;
} }
private static object GetDynamicDeserializer(IDataReader reader) private static object GetDynamicDeserializer(IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false)
{ {
List<string> colNames = new List<string>(); List<string> colNames = new List<string>();
for (int i = 0; i < reader.FieldCount; i++)
if (length == -1)
{
length = reader.FieldCount - startBound;
}
for (int i = startBound; i < startBound + length; i++)
{ {
colNames.Add(reader.GetName(i)); colNames.Add(reader.GetName(i));
} }
...@@ -297,12 +320,19 @@ private static object GetDynamicDeserializer(IDataReader reader) ...@@ -297,12 +320,19 @@ private static object GetDynamicDeserializer(IDataReader reader)
r => r =>
{ {
IDictionary<string, object> row = new ExpandoObject(); IDictionary<string, object> row = new ExpandoObject();
int i = 0; int i = startBound;
bool first = true;
foreach (var colName in colNames) foreach (var colName in colNames)
{ {
var tmp = r.GetValue(i); var tmp = r.GetValue(i);
row[colName] = tmp == DBNull.Value ? null : tmp; tmp = tmp == DBNull.Value ? null : tmp;
row[colName] = tmp;
if (returnNullIfFirstMissing && first && tmp == null)
{
return null;
}
i++; i++;
first = false;
} }
return (ExpandoObject)row; return (ExpandoObject)row;
}; };
...@@ -521,7 +551,7 @@ private static object GetStructDeserializer<T>(IDataReader reader) ...@@ -521,7 +551,7 @@ private static object GetStructDeserializer<T>(IDataReader reader)
return deserializer; return deserializer;
} }
public static Func<IDataReader, T> GetClassDeserializer<T>(IDataReader reader, int startBound = 0, int length = -1) public static Func<IDataReader, T> GetClassDeserializer<T>(IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false)
{ {
DynamicMethod dm = new DynamicMethod("Deserialize" + Guid.NewGuid().ToString(), typeof(T), new Type[] { typeof(IDataReader) }, true); DynamicMethod dm = new DynamicMethod("Deserialize" + Guid.NewGuid().ToString(), typeof(T), new Type[] { typeof(IDataReader) }, true);
...@@ -560,6 +590,7 @@ private static object GetStructDeserializer<T>(IDataReader reader) ...@@ -560,6 +590,7 @@ private static object GetStructDeserializer<T>(IDataReader reader)
// stack is empty // stack is empty
il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes)); // stack is now [target] il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes)); // stack is now [target]
bool first = true;
foreach (var item in setters) foreach (var item in setters)
{ {
if (item.Info != null) if (item.Info != null)
...@@ -581,13 +612,21 @@ private static object GetStructDeserializer<T>(IDataReader reader) ...@@ -581,13 +612,21 @@ private static object GetStructDeserializer<T>(IDataReader reader)
il.Emit(OpCodes.Callvirt, item.Info.Setter); // stack is now [target] il.Emit(OpCodes.Callvirt, item.Info.Setter); // stack is now [target]
il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target] il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]
il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value] il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]
il.Emit(OpCodes.Pop); // stack is now [target][target] il.Emit(OpCodes.Pop); // stack is now [target][target]
il.Emit(OpCodes.Pop); // stack is now [target] il.Emit(OpCodes.Pop); // stack is now [target]
if (first && returnNullIfFirstMissing)
{
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldnull); // stack is now [null]
il.Emit(OpCodes.Ret);
}
il.MarkLabel(finishLabel); il.MarkLabel(finishLabel);
} }
first = false;
index += 1; index += 1;
} }
il.Emit(OpCodes.Ret); // stack is empty il.Emit(OpCodes.Ret); // stack is empty
......
...@@ -269,13 +269,53 @@ public void TestMultiMap() ...@@ -269,13 +269,53 @@ public void TestMultiMap()
left join #Users u on u.Id = p.OwnerId left join #Users u on u.Id = p.OwnerId
Order by p.Id"; Order by p.Id";
var data = connection.Query<Post, User>(sql, (post, user) => { post.Owner = user; }); var data = connection.Query<Post, User>(sql, (post, user) => { post.Owner = user; }).ToList();
var p = data.First(); var p = data.First();
p.Content.IsEqualTo("Sams Post1"); p.Content.IsEqualTo("Sams Post1");
p.Id.IsEqualTo(1); p.Id.IsEqualTo(1);
p.Owner.Name.IsEqualTo("Sam"); p.Owner.Name.IsEqualTo("Sam");
p.Owner.Id.IsEqualTo(99); p.Owner.Id.IsEqualTo(99);
data[2].Owner.IsNull();
connection.Execute("drop table #Users drop table #Posts");
}
public void TestMultiMapDynamic()
{
var createSql = @"
create table #Users (Id int, Name varchar(20))
create table #Posts (Id int, OwnerId int, Content varchar(20))
insert #Users values(99, 'Sam')
insert #Users values(2, 'I am')
insert #Posts values(1, 99, 'Sams Post1')
insert #Posts values(2, 99, 'Sams Post2')
insert #Posts values(3, null, 'no ones post')
";
connection.Execute(createSql);
var sql =
@"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<dynamic, dynamic>(sql, (post, user) => { post.Owner = user; }).ToList();
var p = data.First();
// hairy extension method support for dynamics
((string)p.Content).IsEqualTo("Sams Post1");
((int)p.Id).IsEqualTo(1);
((string)p.Owner.Name).IsEqualTo("Sam");
((int)p.Owner.Id).IsEqualTo(99);
((object)data[2].Owner).IsNull();
connection.Execute("drop table #Users drop table #Posts");
} }
} }
} }
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