Commit f5ae8e67 authored by Marc Gravell's avatar Marc Gravell

Merge pull request #123 from Polylytics/multimapfix

Re-worked GenerateDeserializers to support new test
parents 9a068e79 5afba486
...@@ -1437,77 +1437,108 @@ partial class DontMap { } ...@@ -1437,77 +1437,108 @@ partial class DontMap { }
private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader) private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader)
{ {
int current = 0; var deserializers = new List<Func<IDataReader, object>>();
var splits = splitOn.Split(',').ToArray(); var splits = splitOn.Split(',').Select(s => s.Trim()).ToArray();
var splitIndex = 0; bool isMultiSplit = splits.Length > 1;
if (types.First() == typeof(Object))
Func<Type, int> nextSplit = type => {
// we go left to right for dynamic multi-mapping so that the madness of TestMultiMappingVariations
// is supported
bool first = true;
int currentPos = 0;
int splitIdx = 0;
string currentSplit = splits[splitIdx];
foreach (var type in types)
{ {
var currentSplit = splits[splitIndex].Trim(); if (type == typeof(DontMap))
if (splits.Length > splitIndex + 1)
{ {
splitIndex++; break;
} }
bool skipFirst = false; int splitPoint = GetNextSplitDynamic(currentPos, currentSplit, reader);
int startingPos = current + 1; if (isMultiSplit && splitIdx < splits.Length - 1)
// if our current type has the split, skip the first time you see it. {
if (type != typeof(Object)) currentSplit = splits[++splitIdx];
}
deserializers.Add((GetDeserializer(type, reader, currentPos, splitPoint - currentPos, !first)));
currentPos = splitPoint;
first = false;
}
}
else
{
// in this we go right to left through the data reader in order to cope with properties that are
// named the same as a subsequent primary key that we split on
int currentPos = reader.FieldCount;
int splitIdx = splits.Length - 1;
var currentSplit = splits[splitIdx];
for (var typeIdx = types.Length - 1; typeIdx >= 0; --typeIdx)
{
var type = types[typeIdx];
if (type == typeof (DontMap))
{ {
var props = DefaultTypeMap.GetSettableProps(type); continue;
var fields = DefaultTypeMap.GetSettableFields(type); }
foreach (var name in props.Select(p => p.Name).Concat(fields.Select(f => f.Name))) int splitPoint = 0;
if (typeIdx > 0)
{ {
if (string.Equals(name, currentSplit, StringComparison.OrdinalIgnoreCase)) splitPoint = GetNextSplit(currentPos, currentSplit, reader);
if (isMultiSplit && splitIdx > 0)
{ {
skipFirst = true; currentSplit = splits[--splitIdx];
startingPos = current;
break;
} }
} }
deserializers.Add((GetDeserializer(type, reader, splitPoint, currentPos - splitPoint, typeIdx > 0)));
currentPos = splitPoint;
} }
int pos; deserializers.Reverse();
for (pos = startingPos; pos < reader.FieldCount; pos++)
}
return deserializers.ToArray();
}
private static int GetNextSplitDynamic(int startIdx, string splitOn, IDataReader reader)
{
if (startIdx == reader.FieldCount)
{ {
// some people like ID some id ... assuming case insensitive splits for now throw new ArgumentException(MultiMapSplitExceptionMessage);
}
if (splitOn == "*") if (splitOn == "*")
{ {
break; return ++startIdx;
} }
if (string.Equals(reader.GetName(pos), currentSplit, StringComparison.OrdinalIgnoreCase))
for (var i = startIdx + 1; i < reader.FieldCount; ++i)
{ {
if (skipFirst) if (string.Equals(splitOn, reader.GetName(i), StringComparison.OrdinalIgnoreCase))
{ {
skipFirst = false; return i;
} }
else
{
break;
} }
return reader.FieldCount;
} }
private static int GetNextSplit(int startIdx, string splitOn, IDataReader reader)
{
if (splitOn == "*")
{
return --startIdx;
} }
current = pos;
return pos;
};
var deserializers = new List<Func<IDataReader, object>>(); for (var i = startIdx - 1; i > 0; --i)
int split = 0;
bool first = true;
foreach (var type in types)
{ {
if (type != typeof(DontMap)) if (string.Equals(splitOn, reader.GetName(i), StringComparison.OrdinalIgnoreCase))
{ {
int next = nextSplit(type); return i;
deserializers.Add(GetDeserializer(type, reader, split, next - split, /* returnNullIfFirstMissing: */ !first));
first = false;
split = next;
} }
} }
return deserializers.ToArray(); throw new ArgumentException(MultiMapSplitExceptionMessage);
} }
private static CacheInfo GetCacheInfo(Identity identity, object exampleParameters) private static CacheInfo GetCacheInfo(Identity identity, object exampleParameters)
...@@ -2107,7 +2138,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj ...@@ -2107,7 +2138,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
else else
{ {
return "(SELECT " + variableName + " WHERE 1 = 0)"; return "(SELECT " + variableName + " WHERE 1 = 0)";
}; }
}); });
var dummyParam = command.CreateParameter(); var dummyParam = command.CreateParameter();
dummyParam.ParameterName = namePrefix; dummyParam.ParameterName = namePrefix;
......
...@@ -1284,6 +1284,38 @@ public void TestMultiMappingWithSplitOnSpaceBetweenCommas() ...@@ -1284,6 +1284,38 @@ public void TestMultiMappingWithSplitOnSpaceBetweenCommas()
} }
public void TestMultiMappingWithNonReturnedProperty()
{
var sql = @"select
1 as PostId, 'Title' as Title,
2 as BlogId, 'Blog' as Title";
var postWithBlog = connection.Query<Post_DupeProp, Blog_DupeProp, Post_DupeProp>(sql,
(p, b) =>
{
p.Blog = b;
return p;
}, splitOn: "BlogId").First();
postWithBlog.PostId.IsEqualTo(1);
postWithBlog.Title.IsEqualTo("Title");
postWithBlog.Blog.BlogId.IsEqualTo(2);
postWithBlog.Blog.Title.IsEqualTo("Blog");
}
class Post_DupeProp
{
public int PostId { get; set; }
public string Title { get; set; }
public int BlogId { get; set; }
public Blog_DupeProp Blog { get; set; }
}
class Blog_DupeProp
{
public int BlogId { get; set; }
public string Title { get; set; }
}
public void TestFastExpandoSupportsIDictionary() public void TestFastExpandoSupportsIDictionary()
{ {
var row = connection.Query("select 1 A, 'two' B").First() as IDictionary<string, object>; var row = connection.Query("select 1 A, 'two' B").First() as IDictionary<string, object>;
......
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