Skip to content

Commit d91d904

Browse files
authored
reinstate support for IDataReader (#1913)
* if we stop supporting DbDataReader, someone is going to complain; I'd rather wrap it for now (to make the lib work), and add an analyzer in AOT that shouts at them for using IDbConnection also: compiler nits * release notes
1 parent 677fee2 commit d91d904

File tree

6 files changed

+199
-51
lines changed

6 files changed

+199
-51
lines changed

Dapper/SqlMapper.Async.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,7 @@ private static async Task<DbDataReader> ExecuteWrappedReaderImplAsync(IDbConnect
11361136
var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, commandBehavior, command.CancellationToken).ConfigureAwait(false);
11371137
wasClosed = false;
11381138
disposeCommand = false;
1139-
return WrappedReader.Create(cmd, reader);
1139+
return DbWrappedReader.Create(cmd, reader);
11401140
}
11411141
finally
11421142
{

Dapper/SqlMapper.IDataReader.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static partial class SqlMapper
1414
/// <param name="reader">The data reader to parse results from.</param>
1515
public static IEnumerable<T> Parse<T>(this IDataReader reader)
1616
{
17-
var dbReader = GetDbDataReader(reader, false);
17+
var dbReader = GetDbDataReader(reader);
1818
if (dbReader.Read())
1919
{
2020
var effectiveType = typeof(T);
@@ -42,7 +42,7 @@ public static IEnumerable<T> Parse<T>(this IDataReader reader)
4242
/// <param name="type">The type to parse from the <paramref name="reader"/>.</param>
4343
public static IEnumerable<object> Parse(this IDataReader reader, Type type)
4444
{
45-
var dbReader = GetDbDataReader(reader, false);
45+
var dbReader = GetDbDataReader(reader);
4646
if (dbReader.Read())
4747
{
4848
var deser = GetDeserializer(type, dbReader, 0, -1, false);
@@ -59,7 +59,7 @@ public static IEnumerable<object> Parse(this IDataReader reader, Type type)
5959
/// <param name="reader">The data reader to parse results from.</param>
6060
public static IEnumerable<dynamic> Parse(this IDataReader reader)
6161
{
62-
var dbReader = GetDbDataReader(reader, false);
62+
var dbReader = GetDbDataReader(reader);
6363
if (dbReader.Read())
6464
{
6565
var deser = GetDapperRowDeserializer(dbReader, 0, -1, false);
@@ -86,7 +86,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
8686
public static Func<IDataReader, object> GetRowParser(this IDataReader reader, Type type,
8787
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
8888
{
89-
return WrapObjectReader(GetDeserializer(type, GetDbDataReader(reader, false), startIndex, length, returnNullIfFirstMissing));
89+
return WrapObjectReader(GetDeserializer(type, GetDbDataReader(reader), startIndex, length, returnNullIfFirstMissing));
9090
}
9191

9292
/// <summary>
@@ -113,12 +113,12 @@ public static Func<IDataReader, T> GetRowParser<T>(this IDataReader reader, Type
113113
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
114114
{
115115
concreteType ??= typeof(T);
116-
var func = GetDeserializer(concreteType, GetDbDataReader(reader, false), startIndex, length, returnNullIfFirstMissing);
116+
var func = GetDeserializer(concreteType, GetDbDataReader(reader), startIndex, length, returnNullIfFirstMissing);
117117
return Wrap(func);
118118

119119
// this is just to be very clear about what we're capturing
120120
static Func<IDataReader, T> Wrap(Func<DbDataReader, object> func)
121-
=> reader => (T)func(GetDbDataReader(reader, false));
121+
=> reader => (T)func(GetDbDataReader(reader));
122122
}
123123

124124
/// <summary>

Dapper/SqlMapper.cs

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ private static void OnQueryCachePurged()
5858
handler?.Invoke(null, EventArgs.Empty);
5959
}
6060

61-
private static readonly System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo> _queryCache = new System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo>();
61+
private static readonly System.Collections.Concurrent.ConcurrentDictionary<Identity, CacheInfo> _queryCache = new();
6262
private static void SetQueryCache(Identity key, CacheInfo value)
6363
{
6464
if (Interlocked.Increment(ref collect) == COLLECT_PER_ITEMS)
@@ -188,11 +188,11 @@ public TypeMapEntry(DbType dbType, TypeMapEntryFlags flags)
188188
public override bool Equals(object obj) => obj is TypeMapEntry other && Equals(other);
189189
public bool Equals(TypeMapEntry other) => other.DbType == DbType && other.Flags == Flags;
190190
public static readonly TypeMapEntry
191-
DoNotSet = new TypeMapEntry((DbType)(-2), TypeMapEntryFlags.None),
192-
DecimalFieldValue = new TypeMapEntry(DbType.Decimal, TypeMapEntryFlags.SetType | TypeMapEntryFlags.UseGetFieldValue);
191+
DoNotSet = new((DbType)(-2), TypeMapEntryFlags.None),
192+
DecimalFieldValue = new(DbType.Decimal, TypeMapEntryFlags.SetType | TypeMapEntryFlags.UseGetFieldValue);
193193

194194
public static implicit operator TypeMapEntry(DbType dbType)
195-
=> new TypeMapEntry(dbType, TypeMapEntryFlags.SetType);
195+
=> new(dbType, TypeMapEntryFlags.SetType);
196196
}
197197

198198
static SqlMapper()
@@ -677,7 +677,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, obje
677677
{
678678
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
679679
var reader = ExecuteReaderImpl(cnn, ref command, CommandBehavior.Default, out IDbCommand dbcmd);
680-
return WrappedReader.Create(dbcmd, reader);
680+
return DbWrappedReader.Create(dbcmd, reader);
681681
}
682682

683683
/// <summary>
@@ -693,7 +693,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, string sql, obje
693693
public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinition command)
694694
{
695695
var reader = ExecuteReaderImpl(cnn, ref command, CommandBehavior.Default, out IDbCommand dbcmd);
696-
return WrappedReader.Create(dbcmd, reader);
696+
return DbWrappedReader.Create(dbcmd, reader);
697697
}
698698

699699
/// <summary>
@@ -710,7 +710,7 @@ public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinitio
710710
public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinition command, CommandBehavior commandBehavior)
711711
{
712712
var reader = ExecuteReaderImpl(cnn, ref command, commandBehavior, out IDbCommand dbcmd);
713-
return WrappedReader.Create(dbcmd, reader);
713+
return DbWrappedReader.Create(dbcmd, reader);
714714
}
715715

716716
/// <summary>
@@ -1842,13 +1842,13 @@ private static void PassByPosition(IDbCommand cmd)
18421842
{
18431843
if (cmd.Parameters.Count == 0) return;
18441844

1845-
Dictionary<string, IDbDataParameter> parameters = new Dictionary<string, IDbDataParameter>(StringComparer.Ordinal);
1845+
Dictionary<string, IDbDataParameter> parameters = new(StringComparer.Ordinal);
18461846

18471847
foreach (IDbDataParameter param in cmd.Parameters)
18481848
{
18491849
if (!string.IsNullOrEmpty(param.ParameterName)) parameters[param.ParameterName] = param;
18501850
}
1851-
HashSet<string> consumed = new HashSet<string>(StringComparer.Ordinal);
1851+
var consumed = new HashSet<string>(StringComparer.Ordinal);
18521852
bool firstMatch = true;
18531853
int index = 0; // use this to spoof names; in most pseudo-positional cases, the name is ignored, however:
18541854
// for "snowflake", the name needs to be incremental i.e. "1", "2", "3"
@@ -1884,22 +1884,9 @@ private static void PassByPosition(IDbCommand cmd)
18841884
});
18851885
}
18861886

1887-
static DbDataReader GetDbDataReader(IDataReader reader, bool disposeOnFail = true)
1887+
static DbDataReader GetDbDataReader(IDataReader reader)
18881888
{
1889-
return reader as DbDataReader ?? Throw(reader, disposeOnFail);
1890-
static DbDataReader Throw(IDataReader reader, bool disposeOnFail)
1891-
{
1892-
if (reader is null)
1893-
{
1894-
throw new ArgumentNullException(nameof(reader));
1895-
}
1896-
if (disposeOnFail)
1897-
{
1898-
reader.Dispose(); // don't leak
1899-
}
1900-
// in reality, all providers have satisfied this since forever; we should have made Dapper target DbConnection, oops!
1901-
throw new NotSupportedException("The provided reader is required to be a DbDataReader, and is not");
1902-
}
1889+
return reader as DbDataReader ?? new WrappedBasicReader(reader);
19031890
}
19041891

19051892
private static Func<DbDataReader, object> GetDeserializer(Type type, DbDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
@@ -2171,7 +2158,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
21712158
}
21722159

21732160
var tmp = listParam.Value = SanitizeParameterValue(item);
2174-
if (tmp != null && !(tmp is DBNull))
2161+
if (tmp != null && tmp is not DBNull)
21752162
lastValue = tmp; // only interested in non-trivial values for padding
21762163

21772164
if (DynamicParameters.ShouldSetDbType(dbType) && listParam.DbType != dbType.GetValueOrDefault())
@@ -2277,7 +2264,7 @@ private static bool TryStringSplit(ref IEnumerable list, int splitAt, string nam
22772264
private static bool TryStringSplit<T>(ref IEnumerable<T> list, int splitAt, string namePrefix, IDbCommand command, string colType, bool byPosition,
22782265
Action<StringBuilder, T> append)
22792266
{
2280-
if (!(list is ICollection<T> typed))
2267+
if (list is not ICollection<T> typed)
22812268
{
22822269
typed = list.ToList();
22832270
list = typed; // because we still need to be able to iterate it, even if we fail here
@@ -2371,9 +2358,9 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn
23712358
}
23722359

23732360
// look for ? / @ / : *by itself*
2374-
private static readonly Regex smellsLikeOleDb = new Regex(@"(?<![\p{L}\p{N}@_])[?@:](?![\p{L}\p{N}@_])", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled),
2375-
literalTokens = new Regex(@"(?<![\p{L}\p{N}_])\{=([\p{L}\p{N}_]+)\}", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled),
2376-
pseudoPositional = new Regex(@"\?([\p{L}_][\p{L}\p{N}_]*)\?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
2361+
private static readonly Regex smellsLikeOleDb = new(@"(?<![\p{L}\p{N}@_])[?@:](?![\p{L}\p{N}@_])", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled),
2362+
literalTokens = new(@"(?<![\p{L}\p{N}_])\{=([\p{L}\p{N}_]+)\}", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled),
2363+
pseudoPositional = new(@"\?([\p{L}_][\p{L}\p{N}_]*)\?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
23772364

23782365
/// <summary>
23792366
/// Replace all literal tokens with their text form.
@@ -2483,7 +2470,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
24832470

24842471
var matches = literalTokens.Matches(sql);
24852472
var found = new HashSet<string>(StringComparer.Ordinal);
2486-
List<LiteralToken> list = new List<LiteralToken>(matches.Count);
2473+
var list = new List<LiteralToken>(matches.Count);
24872474
foreach (Match match in matches)
24882475
{
24892476
string token = match.Value;
@@ -3091,7 +3078,7 @@ static Func<DbDataReader, object> ReadViaGetFieldValueFactory(Type type, int ind
30913078
return factory(index);
30923079
}
30933080
// cache of ReadViaGetFieldValueFactory<T> for per-value T
3094-
static readonly Hashtable s_ReadViaGetFieldValueCache = new Hashtable();
3081+
static readonly Hashtable s_ReadViaGetFieldValueCache = new();
30953082

30963083
static Func<DbDataReader, object> UnderlyingReadViaGetFieldValueFactory<T>(int index)
30973084
=> reader => reader.IsDBNull(index) ? null : reader.GetFieldValue<T>(index);
@@ -3165,7 +3152,7 @@ public static ITypeMap GetTypeMap(Type type)
31653152
}
31663153

31673154
// use Hashtable to get free lockless reading
3168-
private static readonly Hashtable _typeMaps = new Hashtable();
3155+
private static readonly Hashtable _typeMaps = new();
31693156

31703157
/// <summary>
31713158
/// Set custom mapping for type deserializers
@@ -3211,7 +3198,7 @@ public static Func<IDataReader, object> GetTypeDeserializer(
32113198
Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false
32123199
)
32133200
{
3214-
return WrapObjectReader(GetTypeDeserializer(type, GetDbDataReader(reader, false), startBound, length, returnNullIfFirstMissing));
3201+
return WrapObjectReader(GetTypeDeserializer(type, GetDbDataReader(reader), startBound, length, returnNullIfFirstMissing));
32153202
}
32163203

32173204
private static Func<IDataReader, object> WrapObjectReader(Func<DbDataReader, object> dbReader)
@@ -3684,10 +3671,7 @@ private static void LoadReaderValueOrBranchToDBNullLabel(ILGenerator il, int ind
36843671
Type numericType = Enum.GetUnderlyingType(unboxType);
36853672
if (colType == typeof(string))
36863673
{
3687-
if (stringEnumLocal == null)
3688-
{
3689-
stringEnumLocal = il.DeclareLocal(typeof(string));
3690-
}
3674+
stringEnumLocal ??= il.DeclareLocal(typeof(string));
36913675
il.Emit(OpCodes.Castclass, typeof(string)); // stack is now [...][string]
36923676
il.Emit(OpCodes.Stloc, stringEnumLocal); // stack is now [...]
36933677
il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [...][enum-type-token]

0 commit comments

Comments
 (0)