Commit c0ea75a5 authored by mgravell's avatar mgravell

spike to see what would be involved in capturing the shape of zero-row grids...

spike to see what would be involved in capturing the shape of zero-row grids by moving the list (buffer) earlier
parent c1d0fd87
......@@ -9,7 +9,7 @@ namespace Dapper
/// <summary>
/// Represents the key aspects of a sql operation
/// </summary>
public struct CommandDefinition
public readonly struct CommandDefinition
{
internal static CommandDefinition ForCallback(object parameters)
{
......
......@@ -391,7 +391,7 @@ private static Task TryOpenAsync(this IDbConnection cnn, CancellationToken cance
/// <summary>
/// Attempts setup a <see cref="DbCommand"/> on a <see cref="DbConnection"/>, with a better error message for unsupported usages.
/// </summary>
private static DbCommand TrySetupAsyncCommand(this CommandDefinition command, IDbConnection cnn, Action<IDbCommand, object> paramReader)
private static DbCommand TrySetupAsyncCommand(in this CommandDefinition command, IDbConnection cnn, Action<IDbCommand, object> paramReader)
{
if (command.SetupCommand(cnn, paramReader) is DbCommand dbCommand)
{
......@@ -420,11 +420,15 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn,
var tuple = info.Deserializer;
int hash = GetColumnHash(reader);
var buffer = command.Buffered ? CreateBufferList<T>() : null;
if (tuple.Func == null || tuple.Hash != hash)
{
if (reader.FieldCount == 0)
return Enumerable.Empty<T>();
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
{
return buffer ?? Enumerable.Empty<T>();
}
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false, buffer as IDynamicRowList));
if (command.AddToCache) SetQueryCache(identity, info);
}
......@@ -432,7 +436,6 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn,
if (command.Buffered)
{
var buffer = new List<T>();
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
{
......@@ -491,7 +494,7 @@ private static async Task<T> QueryRowAsync<T>(this IDbConnection cnn, Row row, T
int hash = GetColumnHash(reader);
if (tuple.Func == null || tuple.Hash != hash)
{
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false, null));
if (command.AddToCache) SetQueryCache(identity, info);
}
......@@ -558,7 +561,7 @@ public static Task<int> ExecuteAsync(this IDbConnection cnn, CommandDefinition c
}
}
private struct AsyncExecState
private readonly struct AsyncExecState
{
public readonly DbCommand Command;
public readonly Task<int> Task;
......
......@@ -18,21 +18,20 @@ public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object
=> new DapperRowTypeDescriptor(instance);
}
//// in theory we could implement this for zero-length results to bind; would require
//// additional changes, though, to capture a table even when no rows - so not currently provided
//internal sealed class DapperRowList : List<DapperRow>, ITypedList
//{
// private readonly DapperTable _table;
// public DapperRowList(DapperTable table) { _table = table; }
// PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
// {
// if (listAccessors != null && listAccessors.Length != 0) return PropertyDescriptorCollection.Empty;
// return DapperRowTypeDescriptor.GetProperties(_table);
// }
// string ITypedList.GetListName(PropertyDescriptor[] listAccessors) => null;
//}
// in theory we could implement this for zero-length results to bind; would require
// additional changes, though, to capture a table even when no rows - so not currently provided
internal sealed class DapperRowList : List<object>, ITypedList, IDynamicRowList
{
private DapperTable _table;
PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
{
if (listAccessors != null && listAccessors.Length != 0) return PropertyDescriptorCollection.Empty;
return DapperRowTypeDescriptor.GetProperties(_table);
}
string ITypedList.GetListName(PropertyDescriptor[] listAccessors) => null;
void IDynamicRowList.SetTable(DapperTable table) => _table = table;
}
private sealed class DapperRowTypeDescriptor : ICustomTypeDescriptor
{
......@@ -60,17 +59,16 @@ AttributeCollection ICustomTypeDescriptor.GetAttributes()
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => EventDescriptorCollection.Empty;
internal static PropertyDescriptorCollection GetProperties(DapperRow row) => GetProperties(row?.table, row);
internal static PropertyDescriptorCollection GetProperties(DapperTable table, IDictionary<string,object> row = null)
internal static PropertyDescriptorCollection GetProperties(DapperRow row) => GetProperties(row?.table);
internal static PropertyDescriptorCollection GetProperties(DapperTable table)
{
string[] names = table?.FieldNames;
if (names == null || names.Length == 0) return PropertyDescriptorCollection.Empty;
var arr = new PropertyDescriptor[names.Length];
var columns = table?.Columns;
if (columns == null || columns.Length == 0) return PropertyDescriptorCollection.Empty;
var arr = new PropertyDescriptor[columns.Length];
for (int i = 0; i < arr.Length; i++)
{
var type = row != null && row.TryGetValue(names[i], out var value) && value != null
? value.GetType() : typeof(object);
arr[i] = new RowBoundPropertyDescriptor(type, names[i], i);
ref readonly DapperTable.DapperColumn col = ref columns[i];
arr[i] = new RowBoundPropertyDescriptor(col.Type, col.Name, i);
}
return new PropertyDescriptorCollection(arr, true);
}
......
......@@ -81,13 +81,13 @@ public override string ToString()
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
var names = table.FieldNames;
for (var i = 0; i < names.Length; i++)
var columns = table.Columns;
for (var i = 0; i < columns.Length; i++)
{
object value = i < values.Length ? values[i] : null;
if (!(value is DeadValue))
{
yield return new KeyValuePair<string, object>(names[i], value);
yield return new KeyValuePair<string, object>(columns[i].Name, value);
}
}
}
......@@ -174,7 +174,7 @@ private object SetValue(string key, object value, bool isAdd)
int index = table.IndexOfName(key);
if (index < 0)
{
index = table.AddField(key);
index = table.AddField(key, typeof(object));
}
else if (isAdd && index < values.Length && !(values[index] is DeadValue))
{
......
using System;
using System.Collections.Generic;
using System.Data;
namespace Dapper
{
public static partial class SqlMapper
{
private sealed class DapperTable
internal sealed class DapperTable
{
private string[] fieldNames;
private readonly Dictionary<string, int> fieldNameLookup;
internal readonly struct DapperColumn
{
public readonly string Name;
public readonly Type Type;
public DapperColumn(string name, Type type)
{
Name = name;
Type = type;
}
}
private DapperColumn[] _columns;
private readonly Dictionary<string, int> _fieldNameLookup;
private readonly int _readerOffset, _readerCount;
internal string[] FieldNames => fieldNames;
internal DapperColumn[] Columns => _columns;
public DapperTable(string[] fieldNames)
private DapperTable(DapperColumn[] columns, int offset)
{
this.fieldNames = fieldNames ?? throw new ArgumentNullException(nameof(fieldNames));
fieldNameLookup = new Dictionary<string, int>(fieldNames.Length, StringComparer.Ordinal);
_readerOffset = offset;
_readerCount = columns.Length;
_columns = columns ?? throw new ArgumentNullException(nameof(columns));
_fieldNameLookup = new Dictionary<string, int>(_columns.Length, StringComparer.Ordinal);
// if there are dups, we want the **first** key to be the "winner" - so iterate backwards
for (int i = fieldNames.Length - 1; i >= 0; i--)
for (int i = columns.Length - 1; i >= 0; i--)
{
string key = fieldNames[i];
if (key != null) fieldNameLookup[key] = i;
string key = columns[i].Name;
if (key != null) _fieldNameLookup[key] = i;
}
}
internal int IndexOfName(string name)
{
return (name != null && fieldNameLookup.TryGetValue(name, out int result)) ? result : -1;
return (name != null && _fieldNameLookup.TryGetValue(name, out int result)) ? result : -1;
}
internal int AddField(string name)
internal int AddField(string name, Type type)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (fieldNameLookup.ContainsKey(name)) throw new InvalidOperationException("Field already exists: " + name);
int oldLen = fieldNames.Length;
Array.Resize(ref fieldNames, oldLen + 1); // yes, this is sub-optimal, but this is not the expected common case
fieldNames[oldLen] = name;
fieldNameLookup[name] = oldLen;
if (_fieldNameLookup.ContainsKey(name)) throw new InvalidOperationException("Field already exists: " + name);
int oldLen = _columns.Length;
Array.Resize(ref _columns, oldLen + 1); // yes, this is sub-optimal, but this is not the expected common case
_columns[oldLen] = new DapperColumn(name, type);
_fieldNameLookup[name] = oldLen;
return oldLen;
}
internal bool FieldExists(string key) => key != null && fieldNameLookup.ContainsKey(key);
public int FieldCount => _columns.Length;
private static readonly DapperColumn[] _nixColumns = new DapperColumn[0];
internal static DapperTable Create(IDataRecord reader, int offset, int count)
{
if (count == 0) return new DapperTable(_nixColumns, offset);
var columns = new DapperColumn[count];
var colIndex = offset;
for (int i = 0; i < count; i++)
{
columns[i] = new DapperColumn(
reader.GetName(colIndex),
reader.GetFieldType(colIndex));
colIndex++;
}
return new DapperTable(columns, offset);
}
internal object AddRow(IDataReader reader)
{
object[] values = new object[_readerCount];
int offset = _readerOffset;
for(int i = 0; i < values.Length; i++)
{
object val = reader.GetValue(offset++);
values[i] = val is DBNull ? null : val;
}
return new DapperRow(this, values);
}
internal object AddRowUnlessFirstMissing(IDataReader reader)
{
int offset = _readerOffset;
object value = reader.GetValue(offset++);
if (value is DBNull) return null;
object[] values = new object[_readerCount];
values[0] = value;
for (int i = 1; i < values.Length; i++)
{
object val = reader.GetValue(offset++);
values[i] = val is DBNull ? null : val;
}
return new DapperRow(this, values);
public int FieldCount => fieldNames.Length;
}
}
}
}
......@@ -5,7 +5,7 @@ namespace Dapper
{
public static partial class SqlMapper
{
private struct DeserializerState
private readonly struct DeserializerState
{
public readonly int Hash;
public readonly Func<IDataReader, object> Func;
......
......@@ -165,15 +165,16 @@ private Task<IEnumerable<T>> ReadAsyncImpl<T>(Type type, bool buffered)
var deserializer = cache.Deserializer;
int hash = GetColumnHash(reader);
var buffer = buffered ? CreateBufferList<T>() : null;
if (deserializer.Func == null || deserializer.Hash != hash)
{
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false, buffer as IDynamicRowList));
cache.Deserializer = deserializer;
}
IsConsumed = true;
if (buffered && reader is DbDataReader)
{
return ReadBufferedAsync<T>(gridIndex, deserializer.Func);
return ReadBufferedAsync<T>(gridIndex, deserializer.Func, buffer);
}
else
{
......@@ -207,7 +208,7 @@ private async Task<T> ReadRowAsyncImplViaDbReader<T>(DbDataReader reader, Type t
int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash)
{
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false, null));
cache.Deserializer = deserializer;
}
result = (T)deserializer.Func(reader);
......@@ -222,12 +223,11 @@ private async Task<T> ReadRowAsyncImplViaDbReader<T>(DbDataReader reader, Type t
return result;
}
private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<IDataReader, object> deserializer)
private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<IDataReader, object> deserializer, List<T> buffer)
{
try
{
var reader = (DbDataReader)this.reader;
var buffer = new List<T>();
while (index == gridIndex && await reader.ReadAsync(cancel).ConfigureAwait(false))
{
buffer.Add((T)deserializer(reader));
......
......@@ -152,14 +152,20 @@ private IEnumerable<T> ReadImpl<T>(Type type, bool buffered)
var deserializer = cache.Deserializer;
int hash = GetColumnHash(reader);
var buffer = buffered ? CreateBufferList<T>() : null;
if (deserializer.Func == null || deserializer.Hash != hash)
{
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false, buffer as IDynamicRowList));
cache.Deserializer = deserializer;
}
IsConsumed = true;
var result = ReadDeferred<T>(gridIndex, deserializer.Func, type);
return buffered ? result.ToList() : result;
if (buffered)
{
buffer.AddRange(result);
return buffer;
}
return result;
}
private T ReadRow<T>(Type type, Row row)
......@@ -178,7 +184,7 @@ private T ReadRow<T>(Type type, Row row)
int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash)
{
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false, null));
cache.Deserializer = deserializer;
}
object val = deserializer.Func(reader);
......
......@@ -16,7 +16,7 @@ public static IEnumerable<T> Parse<T>(this IDataReader reader)
if (reader.Read())
{
var effectiveType = typeof(T);
var deser = GetDeserializer(effectiveType, reader, 0, -1, false);
var deser = GetDeserializer(effectiveType, reader, 0, -1, false, null);
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
do
{
......@@ -42,7 +42,7 @@ public static IEnumerable<object> Parse(this IDataReader reader, Type type)
{
if (reader.Read())
{
var deser = GetDeserializer(type, reader, 0, -1, false);
var deser = GetDeserializer(type, reader, 0, -1, false, null);
do
{
yield return deser(reader);
......@@ -58,7 +58,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
{
if (reader.Read())
{
var deser = GetDapperRowDeserializer(reader, 0, -1, false);
var deser = GetDapperRowDeserializer(reader, 0, -1, false, null);
do
{
yield return deser(reader);
......@@ -79,7 +79,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
public static Func<IDataReader, object> GetRowParser(this IDataReader reader, Type type,
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
{
return GetDeserializer(type, reader, startIndex, length, returnNullIfFirstMissing);
return GetDeserializer(type, reader, startIndex, length, returnNullIfFirstMissing, null);
}
/// <summary>
......@@ -139,7 +139,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
{
concreteType = concreteType ?? typeof(T);
var func = GetDeserializer(concreteType, reader, startIndex, length, returnNullIfFirstMissing);
var func = GetDeserializer(concreteType, reader, startIndex, length, returnNullIfFirstMissing, null);
if (concreteType.IsValueType())
{
return _ => (T)func(_);
......
......@@ -52,7 +52,7 @@ internal static void Purge()
private readonly Dictionary<DeserializerKey, Func<IDataReader, object>> readers = new Dictionary<DeserializerKey, Func<IDataReader, object>>();
private struct DeserializerKey : IEquatable<DeserializerKey>
private readonly struct DeserializerKey : IEquatable<DeserializerKey>
{
private readonly int startBound, length;
private readonly bool returnNullIfFirstMissing;
......
......@@ -647,7 +647,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinitio
/// <param name="commandType">The type of command to execute.</param>
/// <remarks>Note: each row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null) =>
Query<DapperRow>(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);
Query<object>(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);
/// <summary>
/// Return a dynamic object with properties matching the columns.
......@@ -660,7 +660,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinitio
/// <param name="commandType">The type of command to execute.</param>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public static dynamic QueryFirst(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) =>
QueryFirst<DapperRow>(cnn, sql, param as object, transaction, commandTimeout, commandType);
QueryFirst<object>(cnn, sql, param as object, transaction, commandTimeout, commandType);
/// <summary>
/// Return a dynamic object with properties matching the columns.
......@@ -673,7 +673,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinitio
/// <param name="commandType">The type of command to execute.</param>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public static dynamic QueryFirstOrDefault(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) =>
QueryFirstOrDefault<DapperRow>(cnn, sql, param as object, transaction, commandTimeout, commandType);
QueryFirstOrDefault<object>(cnn, sql, param as object, transaction, commandTimeout, commandType);
/// <summary>
/// Return a dynamic object with properties matching the columns.
......@@ -686,7 +686,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinitio
/// <param name="commandType">The type of command to execute.</param>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public static dynamic QuerySingle(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) =>
QuerySingle<DapperRow>(cnn, sql, param as object, transaction, commandTimeout, commandType);
QuerySingle<object>(cnn, sql, param as object, transaction, commandTimeout, commandType);
/// <summary>
/// Return a dynamic object with properties matching the columns.
......@@ -699,7 +699,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinitio
/// <param name="commandType">The type of command to execute.</param>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public static dynamic QuerySingleOrDefault(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) =>
QuerySingleOrDefault<DapperRow>(cnn, sql, param as object, transaction, commandTimeout, commandType);
QuerySingleOrDefault<object>(cnn, sql, param as object, transaction, commandTimeout, commandType);
/// <summary>
/// Executes a query, returning the data typed as <typeparamref name="T"/>.
......@@ -719,8 +719,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinitio
public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
var data = QueryImpl<T>(cnn, command, typeof(T));
return command.Buffered ? data.ToList() : data;
return command.Buffered ? QueryBufferedImpl<T>(cnn, command, typeof(T)) : QueryImpl<T>(cnn, command, typeof(T), null);
}
/// <summary>
......@@ -823,8 +822,7 @@ public static IEnumerable<object> Query(this IDbConnection cnn, Type type, strin
{
if (type == null) throw new ArgumentNullException(nameof(type));
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
var data = QueryImpl<object>(cnn, command, type);
return command.Buffered ? data.ToList() : data;
return command.Buffered ? QueryBufferedImpl<object>(cnn, command, type) : QueryImpl<object>(cnn, command, type, null);
}
/// <summary>
......@@ -926,10 +924,7 @@ public static object QuerySingleOrDefault(this IDbConnection cnn, Type type, str
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive).
/// </returns>
public static IEnumerable<T> Query<T>(this IDbConnection cnn, CommandDefinition command)
{
var data = QueryImpl<T>(cnn, command, typeof(T));
return command.Buffered ? data.ToList() : data;
}
=> command.Buffered ? QueryBufferedImpl<T>(cnn, command, typeof(T)) : QueryImpl<T>(cnn, command, typeof(T), null);
/// <summary>
/// Executes a query, returning the data typed as <typeparamref name="T"/>.
......@@ -1063,7 +1058,30 @@ private static IDataReader ExecuteReaderWithFlagsFallback(IDbCommand cmd, bool w
}
}
private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
internal interface IDynamicRowList
{
void SetTable(DapperTable table);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static List<T> CreateBufferList<T>()
{
#if !NETSTANDARD1_3 // needs the component-model API
if (typeof(T) == typeof(object))
{
return (List<T>)(object)new DapperRow.DapperRowList();
}
#endif
return new List<T>();
}
private static IList<T> QueryBufferedImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
{
List<T> list = CreateBufferList<T>();
list.AddRange(QueryImpl<T>(cnn, command, effectiveType, list));
return list;
}
private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType, List<T> buffer)
{
object param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType(), null);
......@@ -1088,8 +1106,10 @@ private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefini
if (tuple.Func == null || tuple.Hash != hash)
{
if (reader.FieldCount == 0) //https://code.google.com/p/dapper-dot-net/issues/detail?id=57
{
yield break;
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
}
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false, buffer as IDynamicRowList));
if (command.AddToCache) SetQueryCache(identity, info);
}
......@@ -1098,13 +1118,22 @@ private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefini
while (reader.Read())
{
object val = func(reader);
T typed;
if (val == null || val is T)
{
yield return (T)val;
typed = (T)val;
}
else
{
yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
typed = (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
}
if (buffer != null)
{
buffer.Add(typed);
}
else
{
yield return typed;
}
}
while (reader.NextResult()) { /* ignore subsequent result sets */ }
......@@ -1191,7 +1220,7 @@ private static T QueryRowImpl<T>(IDbConnection cnn, Row row, ref CommandDefiniti
int hash = GetColumnHash(reader);
if (tuple.Func == null || tuple.Hash != hash)
{
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false, null));
if (command.AddToCache) SetQueryCache(identity, info);
}
......@@ -1596,7 +1625,7 @@ private static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection cnn
{
currentSplit = splits[++splitIdx];
}
deserializers.Add(GetDeserializer(type, reader, currentPos, splitPoint - currentPos, !first));
deserializers.Add(GetDeserializer(type, reader, currentPos, splitPoint - currentPos, !first, null));
currentPos = splitPoint;
first = false;
}
......@@ -1626,7 +1655,7 @@ private static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection cnn
}
}
deserializers.Add(GetDeserializer(type, reader, splitPoint, currentPos - splitPoint, typeIdx > 0));
deserializers.Add(GetDeserializer(type, reader, splitPoint, currentPos - splitPoint, typeIdx > 0, null));
currentPos = splitPoint;
}
......@@ -1767,12 +1796,12 @@ private static void PassByPosition(IDbCommand cmd)
});
}
private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing, IDynamicRowList rowList)
{
// dynamic is passed in as Object ... by c# design
if (type == typeof(object) || type == typeof(DapperRow))
{
return GetDapperRowDeserializer(reader, startBound, length, returnNullIfFirstMissing);
return GetDapperRowDeserializer(reader, startBound, length, returnNullIfFirstMissing, rowList);
}
Type underlyingType = null;
if (!(typeMap.ContainsKey(type) || type.IsEnum() || type.FullName == LinqBinary
......@@ -1803,7 +1832,7 @@ private static Exception MultiMapException(IDataRecord reader)
return new InvalidOperationException("No columns were selected");
}
internal static Func<IDataReader, object> GetDapperRowDeserializer(IDataRecord reader, int startBound, int length, bool returnNullIfFirstMissing)
internal static Func<IDataReader, object> GetDapperRowDeserializer(IDataRecord reader, int startBound, int length, bool returnNullIfFirstMissing, IDynamicRowList rowList)
{
var fieldCount = reader.FieldCount;
if (length == -1)
......@@ -1818,51 +1847,11 @@ private static Exception MultiMapException(IDataRecord reader)
var effectiveFieldCount = Math.Min(fieldCount - startBound, length);
DapperTable table = null;
DapperTable table = DapperTable.Create(reader, startBound, effectiveFieldCount);
rowList?.SetTable(table);
return
r =>
{
if (table == null)
{
string[] names = new string[effectiveFieldCount];
for (int i = 0; i < effectiveFieldCount; i++)
{
names[i] = r.GetName(i + startBound);
}
table = new DapperTable(names);
}
var values = new object[effectiveFieldCount];
if (returnNullIfFirstMissing)
{
values[0] = r.GetValue(startBound);
if (values[0] is DBNull)
{
return null;
}
}
if (startBound == 0)
{
for (int i = 0; i < values.Length; i++)
{
object val = r.GetValue(i);
values[i] = val is DBNull ? null : val;
}
}
else
{
var begin = returnNullIfFirstMissing ? 1 : 0;
for (var iter = begin; iter < effectiveFieldCount; ++iter)
{
object obj = r.GetValue(iter + startBound);
values[iter] = obj is DBNull ? null : obj;
}
}
return new DapperRow(table, values);
};
if (returnNullIfFirstMissing) return table.AddRowUnlessFirstMissing;
return table.AddRow;
}
/// <summary>
/// Internal use only.
......
......@@ -13,7 +13,7 @@ public BindingForm()
SuspendLayout();
using (var conn = new SqlConnection("Data Source=.;Initial Catalog=master;Integrated Security=SSPI"))
{
mainGrid.DataSource = conn.Query("select * from sys.objects").AsList();
mainGrid.DataSource = conn.Query("select * from sys.objects where 1 = 0").AsList();
}
ResumeLayout();
}
......
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