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 { }
private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader)
{
int current = 0;
var splits = splitOn.Split(',').ToArray();
var splitIndex = 0;
Func<Type, int> nextSplit = type =>
var deserializers = new List<Func<IDataReader, object>>();
var splits = splitOn.Split(',').Select(s => s.Trim()).ToArray();
bool isMultiSplit = splits.Length > 1;
if (types.First() == typeof(Object))
{
// 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 (splits.Length > splitIndex + 1)
if (type == typeof(DontMap))
{
splitIndex++;
break;
}
bool skipFirst = false;
int startingPos = current + 1;
// if our current type has the split, skip the first time you see it.
if (type != typeof(Object))
int splitPoint = GetNextSplitDynamic(currentPos, currentSplit, reader);
if (isMultiSplit && splitIdx < splits.Length - 1)
{
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);
var fields = DefaultTypeMap.GetSettableFields(type);
continue;
}
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;
startingPos = current;
break;
currentSplit = splits[--splitIdx];
}
}
deserializers.Add((GetDeserializer(type, reader, splitPoint, currentPos - splitPoint, typeIdx > 0)));
currentPos = splitPoint;
}
int pos;
for (pos = startingPos; pos < reader.FieldCount; pos++)
deserializers.Reverse();
}
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 == "*")
{
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>>();
int split = 0;
bool first = true;
foreach (var type in types)
for (var i = startIdx - 1; i > 0; --i)
{
if (type != typeof(DontMap))
if (string.Equals(splitOn, reader.GetName(i), StringComparison.OrdinalIgnoreCase))
{
int next = nextSplit(type);
deserializers.Add(GetDeserializer(type, reader, split, next - split, /* returnNullIfFirstMissing: */ !first));
first = false;
split = next;
return i;
}
}
return deserializers.ToArray();
throw new ArgumentException(MultiMapSplitExceptionMessage);
}
private static CacheInfo GetCacheInfo(Identity identity, object exampleParameters)
......@@ -2107,7 +2138,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
else
{
return "(SELECT " + variableName + " WHERE 1 = 0)";
};
}
});
var dummyParam = command.CreateParameter();
dummyParam.ParameterName = namePrefix;
......
......@@ -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()
{
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