Skip to content

Commit 01f03ef

Browse files
authored
add support for fetching values via GetFieldValue<T> (#1910)
add support for fetching values via GetFieldValue<T>; this requires DbDataReader, but in reality the reader is *always* DbDataReader; acknowledge this, and stop pretending to use IDataReader internally (no breaks to public API)
1 parent a31dfd3 commit 01f03ef

14 files changed

+586
-208
lines changed

Dapper/SqlMapper.Async.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,7 @@ private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbC
967967
}
968968
}
969969

970-
private static IEnumerable<T> ExecuteReaderSync<T>(IDataReader reader, Func<IDataReader, object> func, object parameters)
970+
private static IEnumerable<T> ExecuteReaderSync<T>(DbDataReader reader, Func<DbDataReader, object> func, object parameters)
971971
{
972972
using (reader)
973973
{
@@ -1004,7 +1004,7 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
10041004
CacheInfo info = GetCacheInfo(identity, param, command.AddToCache);
10051005

10061006
DbCommand cmd = null;
1007-
IDataReader reader = null;
1007+
DbDataReader reader = null;
10081008
bool wasClosed = cnn.State == ConnectionState.Closed;
10091009
try
10101010
{

Dapper/SqlMapper.CacheInfo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data;
3+
using System.Data.Common;
34
using System.Threading;
45

56
namespace Dapper
@@ -9,7 +10,7 @@ public static partial class SqlMapper
910
private class CacheInfo
1011
{
1112
public DeserializerState Deserializer { get; set; }
12-
public Func<IDataReader, object>[] OtherDeserializers { get; set; }
13+
public Func<DbDataReader, object>[] OtherDeserializers { get; set; }
1314
public Action<IDbCommand, object> ParamReader { get; set; }
1415
private int hitCount;
1516
public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }

Dapper/SqlMapper.DeserializerState.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data;
3+
using System.Data.Common;
34

45
namespace Dapper
56
{
@@ -8,9 +9,9 @@ public static partial class SqlMapper
89
private struct DeserializerState
910
{
1011
public readonly int Hash;
11-
public readonly Func<IDataReader, object> Func;
12+
public readonly Func<DbDataReader, object> Func;
1213

13-
public DeserializerState(int hash, Func<IDataReader, object> func)
14+
public DeserializerState(int hash, Func<DbDataReader, object> func)
1415
{
1516
Hash = hash;
1617
Func = func;

Dapper/SqlMapper.GridReader.Async.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static partial class SqlMapper
1414
public partial class GridReader
1515
{
1616
private readonly CancellationToken cancel;
17-
internal GridReader(IDbCommand command, IDataReader reader, Identity identity, DynamicParameters dynamicParams, bool addToCache, CancellationToken cancel)
17+
internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, DynamicParameters dynamicParams, bool addToCache, CancellationToken cancel)
1818
: this(command, reader, identity, dynamicParams, addToCache)
1919
{
2020
this.cancel = cancel;
@@ -225,7 +225,7 @@ private async Task<T> ReadRowAsyncImplViaDbReader<T>(DbDataReader reader, Type t
225225
return result;
226226
}
227227

228-
private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<IDataReader, object> deserializer)
228+
private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<DbDataReader, object> deserializer)
229229
{
230230
try
231231
{

Dapper/SqlMapper.GridReader.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Globalization;
66
using System.Runtime.CompilerServices;
7+
using System.Data.Common;
78

89
namespace Dapper
910
{
@@ -14,11 +15,11 @@ public static partial class SqlMapper
1415
/// </summary>
1516
public partial class GridReader : IDisposable
1617
{
17-
private IDataReader reader;
18+
private DbDataReader reader;
1819
private readonly Identity identity;
1920
private readonly bool addToCache;
2021

21-
internal GridReader(IDbCommand command, IDataReader reader, Identity identity, IParameterCallbacks callbacks, bool addToCache)
22+
internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, IParameterCallbacks callbacks, bool addToCache)
2223
{
2324
Command = command;
2425
this.reader = reader;
@@ -351,7 +352,7 @@ public IEnumerable<TReturn> Read<TReturn>(Type[] types, Func<object[], TReturn>
351352
return buffered ? result.ToList() : result;
352353
}
353354

354-
private IEnumerable<T> ReadDeferred<T>(int index, Func<IDataReader, object> deserializer, Type effectiveType)
355+
private IEnumerable<T> ReadDeferred<T>(int index, Func<DbDataReader, object> deserializer, Type effectiveType)
355356
{
356357
try
357358
{

Dapper/SqlMapper.IDataReader.cs

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Data;
4+
using System.Data.Common;
45

56
namespace Dapper
67
{
@@ -13,14 +14,15 @@ public static partial class SqlMapper
1314
/// <param name="reader">The data reader to parse results from.</param>
1415
public static IEnumerable<T> Parse<T>(this IDataReader reader)
1516
{
16-
if (reader.Read())
17+
var dbReader = GetDbDataReader(reader, false);
18+
if (dbReader.Read())
1719
{
1820
var effectiveType = typeof(T);
19-
var deser = GetDeserializer(effectiveType, reader, 0, -1, false);
21+
var deser = GetDeserializer(effectiveType, dbReader, 0, -1, false);
2022
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
2123
do
2224
{
23-
object val = deser(reader);
25+
object val = deser(dbReader);
2426
if (val == null || val is T)
2527
{
2628
yield return (T)val;
@@ -29,7 +31,7 @@ public static IEnumerable<T> Parse<T>(this IDataReader reader)
2931
{
3032
yield return (T)Convert.ChangeType(val, convertToType, System.Globalization.CultureInfo.InvariantCulture);
3133
}
32-
} while (reader.Read());
34+
} while (dbReader.Read());
3335
}
3436
}
3537

@@ -40,13 +42,14 @@ public static IEnumerable<T> Parse<T>(this IDataReader reader)
4042
/// <param name="type">The type to parse from the <paramref name="reader"/>.</param>
4143
public static IEnumerable<object> Parse(this IDataReader reader, Type type)
4244
{
43-
if (reader.Read())
45+
var dbReader = GetDbDataReader(reader, false);
46+
if (dbReader.Read())
4447
{
45-
var deser = GetDeserializer(type, reader, 0, -1, false);
48+
var deser = GetDeserializer(type, dbReader, 0, -1, false);
4649
do
4750
{
48-
yield return deser(reader);
49-
} while (reader.Read());
51+
yield return deser(dbReader);
52+
} while (dbReader.Read());
5053
}
5154
}
5255

@@ -56,13 +59,14 @@ public static IEnumerable<object> Parse(this IDataReader reader, Type type)
5659
/// <param name="reader">The data reader to parse results from.</param>
5760
public static IEnumerable<dynamic> Parse(this IDataReader reader)
5861
{
59-
if (reader.Read())
62+
var dbReader = GetDbDataReader(reader, false);
63+
if (dbReader.Read())
6064
{
61-
var deser = GetDapperRowDeserializer(reader, 0, -1, false);
65+
var deser = GetDapperRowDeserializer(dbReader, 0, -1, false);
6266
do
6367
{
64-
yield return deser(reader);
65-
} while (reader.Read());
68+
yield return deser(dbReader);
69+
} while (dbReader.Read());
6670
}
6771
}
6872

@@ -76,12 +80,47 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
7680
/// <param name="length">The length of columns to read (default -1 = all fields following startIndex)</param>
7781
/// <param name="returnNullIfFirstMissing">Return null if we can't find the first column? (default false)</param>
7882
/// <returns>A parser for this specific object from this row.</returns>
83+
#if DEBUG // make sure we're not using this internally
84+
[Obsolete(nameof(DbDataReader) + " API should be preferred")]
85+
#endif
7986
public static Func<IDataReader, object> GetRowParser(this IDataReader reader, Type type,
8087
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
88+
{
89+
return WrapObjectReader(GetDeserializer(type, GetDbDataReader(reader, false), startIndex, length, returnNullIfFirstMissing));
90+
}
91+
92+
/// <summary>
93+
/// Gets the row parser for a specific row on a data reader. This allows for type switching every row based on, for example, a TypeId column.
94+
/// You could return a collection of the base type but have each more specific.
95+
/// </summary>
96+
/// <param name="reader">The data reader to get the parser for the current row from</param>
97+
/// <param name="type">The type to get the parser for</param>
98+
/// <param name="startIndex">The start column index of the object (default 0)</param>
99+
/// <param name="length">The length of columns to read (default -1 = all fields following startIndex)</param>
100+
/// <param name="returnNullIfFirstMissing">Return null if we can't find the first column? (default false)</param>
101+
/// <returns>A parser for this specific object from this row.</returns>
102+
public static Func<DbDataReader, object> GetRowParser(this DbDataReader reader, Type type,
103+
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
81104
{
82105
return GetDeserializer(type, reader, startIndex, length, returnNullIfFirstMissing);
83106
}
84107

108+
/// <inheritdoc cref="GetRowParser{T}(DbDataReader, Type, int, int, bool)"/>
109+
#if DEBUG // make sure we're not using this internally
110+
[Obsolete(nameof(DbDataReader) + " API should be preferred")]
111+
#endif
112+
public static Func<IDataReader, T> GetRowParser<T>(this IDataReader reader, Type concreteType = null,
113+
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
114+
{
115+
concreteType ??= typeof(T);
116+
var func = GetDeserializer(concreteType, GetDbDataReader(reader, false), startIndex, length, returnNullIfFirstMissing);
117+
return Wrap(func);
118+
119+
// this is just to be very clear about what we're capturing
120+
static Func<IDataReader, T> Wrap(Func<DbDataReader, object> func)
121+
=> reader => (T)func(GetDbDataReader(reader, false));
122+
}
123+
85124
/// <summary>
86125
/// Gets the row parser for a specific row on a data reader. This allows for type switching every row based on, for example, a TypeId column.
87126
/// You could return a collection of the base type but have each more specific.
@@ -135,7 +174,7 @@ public static Func<IDataReader, object> GetRowParser(this IDataReader reader, Ty
135174
/// public override int Type =&gt; 2;
136175
/// }
137176
/// </example>
138-
public static Func<IDataReader, T> GetRowParser<T>(this IDataReader reader, Type concreteType = null,
177+
public static Func<DbDataReader, T> GetRowParser<T>(this DbDataReader reader, Type concreteType = null,
139178
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
140179
{
141180
concreteType ??= typeof(T);
@@ -146,7 +185,7 @@ public static Func<IDataReader, T> GetRowParser<T>(this IDataReader reader, Type
146185
}
147186
else
148187
{
149-
return (Func<IDataReader, T>)(Delegate)func;
188+
return (Func<DbDataReader, T>)(Delegate)func;
150189
}
151190
}
152191
}

Dapper/SqlMapper.TypeDeserializerCache.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections;
44
using System.Collections.Generic;
55
using System.Text;
6+
using System.Data.Common;
67

78
namespace Dapper
89
{
@@ -33,7 +34,7 @@ internal static void Purge()
3334
}
3435
}
3536

36-
internal static Func<IDataReader, object> GetReader(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
37+
internal static Func<DbDataReader, object> GetReader(Type type, DbDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
3738
{
3839
var found = (TypeDeserializerCache)byType[type];
3940
if (found == null)
@@ -50,18 +51,18 @@ internal static Func<IDataReader, object> GetReader(Type type, IDataReader reade
5051
return found.GetReader(reader, startBound, length, returnNullIfFirstMissing);
5152
}
5253

53-
private readonly Dictionary<DeserializerKey, Func<IDataReader, object>> readers = new Dictionary<DeserializerKey, Func<IDataReader, object>>();
54+
private readonly Dictionary<DeserializerKey, Func<DbDataReader, object>> readers = new Dictionary<DeserializerKey, Func<DbDataReader, object>>();
5455

5556
private struct DeserializerKey : IEquatable<DeserializerKey>
5657
{
5758
private readonly int startBound, length;
5859
private readonly bool returnNullIfFirstMissing;
59-
private readonly IDataReader reader;
60+
private readonly DbDataReader reader;
6061
private readonly string[] names;
6162
private readonly Type[] types;
6263
private readonly int hashCode;
6364

64-
public DeserializerKey(int hashCode, int startBound, int length, bool returnNullIfFirstMissing, IDataReader reader, bool copyDown)
65+
public DeserializerKey(int hashCode, int startBound, int length, bool returnNullIfFirstMissing, DbDataReader reader, bool copyDown)
6566
{
6667
this.hashCode = hashCode;
6768
this.startBound = startBound;
@@ -136,14 +137,14 @@ public bool Equals(DeserializerKey other)
136137
}
137138
}
138139

139-
private Func<IDataReader, object> GetReader(IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
140+
private Func<DbDataReader, object> GetReader(DbDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
140141
{
141142
if (length < 0) length = reader.FieldCount - startBound;
142143
int hash = GetColumnHash(reader, startBound, length);
143144
if (returnNullIfFirstMissing) hash *= -27;
144145
// get a cheap key first: false means don't copy the values down
145146
var key = new DeserializerKey(hash, startBound, length, returnNullIfFirstMissing, reader, false);
146-
Func<IDataReader, object> deser;
147+
Func<DbDataReader, object> deser;
147148
lock (readers)
148149
{
149150
if (readers.TryGetValue(key, out deser)) return deser;

0 commit comments

Comments
 (0)