...
 
Commits (18)
    https://gitcode.net/jobily/Dapper/-/commit/33e0143002b5f170ae0bb9152d15625cd99c8a00 Builds: Bump library versions (#1935) 2023-07-08T10:28:18-04:00 Nick Craver nickcraver@microsoft.com https://gitcode.net/jobily/Dapper/-/commit/136dc11f0cd1fbab1e23fedbd58d29ca141bfd7d Tests: Upgrade dependencies for Dependabot (#1936) 2023-07-08T11:43:58-04:00 Nick Craver nickcraver@microsoft.com I'll do a pass at all of these later, but getting CVE versions out of the pipe. Resolving 4 alerts here: <a href="https://github.com/DapperLib/Dapper/security/dependabot" rel="nofollow noreferrer noopener" target="_blank">https://github.com/DapperLib/Dapper/security/dependabot</a> https://gitcode.net/jobily/Dapper/-/commit/a7e708a6185bb5a1bd42da082b65f7e3897fa2ae Create FUNDING.yml 2023-08-14T17:03:01+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/19355d5c5c869f4de0d0201366ce63ba5c9e69e1 Update FUNDING.yml 2023-08-14T17:04:38+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/33090c0218383411c3b25fa6cb4cbee38d0f3270 Underscore handling (#1947) 2023-08-17T17:30:34+01:00 Marc Gravell marc.gravell@gmail.com * Adds MatchConstructorParametersWithUnderscores option * generalize to single MatchNamesWithUnderscores (pre-existing) * cite 2nd PR --------- Co-authored-by: <span data-trailer="Co-authored-by:"><a href="mailto:goronczy.jonas@gmail.com" title="goronczy.jonas@gmail.com"></a><a href="javascript:void(0)" class="avatar s16 avatar-inline identicon bg4" style="text-decoration: none">N</a><a href="mailto:goronczy.jonas@gmail.com" title="goronczy.jonas@gmail.com">Jonas Goronczy</a> &lt;<a href="mailto:goronczy.jonas@gmail.com" title="goronczy.jonas@gmail.com">goronczy.jonas@gmail.com</a>&gt;</span> https://gitcode.net/jobily/Dapper/-/commit/8c9b4bde3cdb03e263403bedf0eed83fb6491467 Add FetchSize global setting (#1946) 2023-08-17T20:14:10+01:00 Marc Gravell marc.gravell@gmail.com * fix #1945 * PR number * docs https://gitcode.net/jobily/Dapper/-/commit/63fb7bae11ebf9de02379b7a7b58b663101044ac release notes 2.0.151 2023-08-18T10:47:57+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/67f7a1fe173545a58ded63961570f5bc8ba73774 add Microsoft.CodeAnalysis.PublicApiAnalyzers on the main libs (#1948) 2023-08-18T11:47:37+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/ff98f8dbf1184ea155cce25ea01af94a31fb043c answer #1950 using BDN (#1951) 2023-08-21T11:29:37+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/22adf2488a4e057832e0fa5137ee26ef432f0cce Missing curly brace from code example (#1830) 2023-08-24T12:56:23+01:00 William Cole Boren 64283810+williycole@users.noreply.github.com * Update Readme.md * Added additional braces * Update Readme.md https://gitcode.net/jobily/Dapper/-/commit/ff913be39109a1fdac9caa5a32ff3d32d9acb2a0 Removed block nesting levels in a few tests by using block-scoped `using` sta... 2023-08-24T13:12:33+01:00 Lehonti Ramos 17771375+Lehonti@users.noreply.github.com Co-authored-by: <span data-trailer="Co-authored-by:"><a href="mailto:john@doe" title="john@doe"></a><a href="javascript:void(0)" class="avatar s16 avatar-inline identicon bg5" style="text-decoration: none">N</a><a href="mailto:john@doe" title="john@doe">John Doe</a> &lt;<a href="mailto:john@doe" title="john@doe">john@doe</a>&gt;</span> https://gitcode.net/jobily/Dapper/-/commit/8a68070b8e05e6ed6ff24a4f0d25ca2a156139a6 Support $ as parameter prefix (#1952) 2023-09-03T14:48:12+01:00 Giorgi Dalakishvili Giorgi@users.noreply.github.com https://gitcode.net/jobily/Dapper/-/commit/aecffecae1fd3d2a15bf346bcf3d6bee69b09830 attempt NRT makeover (#1928) 2023-09-12T15:58:49+01:00 Marc Gravell marc.gravell@gmail.com * attempt NRT makeover - annotate NRTs on GridReader API - add protected access to some of the GridReader internals - switch GridReader callbacks to be more type-independent docs lib updates; deal with yellow warnings and test failures (SQL server certs and cancellation surfacing differently) fix break simplify proposed GridReader changes to protected OnBeforeGrid and OnAfterGrid[Async] Builds: Bump library versions (#1935) Tests: Upgrade dependencies for Dependabot (#1936) I'll do a pass at all of these later, but getting CVE versions out of the pipe. Resolving 4 alerts here: <a href="https://github.com/DapperLib/Dapper/security/dependabot" rel="nofollow noreferrer noopener" target="_blank">https://github.com/DapperLib/Dapper/security/dependabot</a> merge and lib updates allow Identity to be constructed on-the-fly inside GridReader fix build warnings include readme rev minor - enable [SkipLocalsInit] everywhere - use NET5_0_OR_GREATER for remoting check # Conflicts: # Dapper/CommandDefinition.cs # Dapper/DefaultTypeMap.cs # Dapper/SqlMapper.Async.cs # Dapper/SqlMapper.IDataReader.cs # Dapper/SqlMapper.Link.cs # Dapper/SqlMapper.cs # Directory.Build.props * shipped, not unshipped * fixup SqlBuilder; use is null / is not null * use central package management (#1949) * Nullable test tweaks * Nuget: let's just remove it! * 2 test fixes from review * fix FindExplicitConstructor * add GetPropertySetterOrThrow * fix NRT on FindConstructor * DapperRow: value is object? * use NotNullWhen * make constructor problem more obvious * test fixes --------- Co-authored-by: <span data-trailer="Co-authored-by:"><a href="mailto:nrcraver@gmail.com" title="nrcraver@gmail.com"></a><a href="javascript:void(0)" class="avatar s16 avatar-inline identicon bg4" style="text-decoration: none">N</a><a href="mailto:nrcraver@gmail.com" title="nrcraver@gmail.com">Nick Craver</a> &lt;<a href="mailto:nrcraver@gmail.com" title="nrcraver@gmail.com">nrcraver@gmail.com</a>&gt;</span> https://gitcode.net/jobily/Dapper/-/commit/a2d539ccd5fcf1744f0eb6fc8fc2fcf6bb9731ec release notes 2.1.1 2023-09-12T16:12:56+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/61ff85a2b86acb046231ccfcafbcf1276814220b add missing ReadUnbufferedAsync untyped API (#1958) 2023-09-13T14:58:12+01:00 Marc Gravell marc.gravell@gmail.com * - add missing ReadUnbufferedAsync untyped API - remove an impossible branch * release notes https://gitcode.net/jobily/Dapper/-/commit/a4a55f5b798ea028692432a86edbbe074adbcd06 assert that type handlers don't need object? (#1960) 2023-09-13T21:43:13+01:00 Marc Gravell marc.gravell@gmail.com * assert that type handlers don't need object? - include test to validate (plus: NRT check was happy) fix #1959 * release notes https://gitcode.net/jobily/Dapper/-/commit/3c4ae7d74a18ec313cffdf14c0338d47695289c5 release notes 2.1.4 2023-09-13T21:52:03+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/47ef1e7100b7ecdb68bcf97432b9674373a6f75c missed an object? type-handler 2023-09-14T06:25:35+01:00 Marc Gravell marc.gravell@gmail.com
# These are supported funding model platforms
github: [mgravell, dapperlib]
custom: ["https://www.buymeacoffee.com/marcgravell"]
...@@ -10,14 +10,14 @@ ...@@ -10,14 +10,14 @@
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageId>Dapper.EntityFramework.StrongName</PackageId> <PackageId>Dapper.EntityFramework.StrongName</PackageId>
<PackageTags>orm;sql;micro-orm</PackageTags> <PackageTags>orm;sql;micro-orm</PackageTags>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Dapper.EntityFramework\**\*.cs" Exclude="..\Dapper.EntityFramework\obj\**\*.cs" /> <Compile Include="..\Dapper.EntityFramework\**\*.cs" Exclude="..\Dapper.EntityFramework\obj\**\*.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Dapper.StrongName\Dapper.StrongName.csproj" /> <ProjectReference Include="..\Dapper.StrongName\Dapper.StrongName.csproj" />
<!-- note: 6.2.0 has regressions; don't force the update --> <PackageReference Include="EntityFramework" />
<PackageReference Include="EntityFramework" Version="6.1.3" /> <PackageReference Include="Microsoft.SqlServer.Types" />
<PackageReference Include="Microsoft.SqlServer.Types" Version="14.0.1016.290" />
</ItemGroup> </ItemGroup>
</Project> </Project>
...@@ -7,11 +7,16 @@ ...@@ -7,11 +7,16 @@
<Authors>Marc Gravell;Nick Craver</Authors> <Authors>Marc Gravell;Nick Craver</Authors>
<TargetFrameworks>net461</TargetFrameworks> <TargetFrameworks>net461</TargetFrameworks>
<PackageTags>orm;sql;micro-orm</PackageTags> <PackageTags>orm;sql;micro-orm</PackageTags>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Dapper\Dapper.csproj" /> <ProjectReference Include="..\Dapper\Dapper.csproj" />
<!-- note: 6.2.0 has regressions; don't force the update --> <PackageReference Include="EntityFramework" />
<PackageReference Include="EntityFramework" Version="6.1.3" /> <PackageReference Include="Microsoft.SqlServer.Types" />
<PackageReference Include="Microsoft.SqlServer.Types" Version="14.0.1016.290" />
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>
\ No newline at end of file
...@@ -27,10 +27,10 @@ public class DbGeographyHandler : SqlMapper.TypeHandler<DbGeography> ...@@ -27,10 +27,10 @@ public class DbGeographyHandler : SqlMapper.TypeHandler<DbGeography>
/// </summary> /// </summary>
/// <param name="parameter">The parameter to configure.</param> /// <param name="parameter">The parameter to configure.</param>
/// <param name="value">Parameter value.</param> /// <param name="value">Parameter value.</param>
public override void SetValue(IDbDataParameter parameter, DbGeography value) public override void SetValue(IDbDataParameter parameter, DbGeography? value)
{ {
object parsed = null; object? parsed = null;
if (value != null) if (value is not null)
{ {
parsed = SqlGeography.STGeomFromWKB(new SqlBytes(value.AsBinary()), value.CoordinateSystemId); parsed = SqlGeography.STGeomFromWKB(new SqlBytes(value.AsBinary()), value.CoordinateSystemId);
} }
...@@ -46,9 +46,9 @@ public override void SetValue(IDbDataParameter parameter, DbGeography value) ...@@ -46,9 +46,9 @@ public override void SetValue(IDbDataParameter parameter, DbGeography value)
/// </summary> /// </summary>
/// <param name="value">The value from the database.</param> /// <param name="value">The value from the database.</param>
/// <returns>The typed value.</returns> /// <returns>The typed value.</returns>
public override DbGeography Parse(object value) public override DbGeography? Parse(object? value)
{ {
if (value == null || value is DBNull) return null; if (value is null || value is DBNull) return null;
if (value is SqlGeography geo) if (value is SqlGeography geo)
{ {
return DbGeography.FromBinary(geo.STAsBinary().Value, geo.STSrid.Value); return DbGeography.FromBinary(geo.STAsBinary().Value, geo.STSrid.Value);
......
...@@ -27,10 +27,10 @@ public class DbGeometryHandler : SqlMapper.TypeHandler<DbGeometry> ...@@ -27,10 +27,10 @@ public class DbGeometryHandler : SqlMapper.TypeHandler<DbGeometry>
/// </summary> /// </summary>
/// <param name="parameter">The parameter to configure.</param> /// <param name="parameter">The parameter to configure.</param>
/// <param name="value">Parameter value.</param> /// <param name="value">Parameter value.</param>
public override void SetValue(IDbDataParameter parameter, DbGeometry value) public override void SetValue(IDbDataParameter parameter, DbGeometry? value)
{ {
object parsed = null; object? parsed = null;
if (value != null) if (value is not null)
{ {
parsed = SqlGeometry.STGeomFromWKB(new SqlBytes(value.AsBinary()), value.CoordinateSystemId); parsed = SqlGeometry.STGeomFromWKB(new SqlBytes(value.AsBinary()), value.CoordinateSystemId);
} }
...@@ -46,9 +46,9 @@ public override void SetValue(IDbDataParameter parameter, DbGeometry value) ...@@ -46,9 +46,9 @@ public override void SetValue(IDbDataParameter parameter, DbGeometry value)
/// </summary> /// </summary>
/// <param name="value">The value from the database.</param> /// <param name="value">The value from the database.</param>
/// <returns>The typed value.</returns> /// <returns>The typed value.</returns>
public override DbGeometry Parse(object value) public override DbGeometry? Parse(object? value)
{ {
if (value == null || value is DBNull) return null; if (value is null || value is DBNull) return null;
if (value is SqlGeometry geo) if (value is SqlGeometry geo)
{ {
return DbGeometry.FromBinary(geo.STAsBinary().Value, geo.STSrid.Value); return DbGeometry.FromBinary(geo.STAsBinary().Value, geo.STSrid.Value);
......
#nullable enable
Dapper.EntityFramework.DbGeographyHandler
Dapper.EntityFramework.DbGeographyHandler.DbGeographyHandler() -> void
Dapper.EntityFramework.DbGeometryHandler
Dapper.EntityFramework.DbGeometryHandler.DbGeometryHandler() -> void
Dapper.EntityFramework.Handlers
override Dapper.EntityFramework.DbGeographyHandler.Parse(object? value) -> System.Data.Entity.Spatial.DbGeography?
override Dapper.EntityFramework.DbGeographyHandler.SetValue(System.Data.IDbDataParameter! parameter, System.Data.Entity.Spatial.DbGeography? value) -> void
override Dapper.EntityFramework.DbGeometryHandler.Parse(object? value) -> System.Data.Entity.Spatial.DbGeometry?
override Dapper.EntityFramework.DbGeometryHandler.SetValue(System.Data.IDbDataParameter! parameter, System.Data.Entity.Spatial.DbGeometry? value) -> void
static Dapper.EntityFramework.Handlers.Register() -> void
static readonly Dapper.EntityFramework.DbGeographyHandler.Default -> Dapper.EntityFramework.DbGeographyHandler!
static readonly Dapper.EntityFramework.DbGeometryHandler.Default -> Dapper.EntityFramework.DbGeometryHandler!
\ No newline at end of file
#nullable enable
\ No newline at end of file
...@@ -20,7 +20,7 @@ public abstract class BulkCopy : IDisposable ...@@ -20,7 +20,7 @@ public abstract class BulkCopy : IDisposable
/// </summary> /// </summary>
public static BulkCopy? TryCreate(DbConnection connection) public static BulkCopy? TryCreate(DbConnection connection)
{ {
if (connection == null) return null; if (connection is null) return null;
var type = connection.GetType(); var type = connection.GetType();
if (!s_bcpFactory.TryGetValue(type, out var func)) if (!s_bcpFactory.TryGetValue(type, out var func))
{ {
...@@ -36,9 +36,9 @@ public abstract class BulkCopy : IDisposable ...@@ -36,9 +36,9 @@ public abstract class BulkCopy : IDisposable
public static BulkCopy Create(DbConnection connection) public static BulkCopy Create(DbConnection connection)
{ {
var bcp = TryCreate(connection); var bcp = TryCreate(connection);
if (bcp == null) if (bcp is null)
{ {
if (connection == null) throw new ArgumentNullException(nameof(connection)); if (connection is null) throw new ArgumentNullException(nameof(connection));
throw new NotSupportedException("Unable to create BulkCopy for " + connection.GetType().FullName); throw new NotSupportedException("Unable to create BulkCopy for " + connection.GetType().FullName);
} }
return bcp; return bcp;
...@@ -64,10 +64,10 @@ public static BulkCopy Create(DbConnection connection) ...@@ -64,10 +64,10 @@ public static BulkCopy Create(DbConnection connection)
{ {
var prefix = match.Groups[1].Value; var prefix = match.Groups[1].Value;
var bcpType = connectionType.Assembly.GetType($"{connectionType.Namespace}.{prefix}BulkCopy"); var bcpType = connectionType.Assembly.GetType($"{connectionType.Namespace}.{prefix}BulkCopy");
if (bcpType != null) if (bcpType is not null)
{ {
var ctor = bcpType.GetConstructor(new[] { connectionType }); var ctor = bcpType.GetConstructor(new[] { connectionType });
if (ctor == null) return null; if (ctor is null) return null;
var p = Expression.Parameter(typeof(DbConnection), "conn"); var p = Expression.Parameter(typeof(DbConnection), "conn");
var body = Expression.New(ctor, Expression.Convert(p, connectionType)); var body = Expression.New(ctor, Expression.Convert(p, connectionType));
...@@ -98,14 +98,17 @@ public static BulkCopy Create(DbConnection connection) ...@@ -98,14 +98,17 @@ public static BulkCopy Create(DbConnection connection)
/// <summary> /// <summary>
/// Write a set of data to the server /// Write a set of data to the server
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
public abstract Task WriteToServerAsync(DbDataReader source, CancellationToken cancellationToken = default); public abstract Task WriteToServerAsync(DbDataReader source, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Write a set of data to the server /// Write a set of data to the server
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
public abstract Task WriteToServerAsync(DataTable source, CancellationToken cancellationToken = default); public abstract Task WriteToServerAsync(DataTable source, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Write a set of data to the server /// Write a set of data to the server
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
public abstract Task WriteToServerAsync(DataRow[] source, CancellationToken cancellationToken = default); public abstract Task WriteToServerAsync(DataRow[] source, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Add a mapping between two columns by name /// Add a mapping between two columns by name
......
...@@ -9,8 +9,14 @@ ...@@ -9,8 +9,14 @@
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> <PackageReference Include="Microsoft.CSharp" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
......
...@@ -17,7 +17,7 @@ public static class DbConnectionExtensions ...@@ -17,7 +17,7 @@ public static class DbConnectionExtensions
public static bool TryGetClientConnectionId(this DbConnection connection, out Guid clientConnectionId) public static bool TryGetClientConnectionId(this DbConnection connection, out Guid clientConnectionId)
{ {
clientConnectionId = default; clientConnectionId = default;
return connection != null && ByTypeHelpers.Get(connection.GetType()).TryGetClientConnectionId( return connection is not null && ByTypeHelpers.Get(connection.GetType()).TryGetClientConnectionId(
connection, out clientConnectionId); connection, out clientConnectionId);
} }
...@@ -25,13 +25,13 @@ public static bool TryGetClientConnectionId(this DbConnection connection, out Gu ...@@ -25,13 +25,13 @@ public static bool TryGetClientConnectionId(this DbConnection connection, out Gu
/// Clear all pools associated with the provided connection type /// Clear all pools associated with the provided connection type
/// </summary> /// </summary>
public static bool TryClearAllPools(this DbConnection connection) public static bool TryClearAllPools(this DbConnection connection)
=> connection != null && ByTypeHelpers.Get(connection.GetType()).TryClearAllPools(); => connection is not null && ByTypeHelpers.Get(connection.GetType()).TryClearAllPools();
/// <summary> /// <summary>
/// Clear the pools associated with the provided connection /// Clear the pools associated with the provided connection
/// </summary> /// </summary>
public static bool TryClearPool(this DbConnection connection) public static bool TryClearPool(this DbConnection connection)
=> connection != null && ByTypeHelpers.Get(connection.GetType()).TryClearPool(connection); => connection is not null && ByTypeHelpers.Get(connection.GetType()).TryClearPool(connection);
private sealed class ByTypeHelpers private sealed class ByTypeHelpers
{ {
...@@ -44,7 +44,7 @@ private sealed class ByTypeHelpers ...@@ -44,7 +44,7 @@ private sealed class ByTypeHelpers
public bool TryGetClientConnectionId(DbConnection connection, out Guid clientConnectionId) public bool TryGetClientConnectionId(DbConnection connection, out Guid clientConnectionId)
{ {
if (_getClientConnectionId == null) if (_getClientConnectionId is null)
{ {
clientConnectionId = default; clientConnectionId = default;
return false; return false;
...@@ -55,14 +55,14 @@ public bool TryGetClientConnectionId(DbConnection connection, out Guid clientCon ...@@ -55,14 +55,14 @@ public bool TryGetClientConnectionId(DbConnection connection, out Guid clientCon
public bool TryClearPool(DbConnection connection) public bool TryClearPool(DbConnection connection)
{ {
if (_clearPool == null) return false; if (_clearPool is null) return false;
_clearPool(connection); _clearPool(connection);
return true; return true;
} }
public bool TryClearAllPools() public bool TryClearAllPools()
{ {
if (_clearAllPools == null) return false; if (_clearAllPools is null) return false;
_clearAllPools(); _clearAllPools();
return true; return true;
} }
...@@ -84,7 +84,7 @@ private ByTypeHelpers(Type type) ...@@ -84,7 +84,7 @@ private ByTypeHelpers(Type type)
{ {
var clearAllPools = type.GetMethod("ClearAllPools", BindingFlags.Public | BindingFlags.Static, var clearAllPools = type.GetMethod("ClearAllPools", BindingFlags.Public | BindingFlags.Static,
null, Type.EmptyTypes, null); null, Type.EmptyTypes, null);
if (clearAllPools != null) if (clearAllPools is not null)
{ {
_clearAllPools = (Action)Delegate.CreateDelegate(typeof(Action), clearAllPools); _clearAllPools = (Action)Delegate.CreateDelegate(typeof(Action), clearAllPools);
} }
...@@ -95,7 +95,7 @@ private ByTypeHelpers(Type type) ...@@ -95,7 +95,7 @@ private ByTypeHelpers(Type type)
{ {
var clearPool = type.GetMethod("ClearPool", BindingFlags.Public | BindingFlags.Static, var clearPool = type.GetMethod("ClearPool", BindingFlags.Public | BindingFlags.Static,
null, new[] { type }, null); null, new[] { type }, null);
if (clearPool != null) if (clearPool is not null)
{ {
var p = Expression.Parameter(typeof(DbConnection), "connection"); var p = Expression.Parameter(typeof(DbConnection), "connection");
var body = Expression.Call(clearPool, Expression.Convert(p, type)); var body = Expression.Call(clearPool, Expression.Convert(p, type));
...@@ -111,7 +111,7 @@ private ByTypeHelpers(Type type) ...@@ -111,7 +111,7 @@ private ByTypeHelpers(Type type)
try try
{ {
var prop = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); var prop = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
if (prop == null || !prop.CanRead) return null; if (prop is null || !prop.CanRead) return null;
if (prop.PropertyType != typeof(T)) return null; if (prop.PropertyType != typeof(T)) return null;
var p = Expression.Parameter(typeof(DbConnection), "connection"); var p = Expression.Parameter(typeof(DbConnection), "connection");
......
...@@ -15,7 +15,7 @@ public static class DbExceptionExtensions ...@@ -15,7 +15,7 @@ public static class DbExceptionExtensions
/// Indicates whether the provided exception has an integer Number property with the supplied value /// Indicates whether the provided exception has an integer Number property with the supplied value
/// </summary> /// </summary>
public static bool IsNumber(this DbException exception, int number) public static bool IsNumber(this DbException exception, int number)
=> exception != null && ByTypeHelpers.Get(exception.GetType()).IsNumber(exception, number); => exception is not null && ByTypeHelpers.Get(exception.GetType()).IsNumber(exception, number);
private sealed class ByTypeHelpers private sealed class ByTypeHelpers
...@@ -25,7 +25,7 @@ private sealed class ByTypeHelpers ...@@ -25,7 +25,7 @@ private sealed class ByTypeHelpers
private readonly Func<DbException, int>? _getNumber; private readonly Func<DbException, int>? _getNumber;
public bool IsNumber(DbException exception, int number) public bool IsNumber(DbException exception, int number)
=> _getNumber != null && _getNumber(exception) == number; => _getNumber is not null && _getNumber(exception) == number;
public static ByTypeHelpers Get(Type type) public static ByTypeHelpers Get(Type type)
{ {
...@@ -46,7 +46,7 @@ private ByTypeHelpers(Type type) ...@@ -46,7 +46,7 @@ private ByTypeHelpers(Type type)
try try
{ {
var prop = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); var prop = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
if (prop == null || !prop.CanRead) return null; if (prop is null || !prop.CanRead) return null;
if (prop.PropertyType != typeof(T)) return null; if (prop.PropertyType != typeof(T)) return null;
var p = Expression.Parameter(typeof(DbException), "exception"); var p = Expression.Parameter(typeof(DbException), "exception");
......
...@@ -9,7 +9,7 @@ namespace Dapper.ProviderTools.Internal ...@@ -9,7 +9,7 @@ namespace Dapper.ProviderTools.Internal
internal sealed class DynamicBulkCopy : BulkCopy internal sealed class DynamicBulkCopy : BulkCopy
{ {
internal static BulkCopy? Create(object? wrapped) internal static BulkCopy? Create(object? wrapped)
=> wrapped == null ? null : new DynamicBulkCopy(wrapped); => wrapped is null ? null : new DynamicBulkCopy(wrapped);
private DynamicBulkCopy(object wrapped) private DynamicBulkCopy(object wrapped)
=> _wrapped = wrapped; => _wrapped = wrapped;
......
#nullable enable
abstract Dapper.ProviderTools.BulkCopy.AddColumnMapping(int sourceColumn, int destinationColumn) -> void
abstract Dapper.ProviderTools.BulkCopy.AddColumnMapping(string! sourceColumn, string! destinationColumn) -> void
abstract Dapper.ProviderTools.BulkCopy.DestinationTableName.get -> string!
abstract Dapper.ProviderTools.BulkCopy.DestinationTableName.set -> void
abstract Dapper.ProviderTools.BulkCopy.Dispose(bool disposing) -> void
abstract Dapper.ProviderTools.BulkCopy.Wrapped.get -> object!
abstract Dapper.ProviderTools.BulkCopy.WriteToServer(System.Data.DataRow![]! source) -> void
abstract Dapper.ProviderTools.BulkCopy.WriteToServer(System.Data.DataTable! source) -> void
abstract Dapper.ProviderTools.BulkCopy.WriteToServer(System.Data.IDataReader! source) -> void
abstract Dapper.ProviderTools.BulkCopy.WriteToServerAsync(System.Data.Common.DbDataReader! source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
abstract Dapper.ProviderTools.BulkCopy.WriteToServerAsync(System.Data.DataRow![]! source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
abstract Dapper.ProviderTools.BulkCopy.WriteToServerAsync(System.Data.DataTable! source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Dapper.ProviderTools.BulkCopy
Dapper.ProviderTools.BulkCopy.BatchSize.get -> int
Dapper.ProviderTools.BulkCopy.BatchSize.set -> void
Dapper.ProviderTools.BulkCopy.BulkCopy() -> void
Dapper.ProviderTools.BulkCopy.BulkCopyTimeout.get -> int
Dapper.ProviderTools.BulkCopy.BulkCopyTimeout.set -> void
Dapper.ProviderTools.BulkCopy.Dispose() -> void
Dapper.ProviderTools.BulkCopy.EnableStreaming.get -> bool
Dapper.ProviderTools.BulkCopy.EnableStreaming.set -> void
Dapper.ProviderTools.DbConnectionExtensions
Dapper.ProviderTools.DbExceptionExtensions
static Dapper.ProviderTools.BulkCopy.Create(System.Data.Common.DbConnection! connection) -> Dapper.ProviderTools.BulkCopy!
static Dapper.ProviderTools.BulkCopy.TryCreate(System.Data.Common.DbConnection! connection) -> Dapper.ProviderTools.BulkCopy?
static Dapper.ProviderTools.DbConnectionExtensions.TryClearAllPools(this System.Data.Common.DbConnection! connection) -> bool
static Dapper.ProviderTools.DbConnectionExtensions.TryClearPool(this System.Data.Common.DbConnection! connection) -> bool
static Dapper.ProviderTools.DbConnectionExtensions.TryGetClientConnectionId(this System.Data.Common.DbConnection! connection, out System.Guid clientConnectionId) -> bool
static Dapper.ProviderTools.DbExceptionExtensions.IsNumber(this System.Data.Common.DbException! exception, int number) -> bool
\ No newline at end of file
#nullable enable
\ No newline at end of file
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<ProjectReference Include="..\Dapper\Dapper.csproj" /> <ProjectReference Include="..\Dapper\Dapper.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> <PackageReference Include="Microsoft.CSharp" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
......
...@@ -8,12 +8,18 @@ ...@@ -8,12 +8,18 @@
<TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks> <TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Dapper\Dapper.csproj" /> <ProjectReference Include="..\Dapper\Dapper.csproj" />
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> <PackageReference Include="Microsoft.CSharp" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
......
#nullable enable
Dapper.SqlBuilder
Dapper.SqlBuilder.AddClause(string! name, string! sql, object? parameters, string! joiner, string! prefix = "", string! postfix = "", bool isInclusive = false) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.AddParameters(dynamic! parameters) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.AddTemplate(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder.Template!
Dapper.SqlBuilder.GroupBy(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.Having(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.InnerJoin(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.Intersect(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.Join(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.LeftJoin(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.OrderBy(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.OrWhere(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.RightJoin(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.Select(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.Set(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
Dapper.SqlBuilder.SqlBuilder() -> void
Dapper.SqlBuilder.Template
Dapper.SqlBuilder.Template.Parameters.get -> object?
Dapper.SqlBuilder.Template.RawSql.get -> string!
Dapper.SqlBuilder.Template.Template(Dapper.SqlBuilder! builder, string! sql, dynamic? parameters) -> void
Dapper.SqlBuilder.Where(string! sql, dynamic? parameters = null) -> Dapper.SqlBuilder!
\ No newline at end of file
#nullable enable
\ No newline at end of file
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
...@@ -11,9 +12,15 @@ public class SqlBuilder ...@@ -11,9 +12,15 @@ public class SqlBuilder
private class Clause private class Clause
{ {
public string Sql { get; set; } public Clause(string sql, object? parameters, bool isInclusive)
public object Parameters { get; set; } {
public bool IsInclusive { get; set; } Sql = sql;
Parameters = parameters;
IsInclusive = isInclusive;
}
public string Sql { get; }
public object? Parameters { get; }
public bool IsInclusive { get; }
} }
private class Clauses : List<Clause> private class Clauses : List<Clause>
...@@ -52,10 +59,10 @@ public class Template ...@@ -52,10 +59,10 @@ public class Template
{ {
private readonly string _sql; private readonly string _sql;
private readonly SqlBuilder _builder; private readonly SqlBuilder _builder;
private readonly object _initParams; private readonly object? _initParams;
private int _dataSeq = -1; // Unresolved private int _dataSeq = -1; // Unresolved
public Template(SqlBuilder builder, string sql, dynamic parameters) public Template(SqlBuilder builder, string sql, dynamic? parameters)
{ {
_initParams = parameters; _initParams = parameters;
_sql = sql; _sql = sql;
...@@ -85,73 +92,73 @@ private void ResolveSql() ...@@ -85,73 +92,73 @@ private void ResolveSql()
} }
} }
private string rawSql; private string? rawSql;
private object parameters; private object? parameters;
public string RawSql public string RawSql
{ {
get { ResolveSql(); return rawSql; } get { ResolveSql(); return rawSql!; }
} }
public object Parameters public object? Parameters
{ {
get { ResolveSql(); return parameters; } get { ResolveSql(); return parameters; }
} }
} }
public Template AddTemplate(string sql, dynamic parameters = null) => public Template AddTemplate(string sql, dynamic? parameters = null) =>
new Template(this, sql, parameters); new Template(this, sql, (object?)parameters);
protected SqlBuilder AddClause(string name, string sql, object parameters, string joiner, string prefix = "", string postfix = "", bool isInclusive = false) protected SqlBuilder AddClause(string name, string sql, object? parameters, string joiner, string prefix = "", string postfix = "", bool isInclusive = false)
{ {
if (!_data.TryGetValue(name, out Clauses clauses)) if (!_data.TryGetValue(name, out var clauses))
{ {
clauses = new Clauses(joiner, prefix, postfix); clauses = new Clauses(joiner, prefix, postfix);
_data[name] = clauses; _data[name] = clauses;
} }
clauses.Add(new Clause { Sql = sql, Parameters = parameters, IsInclusive = isInclusive }); clauses.Add(new Clause(sql, parameters, isInclusive));
_seq++; _seq++;
return this; return this;
} }
public SqlBuilder Intersect(string sql, dynamic parameters = null) => public SqlBuilder Intersect(string sql, dynamic? parameters = null) =>
AddClause("intersect", sql, parameters, "\nINTERSECT\n ", "\n ", "\n", false); AddClause("intersect", sql, (object?)parameters, "\nINTERSECT\n ", "\n ", "\n", false);
public SqlBuilder InnerJoin(string sql, dynamic parameters = null) => public SqlBuilder InnerJoin(string sql, dynamic? parameters = null) =>
AddClause("innerjoin", sql, parameters, "\nINNER JOIN ", "\nINNER JOIN ", "\n", false); AddClause("innerjoin", sql, (object?)parameters, "\nINNER JOIN ", "\nINNER JOIN ", "\n", false);
public SqlBuilder LeftJoin(string sql, dynamic parameters = null) => public SqlBuilder LeftJoin(string sql, dynamic? parameters = null) =>
AddClause("leftjoin", sql, parameters, "\nLEFT JOIN ", "\nLEFT JOIN ", "\n", false); AddClause("leftjoin", sql, (object?)parameters, "\nLEFT JOIN ", "\nLEFT JOIN ", "\n", false);
public SqlBuilder RightJoin(string sql, dynamic parameters = null) => public SqlBuilder RightJoin(string sql, dynamic? parameters = null) =>
AddClause("rightjoin", sql, parameters, "\nRIGHT JOIN ", "\nRIGHT JOIN ", "\n", false); AddClause("rightjoin", sql, (object?)parameters, "\nRIGHT JOIN ", "\nRIGHT JOIN ", "\n", false);
public SqlBuilder Where(string sql, dynamic parameters = null) => public SqlBuilder Where(string sql, dynamic? parameters = null) =>
AddClause("where", sql, parameters, " AND ", "WHERE ", "\n", false); AddClause("where", sql, (object?)parameters, " AND ", "WHERE ", "\n", false);
public SqlBuilder OrWhere(string sql, dynamic parameters = null) => public SqlBuilder OrWhere(string sql, dynamic? parameters = null) =>
AddClause("where", sql, parameters, " OR ", "WHERE ", "\n", true); AddClause("where", sql, (object?)parameters, " OR ", "WHERE ", "\n", true);
public SqlBuilder OrderBy(string sql, dynamic parameters = null) => public SqlBuilder OrderBy(string sql, dynamic? parameters = null) =>
AddClause("orderby", sql, parameters, " , ", "ORDER BY ", "\n", false); AddClause("orderby", sql, (object?)parameters, " , ", "ORDER BY ", "\n", false);
public SqlBuilder Select(string sql, dynamic parameters = null) => public SqlBuilder Select(string sql, dynamic? parameters = null) =>
AddClause("select", sql, parameters, " , ", "", "\n", false); AddClause("select", sql, (object?)parameters, " , ", "", "\n", false);
public SqlBuilder AddParameters(dynamic parameters) => public SqlBuilder AddParameters(dynamic parameters) =>
AddClause("--parameters", "", parameters, "", "", "", false); AddClause("--parameters", "", (object?)parameters, "", "", "", false);
public SqlBuilder Join(string sql, dynamic parameters = null) => public SqlBuilder Join(string sql, dynamic? parameters = null) =>
AddClause("join", sql, parameters, "\nJOIN ", "\nJOIN ", "\n", false); AddClause("join", sql, (object?)parameters, "\nJOIN ", "\nJOIN ", "\n", false);
public SqlBuilder GroupBy(string sql, dynamic parameters = null) => public SqlBuilder GroupBy(string sql, dynamic? parameters = null) =>
AddClause("groupby", sql, parameters, " , ", "\nGROUP BY ", "\n", false); AddClause("groupby", sql, (object?)parameters, " , ", "\nGROUP BY ", "\n", false);
public SqlBuilder Having(string sql, dynamic parameters = null) => public SqlBuilder Having(string sql, dynamic? parameters = null) =>
AddClause("having", sql, parameters, "\nAND ", "HAVING ", "\n", false); AddClause("having", sql, (object?)parameters, "\nAND ", "HAVING ", "\n", false);
public SqlBuilder Set(string sql, dynamic parameters = null) => public SqlBuilder Set(string sql, dynamic? parameters = null) =>
AddClause("set", sql, parameters, " , ", "SET ", "\n", false); AddClause("set", sql, (object?)parameters, " , ", "SET ", "\n", false);
} }
} }
...@@ -8,17 +8,15 @@ ...@@ -8,17 +8,15 @@
<TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks> <TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Properties\" /> <Folder Include="Properties\" />
<Compile Include="..\Dapper\**\*.cs" Exclude="..\Dapper\obj\**\*.cs" /> <Compile Include="..\Dapper\**\*.cs" Exclude="..\Dapper\obj\**\*.cs" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net5.0'">
<DefineConstants>$(DefineConstants);PLAT_NO_REMOTING;PLAT_SKIP_LOCALS_INIT</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" /> <PackageReference Include="System.Reflection.Emit.Lightweight" />
</ItemGroup> </ItemGroup>
</Project> </Project>
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.28917.182 VisualStudioVersion = 17.7.33906.173
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A34907DF-958A-4E4C-8491-84CF303FD13E}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A34907DF-958A-4E4C-8491-84CF303FD13E}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
appveyor.yml = appveyor.yml appveyor.yml = appveyor.yml
Build.csproj = Build.csproj
build.ps1 = build.ps1 build.ps1 = build.ps1
Dapper.png = Dapper.png Dapper.png = Dapper.png
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
global.json = global.json global.json = global.json
docs\index.md = docs\index.md docs\index.md = docs\index.md
License.txt = License.txt License.txt = License.txt
...@@ -31,9 +33,6 @@ EndProject ...@@ -31,9 +33,6 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Rainbow", "Dapper.Rainbow\Dapper.Rainbow.csproj", "{8A74F0B6-188F-45D2-8A4B-51E4F211805A}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Rainbow", "Dapper.Rainbow\Dapper.Rainbow.csproj", "{8A74F0B6-188F-45D2-8A4B-51E4F211805A}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4E956F6B-6BD8-46F5-BC85-49292FF8F9AB}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4E956F6B-6BD8-46F5-BC85-49292FF8F9AB}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{568BD46C-1C65-4D44-870C-12CD72563262}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{568BD46C-1C65-4D44-870C-12CD72563262}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
...@@ -51,6 +50,7 @@ EndProject ...@@ -51,6 +50,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{9D960D4D-80A2-4DAC-B386-8F4235EC73E6}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{9D960D4D-80A2-4DAC-B386-8F4235EC73E6}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
docs\index.md = docs\index.md docs\index.md = docs\index.md
docs\readme.md = docs\readme.md
EndProjectSection EndProjectSection
EndProject EndProject
Global Global
......
...@@ -11,7 +11,7 @@ namespace Dapper ...@@ -11,7 +11,7 @@ namespace Dapper
/// </summary> /// </summary>
public readonly struct CommandDefinition public readonly struct CommandDefinition
{ {
internal static CommandDefinition ForCallback(object parameters) internal static CommandDefinition ForCallback(object? parameters)
{ {
if (parameters is DynamicParameters) if (parameters is DynamicParameters)
{ {
...@@ -36,12 +36,12 @@ internal void OnCompleted() ...@@ -36,12 +36,12 @@ internal void OnCompleted()
/// <summary> /// <summary>
/// The parameters associated with the command /// The parameters associated with the command
/// </summary> /// </summary>
public object Parameters { get; } public object? Parameters { get; }
/// <summary> /// <summary>
/// The active transaction for the command /// The active transaction for the command
/// </summary> /// </summary>
public IDbTransaction Transaction { get; } public IDbTransaction? Transaction { get; }
/// <summary> /// <summary>
/// The effective timeout for the command /// The effective timeout for the command
...@@ -83,7 +83,7 @@ internal void OnCompleted() ...@@ -83,7 +83,7 @@ internal void OnCompleted()
/// <param name="commandType">The <see cref="CommandType"/> for this command.</param> /// <param name="commandType">The <see cref="CommandType"/> for this command.</param>
/// <param name="flags">The behavior flags for this command.</param> /// <param name="flags">The behavior flags for this command.</param>
/// <param name="cancellationToken">The cancellation token for this command.</param> /// <param name="cancellationToken">The cancellation token for this command.</param>
public CommandDefinition(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null, public CommandDefinition(string commandText, object? parameters = null, IDbTransaction? transaction = null, int? commandTimeout = null,
CommandType? commandType = null, CommandFlags flags = CommandFlags.Buffered CommandType? commandType = null, CommandFlags flags = CommandFlags.Buffered
, CancellationToken cancellationToken = default , CancellationToken cancellationToken = default
) )
...@@ -97,9 +97,10 @@ internal void OnCompleted() ...@@ -97,9 +97,10 @@ internal void OnCompleted()
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
} }
private CommandDefinition(object parameters) : this() private CommandDefinition(object? parameters) : this()
{ {
Parameters = parameters; Parameters = parameters;
CommandText = "";
} }
/// <summary> /// <summary>
...@@ -107,12 +108,12 @@ private CommandDefinition(object parameters) : this() ...@@ -107,12 +108,12 @@ private CommandDefinition(object parameters) : this()
/// </summary> /// </summary>
public CancellationToken CancellationToken { get; } public CancellationToken CancellationToken { get; }
internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> paramReader) internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object?>? paramReader)
{ {
var cmd = cnn.CreateCommand(); var cmd = cnn.CreateCommand();
var init = GetInit(cmd.GetType()); var init = GetInit(cmd.GetType());
init?.Invoke(cmd); init?.Invoke(cmd);
if (Transaction != null) if (Transaction is not null)
cmd.Transaction = Transaction; cmd.Transaction = Transaction;
cmd.CommandText = CommandText; cmd.CommandText = CommandText;
if (CommandTimeout.HasValue) if (CommandTimeout.HasValue)
...@@ -129,26 +130,30 @@ internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> p ...@@ -129,26 +130,30 @@ internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> p
return cmd; return cmd;
} }
private static SqlMapper.Link<Type, Action<IDbCommand>> commandInitCache; private static SqlMapper.Link<Type, Action<IDbCommand>>? commandInitCache;
private static Action<IDbCommand> GetInit(Type commandType) internal static void ResetCommandInitCache()
=> SqlMapper.Link<Type, Action<IDbCommand>>.Clear(ref commandInitCache);
private static Action<IDbCommand>? GetInit(Type commandType)
{ {
if (commandType == null) if (commandType is null)
return null; // GIGO return null; // GIGO
if (SqlMapper.Link<Type, Action<IDbCommand>>.TryGet(commandInitCache, commandType, out Action<IDbCommand> action)) if (SqlMapper.Link<Type, Action<IDbCommand>>.TryGet(commandInitCache, commandType, out Action<IDbCommand>? action))
{ {
return action; return action;
} }
var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool)); var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool));
var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int)); var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize", typeof(int));
var fetchSize = GetBasicPropertySetter(commandType, "FetchSize", typeof(long));
action = null; action = null;
if (bindByName != null || initialLongFetchSize != null) if (bindByName is not null || initialLongFetchSize is not null || fetchSize is not null)
{ {
var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) }); var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) });
var il = method.GetILGenerator(); var il = method.GetILGenerator();
if (bindByName != null) if (bindByName is not null)
{ {
// .BindByName = true // .BindByName = true
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
...@@ -156,7 +161,7 @@ private static Action<IDbCommand> GetInit(Type commandType) ...@@ -156,7 +161,7 @@ private static Action<IDbCommand> GetInit(Type commandType)
il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Ldc_I4_1);
il.EmitCall(OpCodes.Callvirt, bindByName, null); il.EmitCall(OpCodes.Callvirt, bindByName, null);
} }
if (initialLongFetchSize != null) if (initialLongFetchSize is not null)
{ {
// .InitialLONGFetchSize = -1 // .InitialLONGFetchSize = -1
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
...@@ -164,15 +169,27 @@ private static Action<IDbCommand> GetInit(Type commandType) ...@@ -164,15 +169,27 @@ private static Action<IDbCommand> GetInit(Type commandType)
il.Emit(OpCodes.Ldc_I4_M1); il.Emit(OpCodes.Ldc_I4_M1);
il.EmitCall(OpCodes.Callvirt, initialLongFetchSize, null); il.EmitCall(OpCodes.Callvirt, initialLongFetchSize, null);
} }
if (fetchSize is not null)
{
var snapshot = SqlMapper.Settings.FetchSize;
if (snapshot >= 0)
{
// .FetchSize = {withValue}
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, commandType);
il.Emit(OpCodes.Ldc_I8, snapshot); // bake it as a constant
il.EmitCall(OpCodes.Callvirt, fetchSize, null);
}
}
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
action = (Action<IDbCommand>)method.CreateDelegate(typeof(Action<IDbCommand>)); action = (Action<IDbCommand>)method.CreateDelegate(typeof(Action<IDbCommand>));
} }
// cache it // cache it
SqlMapper.Link<Type, Action<IDbCommand>>.TryAdd(ref commandInitCache, commandType, ref action); SqlMapper.Link<Type, Action<IDbCommand>>.TryAdd(ref commandInitCache, commandType, ref action!);
return action; return action;
} }
private static MethodInfo GetBasicPropertySetter(Type declaringType, string name, Type expectedType) private static MethodInfo? GetBasicPropertySetter(Type declaringType, string name, Type expectedType)
{ {
var prop = declaringType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); var prop = declaringType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
if (prop?.CanWrite == true && prop.PropertyType == expectedType && prop.GetIndexParameters().Length == 0) if (prop?.CanWrite == true && prop.PropertyType == expectedType && prop.GetIndexParameters().Length == 0)
......
...@@ -28,14 +28,14 @@ public CustomPropertyTypeMap(Type type, Func<Type, string, PropertyInfo> propert ...@@ -28,14 +28,14 @@ public CustomPropertyTypeMap(Type type, Func<Type, string, PropertyInfo> propert
/// <param name="names">DataReader column names</param> /// <param name="names">DataReader column names</param>
/// <param name="types">DataReader column types</param> /// <param name="types">DataReader column types</param>
/// <returns>Default constructor</returns> /// <returns>Default constructor</returns>
public ConstructorInfo FindConstructor(string[] names, Type[] types) => public ConstructorInfo? FindConstructor(string[] names, Type[] types) =>
_type.GetConstructor(Array.Empty<Type>()); _type.GetConstructor(Array.Empty<Type>())!;
/// <summary> /// <summary>
/// Always returns null /// Always returns null
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public ConstructorInfo FindExplicitConstructor() => null; public ConstructorInfo? FindExplicitConstructor() => null;
/// <summary> /// <summary>
/// Not implemented as far as default constructor used for all cases /// Not implemented as far as default constructor used for all cases
...@@ -53,10 +53,10 @@ public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, ...@@ -53,10 +53,10 @@ public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor,
/// </summary> /// </summary>
/// <param name="columnName">DataReader column name</param> /// <param name="columnName">DataReader column name</param>
/// <returns>Property member map</returns> /// <returns>Property member map</returns>
public SqlMapper.IMemberMap GetMember(string columnName) public SqlMapper.IMemberMap? GetMember(string columnName)
{ {
var prop = _propertySelector(_type, columnName); var prop = _propertySelector(_type, columnName);
return prop != null ? new SimpleMemberMap(columnName, prop) : null; return prop is not null ? new SimpleMemberMap(columnName, prop) : null;
} }
} }
} }
...@@ -6,16 +6,25 @@ ...@@ -6,16 +6,25 @@
<Description>A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, SqlCE, Firebird etc..</Description> <Description>A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, SqlCE, Firebird etc..</Description>
<Authors>Sam Saffron;Marc Gravell;Nick Craver</Authors> <Authors>Sam Saffron;Marc Gravell;Nick Craver</Authors>
<TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks> <TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<!-- common base API goes here -->
<AdditionalFiles Include="PublicAPI.*.txt" />
<!-- TFM-specific additions go here; in reality this just means "current" (to avoid DisposeAsync etc in netfx/netstandard) -->
<AdditionalFiles Include="PublicAPI/$(TargetFramework)/PublicAPI.*.txt" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Properties\" /> <Folder Include="Properties\" />
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net5.0'">
<DefineConstants>$(DefineConstants);PLAT_NO_REMOTING;PLAT_SKIP_LOCALS_INIT</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" /> <PackageReference Include="System.Reflection.Emit.Lightweight" />
</ItemGroup> </ItemGroup>
</Project> </Project>
...@@ -43,7 +43,7 @@ public DbString() ...@@ -43,7 +43,7 @@ public DbString()
/// <summary> /// <summary>
/// The value of the string /// The value of the string
/// </summary> /// </summary>
public string Value { get; set; } public string? Value { get; set; }
/// <summary> /// <summary>
/// Gets a string representation of this DbString. /// Gets a string representation of this DbString.
...@@ -76,7 +76,7 @@ public void AddParameter(IDbCommand command, string name) ...@@ -76,7 +76,7 @@ public void AddParameter(IDbCommand command, string name)
#pragma warning disable 0618 #pragma warning disable 0618
param.Value = SqlMapper.SanitizeParameterValue(Value); param.Value = SqlMapper.SanitizeParameterValue(Value);
#pragma warning restore 0618 #pragma warning restore 0618
if (Length == -1 && Value != null && Value.Length <= DefaultLength) if (Length == -1 && Value is not null && Value.Length <= DefaultLength)
{ {
param.Size = DefaultLength; param.Size = DefaultLength;
} }
......
...@@ -19,7 +19,7 @@ public sealed class DefaultTypeMap : SqlMapper.ITypeMap ...@@ -19,7 +19,7 @@ public sealed class DefaultTypeMap : SqlMapper.ITypeMap
/// <param name="type">Entity type</param> /// <param name="type">Entity type</param>
public DefaultTypeMap(Type type) public DefaultTypeMap(Type type)
{ {
if (type == null) if (type is null)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(type));
_fields = GetSettableFields(type); _fields = GetSettableFields(type);
...@@ -27,24 +27,30 @@ public DefaultTypeMap(Type type) ...@@ -27,24 +27,30 @@ public DefaultTypeMap(Type type)
_type = type; _type = type;
} }
internal static MethodInfo GetPropertySetter(PropertyInfo propertyInfo, Type type) internal static MethodInfo GetPropertySetterOrThrow(PropertyInfo propertyInfo, Type type)
{
return GetPropertySetter(propertyInfo, type) ?? Throw(propertyInfo);
static MethodInfo Throw(PropertyInfo propertyInfo) => throw new InvalidOperationException("Property setting not found for: " + propertyInfo?.Name);
}
internal static MethodInfo? GetPropertySetter(PropertyInfo propertyInfo, Type type)
{ {
if (propertyInfo.DeclaringType == type) return propertyInfo.GetSetMethod(true); if (propertyInfo.DeclaringType == type) return propertyInfo.GetSetMethod(true);
return propertyInfo.DeclaringType.GetProperty( return propertyInfo.DeclaringType!.GetProperty(
propertyInfo.Name, propertyInfo.Name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
Type.DefaultBinder, Type.DefaultBinder,
propertyInfo.PropertyType, propertyInfo.PropertyType,
propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(), propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(),
null).GetSetMethod(true); null)!.GetSetMethod(true);
} }
internal static List<PropertyInfo> GetSettableProps(Type t) internal static List<PropertyInfo> GetSettableProps(Type t)
{ {
return t return t
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => GetPropertySetter(p, t) != null) .Where(p => GetPropertySetter(p, t) is not null)
.ToList(); .ToList();
} }
...@@ -59,7 +65,7 @@ internal static List<FieldInfo> GetSettableFields(Type t) ...@@ -59,7 +65,7 @@ internal static List<FieldInfo> GetSettableFields(Type t)
/// <param name="names">DataReader column names</param> /// <param name="names">DataReader column names</param>
/// <param name="types">DataReader column types</param> /// <param name="types">DataReader column types</param>
/// <returns>Matching constructor or default one</returns> /// <returns>Matching constructor or default one</returns>
public ConstructorInfo FindConstructor(string[] names, Type[] types) public ConstructorInfo? FindConstructor(string[] names, Type[] types)
{ {
var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length)) foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length))
...@@ -74,8 +80,16 @@ public ConstructorInfo FindConstructor(string[] names, Type[] types) ...@@ -74,8 +80,16 @@ public ConstructorInfo FindConstructor(string[] names, Type[] types)
int i = 0; int i = 0;
for (; i < ctorParameters.Length; i++) for (; i < ctorParameters.Length; i++)
{ {
if (!string.Equals(ctorParameters[i].Name, names[i], StringComparison.OrdinalIgnoreCase)) if (EqualsCI(ctorParameters[i].Name, names[i]))
{ } // exact match
else if (MatchNamesWithUnderscores && EqualsCIU(ctorParameters[i].Name, names[i]))
{ } // match after applying underscores
else
{
// not a name match
break; break;
}
if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == SqlMapper.LinqBinary) if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == SqlMapper.LinqBinary)
continue; continue;
var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType; var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType;
...@@ -98,7 +112,7 @@ public ConstructorInfo FindConstructor(string[] names, Type[] types) ...@@ -98,7 +112,7 @@ public ConstructorInfo FindConstructor(string[] names, Type[] types)
/// <summary> /// <summary>
/// Returns the constructor, if any, that has the ExplicitConstructorAttribute on it. /// Returns the constructor, if any, that has the ExplicitConstructorAttribute on it.
/// </summary> /// </summary>
public ConstructorInfo FindExplicitConstructor() public ConstructorInfo? FindExplicitConstructor()
{ {
var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var withAttr = constructors.Where(c => c.GetCustomAttributes(typeof(ExplicitConstructorAttribute), true).Length > 0).ToList(); var withAttr = constructors.Where(c => c.GetCustomAttributes(typeof(ExplicitConstructorAttribute), true).Length > 0).ToList();
...@@ -119,9 +133,10 @@ public ConstructorInfo FindExplicitConstructor() ...@@ -119,9 +133,10 @@ public ConstructorInfo FindExplicitConstructor()
/// <returns>Mapping implementation</returns> /// <returns>Mapping implementation</returns>
public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName) public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
{ {
var parameters = constructor.GetParameters(); var param = MatchFirstOrDefault(constructor.GetParameters(), columnName, static p => p.Name) ?? Throw(columnName);
return new SimpleMemberMap(columnName, param);
return new SimpleMemberMap(columnName, parameters.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase))); static ParameterInfo Throw(string name) => throw new ArgumentException("Constructor parameter not found for " + name);
} }
/// <summary> /// <summary>
...@@ -129,18 +144,11 @@ public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, ...@@ -129,18 +144,11 @@ public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor,
/// </summary> /// </summary>
/// <param name="columnName">DataReader column name</param> /// <param name="columnName">DataReader column name</param>
/// <returns>Mapping implementation</returns> /// <returns>Mapping implementation</returns>
public SqlMapper.IMemberMap GetMember(string columnName) public SqlMapper.IMemberMap? GetMember(string columnName)
{ {
var property = Properties.Find(p => string.Equals(p.Name, columnName, StringComparison.Ordinal)) var property = MatchFirstOrDefault(Properties, columnName, static p => p.Name);
?? Properties.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
if (property == null && MatchNamesWithUnderscores) if (property is not null)
{
property = Properties.Find(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal))
?? Properties.Find(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase));
}
if (property != null)
return new SimpleMemberMap(columnName, property); return new SimpleMemberMap(columnName, property);
// roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField; // roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
...@@ -153,7 +161,7 @@ public SqlMapper.IMemberMap GetMember(string columnName) ...@@ -153,7 +161,7 @@ public SqlMapper.IMemberMap GetMember(string columnName)
?? _fields.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase)) ?? _fields.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase))
?? _fields.Find(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase)); ?? _fields.Find(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase));
if (field == null && MatchNamesWithUnderscores) if (field is null && MatchNamesWithUnderscores)
{ {
var effectiveColumnName = columnName.Replace("_", ""); var effectiveColumnName = columnName.Replace("_", "");
backingFieldName = "<" + effectiveColumnName + ">k__BackingField"; backingFieldName = "<" + effectiveColumnName + ">k__BackingField";
...@@ -164,7 +172,7 @@ public SqlMapper.IMemberMap GetMember(string columnName) ...@@ -164,7 +172,7 @@ public SqlMapper.IMemberMap GetMember(string columnName)
?? _fields.Find(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase)); ?? _fields.Find(p => string.Equals(p.Name, backingFieldName, StringComparison.OrdinalIgnoreCase));
} }
if (field != null) if (field is not null)
return new SimpleMemberMap(columnName, field); return new SimpleMemberMap(columnName, field);
return null; return null;
...@@ -174,6 +182,54 @@ public SqlMapper.IMemberMap GetMember(string columnName) ...@@ -174,6 +182,54 @@ public SqlMapper.IMemberMap GetMember(string columnName)
/// </summary> /// </summary>
public static bool MatchNamesWithUnderscores { get; set; } public static bool MatchNamesWithUnderscores { get; set; }
static T? MatchFirstOrDefault<T>(IList<T>? members, string? name, Func<T, string?> selector) where T : class
{
if (members is { Count: > 0 })
{
// try exact first
foreach (var member in members)
{
if (string.Equals(name, selector(member), StringComparison.Ordinal))
{
return member;
}
}
// then exact ignoring case
foreach (var member in members)
{
if (string.Equals(name, selector(member), StringComparison.OrdinalIgnoreCase))
{
return member;
}
}
if (MatchNamesWithUnderscores)
{
// same again, minus underscore delta
name = name?.Replace("_", "");
foreach (var member in members)
{
if (string.Equals(name, selector(member)?.Replace("_", ""), StringComparison.Ordinal))
{
return member;
}
}
foreach (var member in members)
{
if (string.Equals(name, selector(member)?.Replace("_", ""), StringComparison.OrdinalIgnoreCase))
{
return member;
}
}
}
}
return null;
}
internal static bool EqualsCI(string? x, string? y)
=> string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
internal static bool EqualsCIU(string? x, string? y)
=> string.Equals(x?.Replace("_", ""), y?.Replace("_", ""), StringComparison.OrdinalIgnoreCase);
/// <summary> /// <summary>
/// The settable properties for this typemap /// The settable properties for this typemap
/// </summary> /// </summary>
......
...@@ -7,14 +7,14 @@ public partial class DynamicParameters ...@@ -7,14 +7,14 @@ public partial class DynamicParameters
{ {
private sealed class ParamInfo private sealed class ParamInfo
{ {
public string Name { get; set; } public string Name { get; set; } = null!;
public object Value { get; set; } public object? Value { get; set; }
public ParameterDirection ParameterDirection { get; set; } public ParameterDirection ParameterDirection { get; set; }
public DbType? DbType { get; set; } public DbType? DbType { get; set; }
public int? Size { get; set; } public int? Size { get; set; }
public IDbDataParameter AttachedParam { get; set; } public IDbDataParameter AttachedParam { get; set; } = null!;
internal Action<object, DynamicParameters> OutputCallback { get; set; } internal Action<object, DynamicParameters>? OutputCallback { get; set; }
internal object OutputTarget { get; set; } internal object OutputTarget { get; set; } = null!;
internal bool CameFromTemplate { get; set; } internal bool CameFromTemplate { get; set; }
public byte? Precision { get; set; } public byte? Precision { get; set; }
......
...@@ -16,10 +16,10 @@ public partial class DynamicParameters : SqlMapper.IDynamicParameters, SqlMapper ...@@ -16,10 +16,10 @@ public partial class DynamicParameters : SqlMapper.IDynamicParameters, SqlMapper
internal const DbType EnumerableMultiParameter = (DbType)(-1); internal const DbType EnumerableMultiParameter = (DbType)(-1);
private static readonly Dictionary<SqlMapper.Identity, Action<IDbCommand, object>> paramReaderCache = new Dictionary<SqlMapper.Identity, Action<IDbCommand, object>>(); private static readonly Dictionary<SqlMapper.Identity, Action<IDbCommand, object>> paramReaderCache = new Dictionary<SqlMapper.Identity, Action<IDbCommand, object>>();
private readonly Dictionary<string, ParamInfo> parameters = new Dictionary<string, ParamInfo>(); private readonly Dictionary<string, ParamInfo> parameters = new Dictionary<string, ParamInfo>();
private List<object> templates; private List<object>? templates;
object SqlMapper.IParameterLookup.this[string name] => object? SqlMapper.IParameterLookup.this[string name] =>
parameters.TryGetValue(name, out ParamInfo param) ? param.Value : null; parameters.TryGetValue(name, out ParamInfo? param) ? param.Value : null;
/// <summary> /// <summary>
/// construct a dynamic parameter bag /// construct a dynamic parameter bag
...@@ -33,7 +33,7 @@ public DynamicParameters() ...@@ -33,7 +33,7 @@ public DynamicParameters()
/// construct a dynamic parameter bag /// construct a dynamic parameter bag
/// </summary> /// </summary>
/// <param name="template">can be an anonymous type or a DynamicParameters bag</param> /// <param name="template">can be an anonymous type or a DynamicParameters bag</param>
public DynamicParameters(object template) public DynamicParameters(object? template)
{ {
RemoveUnused = true; RemoveUnused = true;
AddDynamicParams(template); AddDynamicParams(template);
...@@ -44,14 +44,14 @@ public DynamicParameters(object template) ...@@ -44,14 +44,14 @@ public DynamicParameters(object template)
/// EG: AddDynamicParams(new {A = 1, B = 2}) // will add property A and B to the dynamic /// EG: AddDynamicParams(new {A = 1, B = 2}) // will add property A and B to the dynamic
/// </summary> /// </summary>
/// <param name="param"></param> /// <param name="param"></param>
public void AddDynamicParams(object param) public void AddDynamicParams(object? param)
{ {
var obj = param; var obj = param;
if (obj != null) if (obj is not null)
{ {
if (obj is DynamicParameters subDynamic) if (obj is DynamicParameters subDynamic)
{ {
if (subDynamic.parameters != null) if (subDynamic.parameters is not null)
{ {
foreach (var kvp in subDynamic.parameters) foreach (var kvp in subDynamic.parameters)
{ {
...@@ -59,7 +59,7 @@ public void AddDynamicParams(object param) ...@@ -59,7 +59,7 @@ public void AddDynamicParams(object param)
} }
} }
if (subDynamic.templates != null) if (subDynamic.templates is not null)
{ {
templates ??= new List<object>(); templates ??= new List<object>();
foreach (var t in subDynamic.templates) foreach (var t in subDynamic.templates)
...@@ -94,7 +94,7 @@ public void AddDynamicParams(object param) ...@@ -94,7 +94,7 @@ public void AddDynamicParams(object param)
/// <param name="dbType">The type of the parameter.</param> /// <param name="dbType">The type of the parameter.</param>
/// <param name="direction">The in or out direction of the parameter.</param> /// <param name="direction">The in or out direction of the parameter.</param>
/// <param name="size">The size of the parameter.</param> /// <param name="size">The size of the parameter.</param>
public void Add(string name, object value, DbType? dbType, ParameterDirection? direction, int? size) public void Add(string name, object? value, DbType? dbType, ParameterDirection? direction, int? size)
{ {
parameters[Clean(name)] = new ParamInfo parameters[Clean(name)] = new ParamInfo
{ {
...@@ -116,7 +116,7 @@ public void Add(string name, object value, DbType? dbType, ParameterDirection? d ...@@ -116,7 +116,7 @@ public void Add(string name, object value, DbType? dbType, ParameterDirection? d
/// <param name="size">The size of the parameter.</param> /// <param name="size">The size of the parameter.</param>
/// <param name="precision">The precision of the parameter.</param> /// <param name="precision">The precision of the parameter.</param>
/// <param name="scale">The scale of the parameter.</param> /// <param name="scale">The scale of the parameter.</param>
public void Add(string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null, byte? precision = null, byte? scale = null) public void Add(string name, object? value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null, byte? precision = null, byte? scale = null)
{ {
parameters[Clean(name)] = new ParamInfo parameters[Clean(name)] = new ParamInfo
{ {
...@@ -170,7 +170,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -170,7 +170,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{ {
var literals = SqlMapper.GetLiteralTokens(identity.sql); var literals = SqlMapper.GetLiteralTokens(identity.sql);
if (templates != null) if (templates is not null)
{ {
foreach (var template in templates) foreach (var template in templates)
{ {
...@@ -179,7 +179,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -179,7 +179,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
lock (paramReaderCache) lock (paramReaderCache)
{ {
if (!paramReaderCache.TryGetValue(newIdent, out appender)) if (!paramReaderCache.TryGetValue(newIdent, out appender!))
{ {
appender = SqlMapper.CreateParamInfoGenerator(newIdent, true, RemoveUnused, literals); appender = SqlMapper.CreateParamInfoGenerator(newIdent, true, RemoveUnused, literals);
paramReaderCache[newIdent] = appender; paramReaderCache[newIdent] = appender;
...@@ -213,7 +213,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -213,7 +213,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
// Now that the parameters are added to the command, let's place our output callbacks // Now that the parameters are added to the command, let's place our output callbacks
var tmp = outputCallbacks; var tmp = outputCallbacks;
if (tmp != null) if (tmp is not null)
{ {
foreach (var generator in tmp) foreach (var generator in tmp)
{ {
...@@ -231,8 +231,8 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -231,8 +231,8 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
string name = Clean(param.Name); string name = Clean(param.Name);
var isCustomQueryParameter = val is SqlMapper.ICustomQueryParameter; var isCustomQueryParameter = val is SqlMapper.ICustomQueryParameter;
SqlMapper.ITypeHandler handler = null; SqlMapper.ITypeHandler? handler = null;
if (dbType == null && val != null && !isCustomQueryParameter) if (dbType is null && val is not null && !isCustomQueryParameter)
{ {
#pragma warning disable 618 #pragma warning disable 618
dbType = SqlMapper.LookupDbType(val.GetType(), name, true, out handler); dbType = SqlMapper.LookupDbType(val.GetType(), name, true, out handler);
...@@ -240,7 +240,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -240,7 +240,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
} }
if (isCustomQueryParameter) if (isCustomQueryParameter)
{ {
((SqlMapper.ICustomQueryParameter)val).AddParameter(command, name); ((SqlMapper.ICustomQueryParameter)val!).AddParameter(command, name);
} }
else if (dbType == EnumerableMultiParameter) else if (dbType == EnumerableMultiParameter)
{ {
...@@ -263,7 +263,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -263,7 +263,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
} }
p.Direction = param.ParameterDirection; p.Direction = param.ParameterDirection;
if (handler == null) if (handler is null)
{ {
#pragma warning disable 0618 #pragma warning disable 0618
p.Value = SqlMapper.SanitizeParameterValue(val); p.Value = SqlMapper.SanitizeParameterValue(val);
...@@ -277,16 +277,16 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -277,16 +277,16 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{ {
p.Size = DbString.DefaultLength; p.Size = DbString.DefaultLength;
} }
if (param.Size != null) p.Size = param.Size.Value; if (param.Size is not null) p.Size = param.Size.Value;
if (param.Precision != null) p.Precision = param.Precision.Value; if (param.Precision is not null) p.Precision = param.Precision.Value;
if (param.Scale != null) p.Scale = param.Scale.Value; if (param.Scale is not null) p.Scale = param.Scale.Value;
} }
else else
{ {
if (ShouldSetDbType(dbType)) p.DbType = dbType.GetValueOrDefault(); if (ShouldSetDbType(dbType)) p.DbType = dbType.GetValueOrDefault();
if (param.Size != null) p.Size = param.Size.Value; if (param.Size is not null) p.Size = param.Size.Value;
if (param.Precision != null) p.Precision = param.Precision.Value; if (param.Precision is not null) p.Precision = param.Precision.Value;
if (param.Scale != null) p.Scale = param.Scale.Value; if (param.Scale is not null) p.Scale = param.Scale.Value;
handler.SetValue(p, val ?? DBNull.Value); handler.SetValue(p, val ?? DBNull.Value);
} }
...@@ -317,16 +317,16 @@ public T Get<T>(string name) ...@@ -317,16 +317,16 @@ public T Get<T>(string name)
{ {
var paramInfo = parameters[Clean(name)]; var paramInfo = parameters[Clean(name)];
var attachedParam = paramInfo.AttachedParam; var attachedParam = paramInfo.AttachedParam;
object val = attachedParam == null ? paramInfo.Value : attachedParam.Value; object? val = attachedParam is null ? paramInfo.Value : attachedParam.Value;
if (val == DBNull.Value) if (val == DBNull.Value)
{ {
if (default(T) != null) if (default(T) is not null)
{ {
throw new ApplicationException("Attempting to cast a DBNull to a non nullable type! Note that out/return parameters will not have updated values until the data stream completes (after the 'foreach' for Query(..., buffered: false), or after the GridReader has been disposed for QueryMultiple)"); throw new ApplicationException("Attempting to cast a DBNull to a non nullable type! Note that out/return parameters will not have updated values until the data stream completes (after the 'foreach' for Query(..., buffered: false), or after the GridReader has been disposed for QueryMultiple)");
} }
return default; return default!;
} }
return (T)val; return (T)val!;
} }
/// <summary> /// <summary>
...@@ -339,7 +339,7 @@ public T Get<T>(string name) ...@@ -339,7 +339,7 @@ public T Get<T>(string name)
/// <param name="dbType"></param> /// <param name="dbType"></param>
/// <param name="size">The size to set on the parameter. Defaults to 0, or DbString.DefaultLength in case of strings.</param> /// <param name="size">The size to set on the parameter. Defaults to 0, or DbString.DefaultLength in case of strings.</param>
/// <returns>The DynamicParameters instance</returns> /// <returns>The DynamicParameters instance</returns>
public DynamicParameters Output<T>(T target, Expression<Func<T, object>> expression, DbType? dbType = null, int? size = null) public DynamicParameters Output<T>(T target, Expression<Func<T, object?>> expression, DbType? dbType = null, int? size = null)
{ {
static void ThrowInvalidChain() static void ThrowInvalidChain()
=> throw new InvalidOperationException($"Expression must be a property/field chain off of a(n) {typeof(T).Name} instance"); => throw new InvalidOperationException($"Expression must be a property/field chain off of a(n) {typeof(T).Name} instance");
...@@ -349,7 +349,7 @@ static void ThrowInvalidChain() ...@@ -349,7 +349,7 @@ static void ThrowInvalidChain()
var lastMemberAccess = expression.Body as MemberExpression; var lastMemberAccess = expression.Body as MemberExpression;
#pragma warning restore IDE0019 // Use pattern matching #pragma warning restore IDE0019 // Use pattern matching
if (lastMemberAccess == null if (lastMemberAccess is null
|| (!(lastMemberAccess.Member is PropertyInfo) || (!(lastMemberAccess.Member is PropertyInfo)
&& !(lastMemberAccess.Member is FieldInfo))) && !(lastMemberAccess.Member is FieldInfo)))
{ {
...@@ -367,10 +367,10 @@ static void ThrowInvalidChain() ...@@ -367,10 +367,10 @@ static void ThrowInvalidChain()
} }
// Does the chain consist of MemberExpressions leading to a ParameterExpression of type T? // Does the chain consist of MemberExpressions leading to a ParameterExpression of type T?
MemberExpression diving = lastMemberAccess; MemberExpression? diving = lastMemberAccess;
// Retain a list of member names and the member expressions so we can rebuild the chain. // Retain a list of member names and the member expressions so we can rebuild the chain.
List<string> names = new List<string>(); List<string?> names = new List<string?>();
List<MemberExpression> chain = new List<MemberExpression>(); List<MemberExpression?> chain = new List<MemberExpression?>();
do do
{ {
...@@ -384,18 +384,18 @@ static void ThrowInvalidChain() ...@@ -384,18 +384,18 @@ static void ThrowInvalidChain()
diving = diving?.Expression as MemberExpression; diving = diving?.Expression as MemberExpression;
#pragma warning restore IDE0019 // use pattern matching #pragma warning restore IDE0019 // use pattern matching
if (constant is object && constant.Type == typeof(T)) if (constant is not null && constant.Type == typeof(T))
{ {
break; break;
} }
else if (diving == null else if (diving is null
|| (!(diving.Member is PropertyInfo) || (!(diving.Member is PropertyInfo)
&& !(diving.Member is FieldInfo))) && !(diving.Member is FieldInfo)))
{ {
ThrowInvalidChain(); ThrowInvalidChain();
} }
} }
while (diving != null); while (diving is not null);
var dynamicParamName = string.Concat(names.ToArray()); var dynamicParamName = string.Concat(names.ToArray());
...@@ -403,8 +403,8 @@ static void ThrowInvalidChain() ...@@ -403,8 +403,8 @@ static void ThrowInvalidChain()
var lookup = string.Join("|", names.ToArray()); var lookup = string.Join("|", names.ToArray());
var cache = CachedOutputSetters<T>.Cache; var cache = CachedOutputSetters<T>.Cache;
var setter = (Action<object, DynamicParameters>)cache[lookup]; var setter = (Action<object, DynamicParameters>?)cache[lookup];
if (setter != null) goto MAKECALLBACK; if (setter is not null) goto MAKECALLBACK;
// Come on let's build a method, let's build it, let's build it now! // Come on let's build a method, let's build it, let's build it now!
var dm = new DynamicMethod("ExpressionParam" + Guid.NewGuid().ToString(), null, new[] { typeof(object), GetType() }, true); var dm = new DynamicMethod("ExpressionParam" + Guid.NewGuid().ToString(), null, new[] { typeof(object), GetType() }, true);
...@@ -416,20 +416,20 @@ static void ThrowInvalidChain() ...@@ -416,20 +416,20 @@ static void ThrowInvalidChain()
// Count - 1 to skip the last member access // Count - 1 to skip the last member access
for (var i = 0; i < chain.Count - 1; i++) for (var i = 0; i < chain.Count - 1; i++)
{ {
var member = chain[i].Member; var member = chain[i]?.Member;
if (member is PropertyInfo info) if (member is PropertyInfo info)
{ {
var get = info.GetGetMethod(true); var get = info.GetGetMethod(true);
il.Emit(OpCodes.Callvirt, get); // [Member{i}] il.Emit(OpCodes.Callvirt, get!); // [Member{i}]
} }
else // Else it must be a field! else // Else it must be a field!
{ {
il.Emit(OpCodes.Ldfld, (FieldInfo)member); // [Member{i}] il.Emit(OpCodes.Ldfld, (FieldInfo)member!); // [Member{i}]
} }
} }
var paramGetter = GetType().GetMethod("Get", new Type[] { typeof(string) }).MakeGenericMethod(lastMemberAccess.Type); var paramGetter = GetType().GetMethod("Get", new Type[] { typeof(string) })!.MakeGenericMethod(lastMemberAccess!.Type);
il.Emit(OpCodes.Ldarg_1); // [target] [DynamicParameters] il.Emit(OpCodes.Ldarg_1); // [target] [DynamicParameters]
il.Emit(OpCodes.Ldstr, dynamicParamName); // [target] [DynamicParameters] [ParamName] il.Emit(OpCodes.Ldstr, dynamicParamName); // [target] [DynamicParameters] [ParamName]
...@@ -440,7 +440,7 @@ static void ThrowInvalidChain() ...@@ -440,7 +440,7 @@ static void ThrowInvalidChain()
if (lastMember is PropertyInfo property) if (lastMember is PropertyInfo property)
{ {
var set = property.GetSetMethod(true); var set = property.GetSetMethod(true);
il.Emit(OpCodes.Callvirt, set); // SET il.Emit(OpCodes.Callvirt, set!); // SET
} }
else else
{ {
...@@ -463,7 +463,7 @@ static void ThrowInvalidChain() ...@@ -463,7 +463,7 @@ static void ThrowInvalidChain()
var targetMemberType = lastMemberAccess?.Type; var targetMemberType = lastMemberAccess?.Type;
int sizeToSet = (!size.HasValue && targetMemberType == typeof(string)) ? DbString.DefaultLength : size ?? 0; int sizeToSet = (!size.HasValue && targetMemberType == typeof(string)) ? DbString.DefaultLength : size ?? 0;
if (parameters.TryGetValue(dynamicParamName, out ParamInfo parameter)) if (parameters.TryGetValue(dynamicParamName, out ParamInfo? parameter))
{ {
parameter.ParameterDirection = parameter.AttachedParam.Direction = ParameterDirection.InputOutput; parameter.ParameterDirection = parameter.AttachedParam.Direction = ParameterDirection.InputOutput;
...@@ -481,13 +481,13 @@ static void ThrowInvalidChain() ...@@ -481,13 +481,13 @@ static void ThrowInvalidChain()
parameter = parameters[dynamicParamName]; parameter = parameters[dynamicParamName];
parameter.OutputCallback = setter; parameter.OutputCallback = setter;
parameter.OutputTarget = target; parameter.OutputTarget = target!;
}); });
return this; return this;
} }
private List<Action> outputCallbacks; private List<Action>? outputCallbacks;
void SqlMapper.IParameterCallbacks.OnCompleted() void SqlMapper.IParameterCallbacks.OnCompleted()
{ {
......
...@@ -22,10 +22,10 @@ internal static class Extensions ...@@ -22,10 +22,10 @@ internal static class Extensions
return source.Task; return source.Task;
} }
private static void OnTaskCompleted<TFrom, TTo>(Task<TFrom> completedTask, object state) private static void OnTaskCompleted<TFrom, TTo>(Task<TFrom> completedTask, object? state)
where TFrom : TTo where TFrom : TTo
{ {
var source = (TaskCompletionSource<TTo>)state; var source = (TaskCompletionSource<TTo>)state!;
switch (completedTask.Status) switch (completedTask.Status)
{ {
...@@ -36,7 +36,7 @@ internal static class Extensions ...@@ -36,7 +36,7 @@ internal static class Extensions
source.SetCanceled(); source.SetCanceled();
break; break;
case TaskStatus.Faulted: case TaskStatus.Faulted:
source.SetException(completedTask.Exception.InnerExceptions); source.SetException(completedTask.Exception!.InnerExceptions);
break; break;
} }
} }
......
...@@ -17,9 +17,9 @@ private static readonly FeatureSupport ...@@ -17,9 +17,9 @@ private static readonly FeatureSupport
/// Gets the feature set based on the passed connection /// Gets the feature set based on the passed connection
/// </summary> /// </summary>
/// <param name="connection">The connection to get supported features for.</param> /// <param name="connection">The connection to get supported features for.</param>
public static FeatureSupport Get(IDbConnection connection) public static FeatureSupport Get(IDbConnection? connection)
{ {
string name = connection?.GetType().Name; string? name = connection?.GetType().Name;
if (string.Equals(name, "npgsqlconnection", StringComparison.OrdinalIgnoreCase)) return Postgres; if (string.Equals(name, "npgsqlconnection", StringComparison.OrdinalIgnoreCase)) return Postgres;
if (string.Equals(name, "clickhouseconnection", StringComparison.OrdinalIgnoreCase)) return ClickHouse; if (string.Equals(name, "clickhouseconnection", StringComparison.OrdinalIgnoreCase)) return ClickHouse;
return Default; return Default;
......
#if !NET5_0_OR_GREATER
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
public bool ReturnValue { get; }
}
}
#endif
 [module: System.Runtime.CompilerServices.SkipLocalsInit]
#if PLAT_SKIP_LOCALS_INIT
[module: System.Runtime.CompilerServices.SkipLocalsInit] #if !NET5_0_OR_GREATER
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
internal sealed class SkipLocalsInitAttribute : Attribute {}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
internal sealed class MemberNotNullAttribute : Attribute
{
public MemberNotNullAttribute(string member) {}
public MemberNotNullAttribute(params string[] members) {}
}
}
#endif #endif
此差异已折叠。
#nullable enable
\ No newline at end of file
#nullable enable
\ No newline at end of file
#nullable enable
\ No newline at end of file
#nullable enable
Dapper.SqlMapper.GridReader.DisposeAsync() -> System.Threading.Tasks.ValueTask
Dapper.SqlMapper.GridReader.ReadUnbufferedAsync() -> System.Collections.Generic.IAsyncEnumerable<dynamic!>!
Dapper.SqlMapper.GridReader.ReadUnbufferedAsync<T>() -> System.Collections.Generic.IAsyncEnumerable<T>!
static Dapper.SqlMapper.QueryUnbufferedAsync(this System.Data.Common.DbConnection! cnn, string! sql, object? param = null, System.Data.Common.DbTransaction? transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) -> System.Collections.Generic.IAsyncEnumerable<dynamic!>!
static Dapper.SqlMapper.QueryUnbufferedAsync<T>(this System.Data.Common.DbConnection! cnn, string! sql, object? param = null, System.Data.Common.DbTransaction? transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) -> System.Collections.Generic.IAsyncEnumerable<T>!
\ No newline at end of file
#nullable enable
\ No newline at end of file
#nullable enable
\ No newline at end of file
#nullable enable
\ No newline at end of file
...@@ -49,21 +49,21 @@ public SimpleMemberMap(string columnName, ParameterInfo parameter) ...@@ -49,21 +49,21 @@ public SimpleMemberMap(string columnName, ParameterInfo parameter)
/// <summary> /// <summary>
/// Target member type /// Target member type
/// </summary> /// </summary>
public Type MemberType => Field?.FieldType ?? Property?.PropertyType ?? Parameter?.ParameterType; public Type MemberType => Field?.FieldType ?? Property?.PropertyType ?? Parameter?.ParameterType!;
/// <summary> /// <summary>
/// Target property /// Target property
/// </summary> /// </summary>
public PropertyInfo Property { get; } public PropertyInfo? Property { get; }
/// <summary> /// <summary>
/// Target field /// Target field
/// </summary> /// </summary>
public FieldInfo Field { get; } public FieldInfo? Field { get; }
/// <summary> /// <summary>
/// Target constructor parameter /// Target constructor parameter
/// </summary> /// </summary>
public ParameterInfo Parameter { get; } public ParameterInfo? Parameter { get; }
} }
} }
...@@ -35,9 +35,9 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam ...@@ -35,9 +35,9 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
command.Parameters.Add(param); command.Parameters.Add(param);
} }
internal static void Set(IDbDataParameter parameter, IEnumerable<T> data, string typeName) internal static void Set(IDbDataParameter parameter, IEnumerable<T>? data, string? typeName)
{ {
parameter.Value = data != null && data.Any() ? data : null; parameter.Value = data is not null && data.Any() ? data : null;
StructuredHelper.ConfigureTVP(parameter, typeName); StructuredHelper.ConfigureTVP(parameter, typeName);
} }
} }
...@@ -46,16 +46,16 @@ static class StructuredHelper ...@@ -46,16 +46,16 @@ static class StructuredHelper
private static readonly Hashtable s_udt = new Hashtable(), s_tvp = new Hashtable(); private static readonly Hashtable s_udt = new Hashtable(), s_tvp = new Hashtable();
private static Action<IDbDataParameter, string> GetUDT(Type type) private static Action<IDbDataParameter, string> GetUDT(Type type)
=> (Action<IDbDataParameter, string>)s_udt[type] ?? SlowGetHelper(type, s_udt, "UdtTypeName", 29); // 29 = SqlDbType.Udt (avoiding ref) => (Action<IDbDataParameter, string?>?)s_udt[type] ?? SlowGetHelper(type, s_udt, "UdtTypeName", 29); // 29 = SqlDbType.Udt (avoiding ref)
private static Action<IDbDataParameter, string> GetTVP(Type type) private static Action<IDbDataParameter, string?> GetTVP(Type type)
=> (Action<IDbDataParameter, string>)s_tvp[type] ?? SlowGetHelper(type, s_tvp, "TypeName", 30); // 30 = SqlDbType.Structured (avoiding ref) => (Action<IDbDataParameter, string?>?)s_tvp[type] ?? SlowGetHelper(type, s_tvp, "TypeName", 30); // 30 = SqlDbType.Structured (avoiding ref)
static Action<IDbDataParameter, string> SlowGetHelper(Type type, Hashtable hashtable, string nameProperty, int sqlDbType) static Action<IDbDataParameter, string?> SlowGetHelper(Type type, Hashtable hashtable, string nameProperty, int sqlDbType)
{ {
lock (hashtable) lock (hashtable)
{ {
var helper = (Action<IDbDataParameter, string>)hashtable[type]; var helper = (Action<IDbDataParameter, string?>?)hashtable[type];
if (helper == null) if (helper is null)
{ {
helper = CreateFor(type, nameProperty, sqlDbType); helper = CreateFor(type, nameProperty, sqlDbType);
hashtable.Add(type, helper); hashtable.Add(type, helper);
...@@ -64,16 +64,16 @@ static class StructuredHelper ...@@ -64,16 +64,16 @@ static class StructuredHelper
} }
} }
static Action<IDbDataParameter, string> CreateFor(Type type, string nameProperty, int sqlDbType) static Action<IDbDataParameter, string?> CreateFor(Type type, string nameProperty, int sqlDbType)
{ {
var name = type.GetProperty(nameProperty, BindingFlags.Public | BindingFlags.Instance); var name = type.GetProperty(nameProperty, BindingFlags.Public | BindingFlags.Instance);
if (name == null || !name.CanWrite) if (name is null || !name.CanWrite)
{ {
return (p, n) => { }; return (p, n) => { };
} }
var dbType = type.GetProperty("SqlDbType", BindingFlags.Public | BindingFlags.Instance); var dbType = type.GetProperty("SqlDbType", BindingFlags.Public | BindingFlags.Instance);
if (dbType != null && !dbType.CanWrite) dbType = null; if (dbType is not null && !dbType.CanWrite) dbType = null;
var dm = new DynamicMethod(nameof(CreateFor) + "_" + type.Name, null, var dm = new DynamicMethod(nameof(CreateFor) + "_" + type.Name, null,
new[] { typeof(IDbDataParameter), typeof(string) }, true); new[] { typeof(IDbDataParameter), typeof(string) }, true);
...@@ -81,18 +81,18 @@ static class StructuredHelper ...@@ -81,18 +81,18 @@ static class StructuredHelper
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, name.GetSetMethod(), null); il.EmitCall(OpCodes.Callvirt, name.GetSetMethod()!, null);
if (dbType != null) if (dbType is not null)
{ {
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Ldc_I4, sqlDbType); il.Emit(OpCodes.Ldc_I4, sqlDbType);
il.EmitCall(OpCodes.Callvirt, dbType.GetSetMethod(), null); il.EmitCall(OpCodes.Callvirt, dbType.GetSetMethod()!, null);
} }
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
return (Action<IDbDataParameter, string>)dm.CreateDelegate(typeof(Action<IDbDataParameter, string>)); return (Action<IDbDataParameter, string?>)dm.CreateDelegate(typeof(Action<IDbDataParameter, string?>));
} }
...@@ -100,7 +100,7 @@ static class StructuredHelper ...@@ -100,7 +100,7 @@ static class StructuredHelper
// would be a fair option otherwise // would be a fair option otherwise
internal static void ConfigureUDT(IDbDataParameter parameter, string typeName) internal static void ConfigureUDT(IDbDataParameter parameter, string typeName)
=> GetUDT(parameter.GetType())(parameter, typeName); => GetUDT(parameter.GetType())(parameter, typeName);
internal static void ConfigureTVP(IDbDataParameter parameter, string typeName) internal static void ConfigureTVP(IDbDataParameter parameter, string? typeName)
=> GetTVP(parameter.GetType())(parameter, typeName); => GetTVP(parameter.GetType())(parameter, typeName);
} }
} }
此差异已折叠。
...@@ -10,8 +10,8 @@ public static partial class SqlMapper ...@@ -10,8 +10,8 @@ public static partial class SqlMapper
private class CacheInfo private class CacheInfo
{ {
public DeserializerState Deserializer { get; set; } public DeserializerState Deserializer { get; set; }
public Func<DbDataReader, object>[] OtherDeserializers { get; set; } public Func<DbDataReader, object>[]? OtherDeserializers { get; set; }
public Action<IDbCommand, object> ParamReader { get; set; } public Action<IDbCommand, object?>? ParamReader { get; set; }
private int hitCount; private int hitCount;
public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); } public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }
public void RecordHit() { Interlocked.Increment(ref hitCount); } public void RecordHit() { Interlocked.Increment(ref hitCount); }
......
...@@ -25,7 +25,7 @@ public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object ...@@ -25,7 +25,7 @@ public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object
// public DapperRowList(DapperTable table) { _table = table; } // public DapperRowList(DapperTable table) { _table = table; }
// PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) // PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
// { // {
// if (listAccessors != null && listAccessors.Length != 0) return PropertyDescriptorCollection.Empty; // if (listAccessors is not null && listAccessors.Length != 0) return PropertyDescriptorCollection.Empty;
// return DapperRowTypeDescriptor.GetProperties(_table); // return DapperRowTypeDescriptor.GetProperties(_table);
// } // }
...@@ -42,32 +42,32 @@ public DapperRowTypeDescriptor(object instance) ...@@ -42,32 +42,32 @@ public DapperRowTypeDescriptor(object instance)
AttributeCollection ICustomTypeDescriptor.GetAttributes() AttributeCollection ICustomTypeDescriptor.GetAttributes()
=> AttributeCollection.Empty; => AttributeCollection.Empty;
string ICustomTypeDescriptor.GetClassName() => typeof(DapperRow).FullName; string ICustomTypeDescriptor.GetClassName() => typeof(DapperRow).FullName!;
string ICustomTypeDescriptor.GetComponentName() => null; string ICustomTypeDescriptor.GetComponentName() => null!;
private static readonly TypeConverter s_converter = new ExpandableObjectConverter(); private static readonly TypeConverter s_converter = new ExpandableObjectConverter();
TypeConverter ICustomTypeDescriptor.GetConverter() => s_converter; TypeConverter ICustomTypeDescriptor.GetConverter() => s_converter;
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null!;
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null!;
object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null!;
EventDescriptorCollection ICustomTypeDescriptor.GetEvents() => EventDescriptorCollection.Empty; EventDescriptorCollection ICustomTypeDescriptor.GetEvents() => EventDescriptorCollection.Empty;
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => EventDescriptorCollection.Empty; EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => EventDescriptorCollection.Empty;
internal static PropertyDescriptorCollection GetProperties(DapperRow row) => GetProperties(row?.table, row); 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(DapperTable? table, IDictionary<string,object?>? row = null)
{ {
string[] names = table?.FieldNames; string[]? names = table?.FieldNames;
if (names == null || names.Length == 0) return PropertyDescriptorCollection.Empty; if (names is null || names.Length == 0) return PropertyDescriptorCollection.Empty;
var arr = new PropertyDescriptor[names.Length]; var arr = new PropertyDescriptor[names.Length];
for (int i = 0; i < arr.Length; i++) for (int i = 0; i < arr.Length; i++)
{ {
var type = row != null && row.TryGetValue(names[i], out var value) && value != null var type = row is not null && row.TryGetValue(names[i], out var value) && value is not null
? value.GetType() : typeof(object); ? value.GetType() : typeof(object);
arr[i] = new RowBoundPropertyDescriptor(type, names[i], i); arr[i] = new RowBoundPropertyDescriptor(type, names[i], i);
} }
...@@ -97,7 +97,7 @@ public RowBoundPropertyDescriptor(Type type, string name, int index) : base(name ...@@ -97,7 +97,7 @@ public RowBoundPropertyDescriptor(Type type, string name, int index) : base(name
public override Type PropertyType => _type; public override Type PropertyType => _type;
public override object GetValue(object component) public override object GetValue(object component)
=> ((DapperRow)component).TryGetValue(_index, out var val) ? (val ?? DBNull.Value): DBNull.Value; => ((DapperRow)component).TryGetValue(_index, out var val) ? (val ?? DBNull.Value): DBNull.Value;
public override void SetValue(object component, object value) public override void SetValue(object component, object? value)
=> ((DapperRow)component).SetValue(_index, value is DBNull ? null : value); => ((DapperRow)component).SetValue(_index, value is DBNull ? null : value);
} }
} }
......
...@@ -8,13 +8,13 @@ namespace Dapper ...@@ -8,13 +8,13 @@ namespace Dapper
public static partial class SqlMapper public static partial class SqlMapper
{ {
private sealed partial class DapperRow private sealed partial class DapperRow
: IDictionary<string, object> : IDictionary<string, object?>
, IReadOnlyDictionary<string, object> , IReadOnlyDictionary<string, object?>
{ {
private readonly DapperTable table; private readonly DapperTable table;
private object[] values; private object?[] values;
public DapperRow(DapperTable table, object[] values) public DapperRow(DapperTable table, object?[] values)
{ {
this.table = table ?? throw new ArgumentNullException(nameof(table)); this.table = table ?? throw new ArgumentNullException(nameof(table));
this.values = values ?? throw new ArgumentNullException(nameof(values)); this.values = values ?? throw new ArgumentNullException(nameof(values));
...@@ -26,7 +26,7 @@ private sealed class DeadValue ...@@ -26,7 +26,7 @@ private sealed class DeadValue
private DeadValue() { /* hiding constructor */ } private DeadValue() { /* hiding constructor */ }
} }
int ICollection<KeyValuePair<string, object>>.Count int ICollection<KeyValuePair<string, object?>>.Count
{ {
get get
{ {
...@@ -39,10 +39,10 @@ private sealed class DeadValue ...@@ -39,10 +39,10 @@ private sealed class DeadValue
} }
} }
public bool TryGetValue(string key, out object value) public bool TryGetValue(string key, out object? value)
=> TryGetValue(table.IndexOfName(key), out value); => TryGetValue(table.IndexOfName(key), out value);
internal bool TryGetValue(int index, out object value) internal bool TryGetValue(int index, out object? value)
{ {
if (index < 0) if (index < 0)
{ // doesn't exist { // doesn't exist
...@@ -66,7 +66,7 @@ public override string ToString() ...@@ -66,7 +66,7 @@ public override string ToString()
{ {
var value = kv.Value; var value = kv.Value;
sb.Append(", ").Append(kv.Key); sb.Append(", ").Append(kv.Key);
if (value != null) if (value is not null)
{ {
sb.Append(" = '").Append(kv.Value).Append('\''); sb.Append(" = '").Append(kv.Value).Append('\'');
} }
...@@ -79,15 +79,15 @@ public override string ToString() ...@@ -79,15 +79,15 @@ public override string ToString()
return sb.Append('}').ToStringRecycle(); return sb.Append('}').ToStringRecycle();
} }
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
{ {
var names = table.FieldNames; var names = table.FieldNames;
for (var i = 0; i < names.Length; i++) for (var i = 0; i < names.Length; i++)
{ {
object value = i < values.Length ? values[i] : null; object? value = i < values.Length ? values[i] : null!;
if (!(value is DeadValue)) if (!(value is DeadValue))
{ {
yield return new KeyValuePair<string, object>(names[i], value); yield return new KeyValuePair<string, object?>(names[i], value);
} }
} }
} }
...@@ -99,24 +99,24 @@ IEnumerator IEnumerable.GetEnumerator() ...@@ -99,24 +99,24 @@ IEnumerator IEnumerable.GetEnumerator()
#region Implementation of ICollection<KeyValuePair<string,object>> #region Implementation of ICollection<KeyValuePair<string,object>>
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) void ICollection<KeyValuePair<string, object?>>.Add(KeyValuePair<string, object?> item)
{ {
IDictionary<string, object> dic = this; IDictionary<string, object?> dic = this;
dic.Add(item.Key, item.Value); dic.Add(item.Key, item.Value);
} }
void ICollection<KeyValuePair<string, object>>.Clear() void ICollection<KeyValuePair<string, object?>>.Clear()
{ // removes values for **this row**, but doesn't change the fundamental table { // removes values for **this row**, but doesn't change the fundamental table
for (int i = 0; i < values.Length; i++) for (int i = 0; i < values.Length; i++)
values[i] = DeadValue.Default; values[i] = DeadValue.Default;
} }
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) bool ICollection<KeyValuePair<string, object?>>.Contains(KeyValuePair<string, object?> item)
{ {
return TryGetValue(item.Key, out object value) && Equals(value, item.Value); return TryGetValue(item.Key, out object? value) && Equals(value, item.Value);
} }
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) void ICollection<KeyValuePair<string, object?>>.CopyTo(KeyValuePair<string, object?>[] array, int arrayIndex)
{ {
foreach (var kv in this) foreach (var kv in this)
{ {
...@@ -124,30 +124,30 @@ IEnumerator IEnumerable.GetEnumerator() ...@@ -124,30 +124,30 @@ IEnumerator IEnumerable.GetEnumerator()
} }
} }
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) bool ICollection<KeyValuePair<string, object?>>.Remove(KeyValuePair<string, object?> item)
{ {
IDictionary<string, object> dic = this; IDictionary<string, object?> dic = this;
return dic.Remove(item.Key); return dic.Remove(item.Key);
} }
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false; bool ICollection<KeyValuePair<string, object?>>.IsReadOnly => false;
#endregion #endregion
#region Implementation of IDictionary<string,object> #region Implementation of IDictionary<string,object>
bool IDictionary<string, object>.ContainsKey(string key) bool IDictionary<string, object?>.ContainsKey(string key)
{ {
int index = table.IndexOfName(key); int index = table.IndexOfName(key);
if (index < 0 || index >= values.Length || values[index] is DeadValue) return false; if (index < 0 || index >= values.Length || values[index] is DeadValue) return false;
return true; return true;
} }
void IDictionary<string, object>.Add(string key, object value) void IDictionary<string, object?>.Add(string key, object? value)
{ {
SetValue(key, value, true); SetValue(key, value, true);
} }
bool IDictionary<string, object>.Remove(string key) bool IDictionary<string, object?>.Remove(string key)
=> Remove(table.IndexOfName(key)); => Remove(table.IndexOfName(key));
internal bool Remove(int index) internal bool Remove(int index)
...@@ -157,20 +157,20 @@ internal bool Remove(int index) ...@@ -157,20 +157,20 @@ internal bool Remove(int index)
return true; return true;
} }
object IDictionary<string, object>.this[string key] object? IDictionary<string, object?>.this[string key]
{ {
get { TryGetValue(key, out object val); return val; } get { TryGetValue(key, out object? val); return val; }
set { SetValue(key, value, false); } set { SetValue(key, value, false); }
} }
public object SetValue(string key, object value) public object? SetValue(string key, object? value)
{ {
return SetValue(key, value, false); return SetValue(key, value, false);
} }
private object SetValue(string key, object value, bool isAdd) private object? SetValue(string key, object? value, bool isAdd)
{ {
if (key == null) throw new ArgumentNullException(nameof(key)); if (key is null) throw new ArgumentNullException(nameof(key));
int index = table.IndexOfName(key); int index = table.IndexOfName(key);
if (index < 0) if (index < 0)
{ {
...@@ -183,7 +183,7 @@ private object SetValue(string key, object value, bool isAdd) ...@@ -183,7 +183,7 @@ private object SetValue(string key, object value, bool isAdd)
} }
return SetValue(index, value); return SetValue(index, value);
} }
internal object SetValue(int index, object value) internal object? SetValue(int index, object? value)
{ {
int oldLength = values.Length; int oldLength = values.Length;
if (oldLength <= index) if (oldLength <= index)
...@@ -199,12 +199,12 @@ internal object SetValue(int index, object value) ...@@ -199,12 +199,12 @@ internal object SetValue(int index, object value)
return values[index] = value; return values[index] = value;
} }
ICollection<string> IDictionary<string, object>.Keys ICollection<string> IDictionary<string, object?>.Keys
{ {
get { return this.Select(kv => kv.Key).ToArray(); } get { return this.Select(kv => kv.Key).ToArray(); }
} }
ICollection<object> IDictionary<string, object>.Values ICollection<object?> IDictionary<string, object?>.Values
{ {
get { return this.Select(kv => kv.Value).ToArray(); } get { return this.Select(kv => kv.Value).ToArray(); }
} }
...@@ -215,7 +215,7 @@ internal object SetValue(int index, object value) ...@@ -215,7 +215,7 @@ internal object SetValue(int index, object value)
#region Implementation of IReadOnlyDictionary<string,object> #region Implementation of IReadOnlyDictionary<string,object>
int IReadOnlyCollection<KeyValuePair<string, object>>.Count int IReadOnlyCollection<KeyValuePair<string, object?>>.Count
{ {
get get
{ {
...@@ -223,23 +223,23 @@ internal object SetValue(int index, object value) ...@@ -223,23 +223,23 @@ internal object SetValue(int index, object value)
} }
} }
bool IReadOnlyDictionary<string, object>.ContainsKey(string key) bool IReadOnlyDictionary<string, object?>.ContainsKey(string key)
{ {
int index = table.IndexOfName(key); int index = table.IndexOfName(key);
return index >= 0 && index < values.Length && !(values[index] is DeadValue); return index >= 0 && index < values.Length && !(values[index] is DeadValue);
} }
object IReadOnlyDictionary<string, object>.this[string key] object? IReadOnlyDictionary<string, object?>.this[string key]
{ {
get { TryGetValue(key, out object val); return val; } get { TryGetValue(key, out object? val); return val; }
} }
IEnumerable<string> IReadOnlyDictionary<string, object>.Keys IEnumerable<string> IReadOnlyDictionary<string, object?>.Keys
{ {
get { return this.Select(kv => kv.Key); } get { return this.Select(kv => kv.Key); }
} }
IEnumerable<object> IReadOnlyDictionary<string, object>.Values IEnumerable<object?> IReadOnlyDictionary<string, object?>.Values
{ {
get { return this.Select(kv => kv.Value); } get { return this.Select(kv => kv.Value); }
} }
......
...@@ -16,8 +16,8 @@ private sealed partial class DapperRow : System.Dynamic.IDynamicMetaObjectProvid ...@@ -16,8 +16,8 @@ private sealed partial class DapperRow : System.Dynamic.IDynamicMetaObjectProvid
private sealed class DapperRowMetaObject : System.Dynamic.DynamicMetaObject private sealed class DapperRowMetaObject : System.Dynamic.DynamicMetaObject
{ {
private static readonly MethodInfo getValueMethod = typeof(IDictionary<string, object>).GetProperty("Item").GetGetMethod(); private static readonly MethodInfo getValueMethod = typeof(IDictionary<string, object>).GetProperty("Item")!.GetGetMethod()!;
private static readonly MethodInfo setValueMethod = typeof(DapperRow).GetMethod("SetValue", new Type[] { typeof(string), typeof(object) }); private static readonly MethodInfo setValueMethod = typeof(DapperRow).GetMethod("SetValue", new Type[] { typeof(string), typeof(object) })!;
public DapperRowMetaObject( public DapperRowMetaObject(
System.Linq.Expressions.Expression expression, System.Linq.Expressions.Expression expression,
......
...@@ -21,18 +21,18 @@ public DapperTable(string[] fieldNames) ...@@ -21,18 +21,18 @@ public DapperTable(string[] fieldNames)
for (int i = fieldNames.Length - 1; i >= 0; i--) for (int i = fieldNames.Length - 1; i >= 0; i--)
{ {
string key = fieldNames[i]; string key = fieldNames[i];
if (key != null) fieldNameLookup[key] = i; if (key is not null) fieldNameLookup[key] = i;
} }
} }
internal int IndexOfName(string name) internal int IndexOfName(string name)
{ {
return (name != null && fieldNameLookup.TryGetValue(name, out int result)) ? result : -1; return (name is not null && fieldNameLookup.TryGetValue(name, out int result)) ? result : -1;
} }
internal int AddField(string name) internal int AddField(string name)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name is null) throw new ArgumentNullException(nameof(name));
if (fieldNameLookup.ContainsKey(name)) throw new InvalidOperationException("Field already exists: " + name); if (fieldNameLookup.ContainsKey(name)) throw new InvalidOperationException("Field already exists: " + name);
int oldLen = fieldNames.Length; int oldLen = fieldNames.Length;
Array.Resize(ref fieldNames, oldLen + 1); // yes, this is sub-optimal, but this is not the expected common case Array.Resize(ref fieldNames, oldLen + 1); // yes, this is sub-optimal, but this is not the expected common case
...@@ -41,7 +41,7 @@ internal int AddField(string name) ...@@ -41,7 +41,7 @@ internal int AddField(string name)
return oldLen; return oldLen;
} }
internal bool FieldExists(string key) => key != null && fieldNameLookup.ContainsKey(key); internal bool FieldExists(string key) => key is not null && fieldNameLookup.ContainsKey(key);
public int FieldCount => fieldNames.Length; public int FieldCount => fieldNames.Length;
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -16,13 +14,6 @@ public partial class GridReader ...@@ -16,13 +14,6 @@ public partial class GridReader
: IAsyncDisposable : IAsyncDisposable
#endif #endif
{ {
private readonly CancellationToken cancel;
internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, DynamicParameters dynamicParams, bool addToCache, CancellationToken cancel)
: this(command, reader, identity, dynamicParams, addToCache)
{
this.cancel = cancel;
}
/// <summary> /// <summary>
/// Read the next grid of results, returned as a dynamic object /// Read the next grid of results, returned as a dynamic object
/// </summary> /// </summary>
...@@ -40,7 +31,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -40,7 +31,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// Read an individual row of the next grid of results, returned as a dynamic object /// Read an individual row of the next grid of results, returned as a dynamic object
/// </summary> /// </summary>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks> /// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public Task<dynamic> ReadFirstOrDefaultAsync() => ReadRowAsyncImpl<dynamic>(typeof(DapperRow), Row.FirstOrDefault); public Task<dynamic?> ReadFirstOrDefaultAsync() => ReadRowAsyncImpl<dynamic?>(typeof(DapperRow), Row.FirstOrDefault);
/// <summary> /// <summary>
/// Read an individual row of the next grid of results, returned as a dynamic object /// Read an individual row of the next grid of results, returned as a dynamic object
...@@ -52,7 +43,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -52,7 +43,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// Read an individual row of the next grid of results, returned as a dynamic object /// Read an individual row of the next grid of results, returned as a dynamic object
/// </summary> /// </summary>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks> /// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public Task<dynamic> ReadSingleOrDefaultAsync() => ReadRowAsyncImpl<dynamic>(typeof(DapperRow), Row.SingleOrDefault); public Task<dynamic?> ReadSingleOrDefaultAsync() => ReadRowAsyncImpl<dynamic?>(typeof(DapperRow), Row.SingleOrDefault);
/// <summary> /// <summary>
/// Read the next grid of results /// Read the next grid of results
...@@ -62,7 +53,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -62,7 +53,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public Task<IEnumerable<object>> ReadAsync(Type type, bool buffered = true) public Task<IEnumerable<object>> ReadAsync(Type type, bool buffered = true)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadAsyncImpl<object>(type, buffered); return ReadAsyncImpl<object>(type, buffered);
} }
...@@ -73,7 +64,7 @@ public Task<IEnumerable<object>> ReadAsync(Type type, bool buffered = true) ...@@ -73,7 +64,7 @@ public Task<IEnumerable<object>> ReadAsync(Type type, bool buffered = true)
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public Task<object> ReadFirstAsync(Type type) public Task<object> ReadFirstAsync(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRowAsyncImpl<object>(type, Row.First); return ReadRowAsyncImpl<object>(type, Row.First);
} }
...@@ -82,10 +73,10 @@ public Task<object> ReadFirstAsync(Type type) ...@@ -82,10 +73,10 @@ public Task<object> ReadFirstAsync(Type type)
/// </summary> /// </summary>
/// <param name="type">The type to read.</param> /// <param name="type">The type to read.</param>
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public Task<object> ReadFirstOrDefaultAsync(Type type) public Task<object?> ReadFirstOrDefaultAsync(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRowAsyncImpl<object>(type, Row.FirstOrDefault); return ReadRowAsyncImpl<object?>(type, Row.FirstOrDefault);
} }
/// <summary> /// <summary>
...@@ -95,7 +86,7 @@ public Task<object> ReadFirstOrDefaultAsync(Type type) ...@@ -95,7 +86,7 @@ public Task<object> ReadFirstOrDefaultAsync(Type type)
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public Task<object> ReadSingleAsync(Type type) public Task<object> ReadSingleAsync(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRowAsyncImpl<object>(type, Row.Single); return ReadRowAsyncImpl<object>(type, Row.Single);
} }
...@@ -104,10 +95,10 @@ public Task<object> ReadSingleAsync(Type type) ...@@ -104,10 +95,10 @@ public Task<object> ReadSingleAsync(Type type)
/// </summary> /// </summary>
/// <param name="type">The type to read.</param> /// <param name="type">The type to read.</param>
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public Task<object> ReadSingleOrDefaultAsync(Type type) public Task<object?> ReadSingleOrDefaultAsync(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRowAsyncImpl<object>(type, Row.SingleOrDefault); return ReadRowAsyncImpl<object?>(type, Row.SingleOrDefault);
} }
/// <summary> /// <summary>
...@@ -127,7 +118,7 @@ public Task<object> ReadSingleOrDefaultAsync(Type type) ...@@ -127,7 +118,7 @@ public Task<object> ReadSingleOrDefaultAsync(Type type)
/// Read an individual row of the next grid of results. /// Read an individual row of the next grid of results.
/// </summary> /// </summary>
/// <typeparam name="T">The type to read.</typeparam> /// <typeparam name="T">The type to read.</typeparam>
public Task<T> ReadFirstOrDefaultAsync<T>() => ReadRowAsyncImpl<T>(typeof(T), Row.FirstOrDefault); public Task<T?> ReadFirstOrDefaultAsync<T>() => ReadRowAsyncImpl<T?>(typeof(T), Row.FirstOrDefault);
/// <summary> /// <summary>
/// Read an individual row of the next grid of results. /// Read an individual row of the next grid of results.
...@@ -139,15 +130,25 @@ public Task<object> ReadSingleOrDefaultAsync(Type type) ...@@ -139,15 +130,25 @@ public Task<object> ReadSingleOrDefaultAsync(Type type)
/// Read an individual row of the next grid of results. /// Read an individual row of the next grid of results.
/// </summary> /// </summary>
/// <typeparam name="T">The type to read.</typeparam> /// <typeparam name="T">The type to read.</typeparam>
public Task<T> ReadSingleOrDefaultAsync<T>() => ReadRowAsyncImpl<T>(typeof(T), Row.SingleOrDefault); public Task<T?> ReadSingleOrDefaultAsync<T>() => ReadRowAsyncImpl<T?>(typeof(T), Row.SingleOrDefault);
private async Task NextResultAsync() /// <summary>
/// Marks the current grid as consumed, and moves to the next result
/// </summary>
protected async Task OnAfterGridAsync(int index)
{ {
if (await reader.NextResultAsync(cancel).ConfigureAwait(false)) if (index != ResultIndex)
{
// not our data
}
else if (reader is null)
{
// nothing to do
}
else if (await reader.NextResultAsync(cancel).ConfigureAwait(false))
{ {
// readCount++; // readCount++;
gridIndex++; _resultIndexAndConsumedFlag = index + 1;
IsConsumed = false;
} }
else else
{ {
...@@ -158,8 +159,8 @@ private async Task NextResultAsync() ...@@ -158,8 +159,8 @@ private async Task NextResultAsync()
#else #else
reader.Dispose(); reader.Dispose();
#endif #endif
reader = null; reader = null!;
callbacks?.OnCompleted(); onCompleted?.Invoke(state);
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
await DisposeAsync(); await DisposeAsync();
#else #else
...@@ -170,52 +171,47 @@ private async Task NextResultAsync() ...@@ -170,52 +171,47 @@ private async Task NextResultAsync()
private Task<IEnumerable<T>> ReadAsyncImpl<T>(Type type, bool buffered) private Task<IEnumerable<T>> ReadAsyncImpl<T>(Type type, bool buffered)
{ {
var deserializer = ValidateAndMarkConsumed(type); var deserializer = ValidateAndMarkConsumed(type, out var index);
if (buffered) if (buffered)
{ {
return ReadBufferedAsync<T>(gridIndex, deserializer); return ReadBufferedAsync<T>(index, deserializer);
} }
else else
{ {
var result = ReadDeferred<T>(gridIndex, deserializer, type); var result = ReadDeferred<T>(index, deserializer, type);
if (buffered) result = result?.ToList(); // for the "not a DbDataReader" scenario
return Task.FromResult(result); return Task.FromResult(result);
} }
} }
private Func<DbDataReader, object> ValidateAndMarkConsumed(Type type) private Func<DbDataReader, object> ValidateAndMarkConsumed(Type type, out int index)
{ {
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed"); index = OnBeforeGrid();
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once"); var typedIdentity = Identity.ForGrid(type, index);
var typedIdentity = identity.ForGrid(type, gridIndex);
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache); CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
var deserializer = cache.Deserializer; var deserializer = cache.Deserializer;
int hash = GetColumnHash(reader); int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash) if (deserializer.Func is null || deserializer.Hash != hash)
{ {
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false)); deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
cache.Deserializer = deserializer; cache.Deserializer = deserializer;
} }
IsConsumed = true;
return deserializer.Func; return deserializer.Func;
} }
private async Task<T> ReadRowAsyncImpl<T>(Type type, Row row) private async Task<T> ReadRowAsyncImpl<T>(Type type, Row row)
{ {
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed"); var index = OnBeforeGrid();
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
IsConsumed = true; T result = default!;
T result = default;
if (await reader.ReadAsync(cancel).ConfigureAwait(false) && reader.FieldCount != 0) if (await reader.ReadAsync(cancel).ConfigureAwait(false) && reader.FieldCount != 0)
{ {
var typedIdentity = identity.ForGrid(type, gridIndex); var typedIdentity = Identity.ForGrid(type, index);
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache); CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
var deserializer = cache.Deserializer; var deserializer = cache.Deserializer;
int hash = GetColumnHash(reader); int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash) if (deserializer.Func is null || deserializer.Hash != hash)
{ {
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false)); deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
cache.Deserializer = deserializer; cache.Deserializer = deserializer;
...@@ -230,7 +226,7 @@ private async Task<T> ReadRowAsyncImpl<T>(Type type, Row row) ...@@ -230,7 +226,7 @@ private async Task<T> ReadRowAsyncImpl<T>(Type type, Row row)
{ {
ThrowZeroRows(row); ThrowZeroRows(row);
} }
await NextResultAsync().ConfigureAwait(false); await OnAfterGridAsync(index).ConfigureAwait(false);
return result; return result;
} }
...@@ -239,7 +235,7 @@ private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<DbDataRe ...@@ -239,7 +235,7 @@ private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<DbDataRe
try try
{ {
var buffer = new List<T>(); var buffer = new List<T>();
while (index == gridIndex && await reader.ReadAsync(cancel).ConfigureAwait(false)) while (index == ResultIndex && await reader!.ReadAsync(cancel).ConfigureAwait(false))
{ {
buffer.Add(ConvertTo<T>(deserializer(reader))); buffer.Add(ConvertTo<T>(deserializer(reader)));
} }
...@@ -247,10 +243,7 @@ private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<DbDataRe ...@@ -247,10 +243,7 @@ private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<DbDataRe
} }
finally // finally so that First etc progresses things even when multiple rows finally // finally so that First etc progresses things even when multiple rows
{ {
if (index == gridIndex) await OnAfterGridAsync(index).ConfigureAwait(false);
{
await NextResultAsync().ConfigureAwait(false);
}
} }
} }
...@@ -261,27 +254,29 @@ private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<DbDataRe ...@@ -261,27 +254,29 @@ private async Task<IEnumerable<T>> ReadBufferedAsync<T>(int index, Func<DbDataRe
/// <typeparam name="T">The type to read.</typeparam> /// <typeparam name="T">The type to read.</typeparam>
public IAsyncEnumerable<T> ReadUnbufferedAsync<T>() => ReadAsyncUnbufferedImpl<T>(typeof(T)); public IAsyncEnumerable<T> ReadUnbufferedAsync<T>() => ReadAsyncUnbufferedImpl<T>(typeof(T));
/// <summary>
/// Read the next grid of results.
/// </summary>
public IAsyncEnumerable<dynamic> ReadUnbufferedAsync() => ReadAsyncUnbufferedImpl<dynamic>(typeof(DapperRow));
private IAsyncEnumerable<T> ReadAsyncUnbufferedImpl<T>(Type type) private IAsyncEnumerable<T> ReadAsyncUnbufferedImpl<T>(Type type)
{ {
var deserializer = ValidateAndMarkConsumed(type); var deserializer = ValidateAndMarkConsumed(type, out var index);
return ReadUnbufferedAsync<T>(gridIndex, deserializer, cancel); return ReadUnbufferedAsync<T>(index, deserializer, cancel);
} }
private async IAsyncEnumerable<T> ReadUnbufferedAsync<T>(int index, Func<DbDataReader, object> deserializer, [EnumeratorCancellation] CancellationToken cancel) private async IAsyncEnumerable<T> ReadUnbufferedAsync<T>(int index, Func<DbDataReader, object> deserializer, [EnumeratorCancellation] CancellationToken cancel)
{ {
try try
{ {
while (index == gridIndex && await reader.ReadAsync(cancel).ConfigureAwait(false)) while (index == ResultIndex && await reader!.ReadAsync(cancel).ConfigureAwait(false))
{ {
yield return ConvertTo<T>(deserializer(reader)); yield return ConvertTo<T>(deserializer(reader));
} }
} }
finally // finally so that First etc progresses things even when multiple rows finally // finally so that First etc progresses things even when multiple rows
{ {
if (index == gridIndex) await OnAfterGridAsync(index).ConfigureAwait(false);
{
await NextResultAsync().ConfigureAwait(false);
}
} }
} }
...@@ -290,13 +285,13 @@ private async IAsyncEnumerable<T> ReadUnbufferedAsync<T>(int index, Func<DbDataR ...@@ -290,13 +285,13 @@ private async IAsyncEnumerable<T> ReadUnbufferedAsync<T>(int index, Func<DbDataR
/// </summary> /// </summary>
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
if (reader != null) if (reader is not null)
{ {
if (!reader.IsClosed) Command?.Cancel(); if (!reader.IsClosed) Command?.Cancel();
await reader.DisposeAsync(); await reader.DisposeAsync();
reader = null; reader = null!;
} }
if (Command != null) if (Command is not null)
{ {
if (Command is DbCommand typed) if (Command is DbCommand typed)
{ {
...@@ -306,7 +301,7 @@ public async ValueTask DisposeAsync() ...@@ -306,7 +301,7 @@ public async ValueTask DisposeAsync()
{ {
Command.Dispose(); Command.Dispose();
} }
Command = null; Command = null!;
} }
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading;
namespace Dapper namespace Dapper
{ {
...@@ -16,16 +18,43 @@ public static partial class SqlMapper ...@@ -16,16 +18,43 @@ public static partial class SqlMapper
public partial class GridReader : IDisposable public partial class GridReader : IDisposable
{ {
private DbDataReader reader; private DbDataReader reader;
private readonly Identity identity; private Identity? _identity;
private readonly bool addToCache; private readonly bool addToCache;
private readonly Action<object?>? onCompleted;
private readonly object? state;
private readonly CancellationToken cancel;
internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, IParameterCallbacks callbacks, bool addToCache) /// <summary>
/// Creates a grid reader over an existing command and reader
/// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
protected GridReader(IDbCommand command, DbDataReader reader, Identity? identity, Action<object?>? onCompleted = null, object? state = null, bool addToCache = false, CancellationToken cancellationToken = default)
{ {
Command = command; Command = command;
this.reader = reader; this.reader = reader;
this.identity = identity; _identity = identity;
this.callbacks = callbacks; this.onCompleted = onCompleted;
this.state = state;
this.addToCache = addToCache; this.addToCache = addToCache;
cancel = cancellationToken;
}
internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, IParameterCallbacks? callbacks, bool addToCache,
CancellationToken cancellationToken = default)
: this(command, reader, identity, callbacks is null ? null : static state => ((IParameterCallbacks)state!).OnCompleted(),
callbacks, addToCache, cancellationToken)
{ }
private Identity Identity => _identity ??= CreateIdentity();
private Identity CreateIdentity()
{
var cmd = Command;
if (cmd is not null && cmd.Connection is not null)
{
return new Identity(cmd.CommandText, cmd.CommandType, cmd.Connection, null, null);
}
throw new InvalidOperationException("This operation requires an identity or a connected command");
} }
/// <summary> /// <summary>
...@@ -45,7 +74,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -45,7 +74,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// Read an individual row of the next grid of results, returned as a dynamic object. /// Read an individual row of the next grid of results, returned as a dynamic object.
/// </summary> /// </summary>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks> /// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public dynamic ReadFirstOrDefault() => ReadRow<dynamic>(typeof(DapperRow), Row.FirstOrDefault); public dynamic? ReadFirstOrDefault() => ReadRow<dynamic>(typeof(DapperRow), Row.FirstOrDefault);
/// <summary> /// <summary>
/// Read an individual row of the next grid of results, returned as a dynamic object. /// Read an individual row of the next grid of results, returned as a dynamic object.
...@@ -57,7 +86,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -57,7 +86,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// Read an individual row of the next grid of results, returned as a dynamic object. /// Read an individual row of the next grid of results, returned as a dynamic object.
/// </summary> /// </summary>
/// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks> /// <remarks>Note: the row can be accessed via "dynamic", or by casting to an IDictionary&lt;string,object&gt;</remarks>
public dynamic ReadSingleOrDefault() => ReadRow<dynamic>(typeof(DapperRow), Row.SingleOrDefault); public dynamic? ReadSingleOrDefault() => ReadRow<dynamic>(typeof(DapperRow), Row.SingleOrDefault);
/// <summary> /// <summary>
/// Read the next grid of results. /// Read the next grid of results.
...@@ -76,7 +105,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -76,7 +105,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// Read an individual row of the next grid of results. /// Read an individual row of the next grid of results.
/// </summary> /// </summary>
/// <typeparam name="T">The type to read.</typeparam> /// <typeparam name="T">The type to read.</typeparam>
public T ReadFirstOrDefault<T>() => ReadRow<T>(typeof(T), Row.FirstOrDefault); public T? ReadFirstOrDefault<T>() => ReadRow<T>(typeof(T), Row.FirstOrDefault);
/// <summary> /// <summary>
/// Read an individual row of the next grid of results. /// Read an individual row of the next grid of results.
...@@ -88,7 +117,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -88,7 +117,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// Read an individual row of the next grid of results. /// Read an individual row of the next grid of results.
/// </summary> /// </summary>
/// <typeparam name="T">The type to read.</typeparam> /// <typeparam name="T">The type to read.</typeparam>
public T ReadSingleOrDefault<T>() => ReadRow<T>(typeof(T), Row.SingleOrDefault); public T? ReadSingleOrDefault<T>() => ReadRow<T>(typeof(T), Row.SingleOrDefault);
/// <summary> /// <summary>
/// Read the next grid of results. /// Read the next grid of results.
...@@ -98,7 +127,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, ...@@ -98,7 +127,7 @@ internal GridReader(IDbCommand command, DbDataReader reader, Identity identity,
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public IEnumerable<object> Read(Type type, bool buffered = true) public IEnumerable<object> Read(Type type, bool buffered = true)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadImpl<object>(type, buffered); return ReadImpl<object>(type, buffered);
} }
...@@ -109,7 +138,7 @@ public IEnumerable<object> Read(Type type, bool buffered = true) ...@@ -109,7 +138,7 @@ public IEnumerable<object> Read(Type type, bool buffered = true)
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public object ReadFirst(Type type) public object ReadFirst(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRow<object>(type, Row.First); return ReadRow<object>(type, Row.First);
} }
...@@ -118,9 +147,9 @@ public object ReadFirst(Type type) ...@@ -118,9 +147,9 @@ public object ReadFirst(Type type)
/// </summary> /// </summary>
/// <param name="type">The type to read.</param> /// <param name="type">The type to read.</param>
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public object ReadFirstOrDefault(Type type) public object? ReadFirstOrDefault(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRow<object>(type, Row.FirstOrDefault); return ReadRow<object>(type, Row.FirstOrDefault);
} }
...@@ -131,7 +160,7 @@ public object ReadFirstOrDefault(Type type) ...@@ -131,7 +160,7 @@ public object ReadFirstOrDefault(Type type)
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public object ReadSingle(Type type) public object ReadSingle(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRow<object>(type, Row.Single); return ReadRow<object>(type, Row.Single);
} }
...@@ -140,46 +169,55 @@ public object ReadSingle(Type type) ...@@ -140,46 +169,55 @@ public object ReadSingle(Type type)
/// </summary> /// </summary>
/// <param name="type">The type to read.</param> /// <param name="type">The type to read.</param>
/// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
public object ReadSingleOrDefault(Type type) public object? ReadSingleOrDefault(Type type)
{ {
if (type == null) throw new ArgumentNullException(nameof(type)); if (type is null) throw new ArgumentNullException(nameof(type));
return ReadRow<object>(type, Row.SingleOrDefault); return ReadRow<object>(type, Row.SingleOrDefault);
} }
private IEnumerable<T> ReadImpl<T>(Type type, bool buffered)
/// <summary>
/// Validates that data is available, returning the <see cref="ResultIndex"/> that corresponds to the current grid - and marks the current grid as consumed;
/// this call <em>must</em> be paired with a call to <see cref="OnAfterGrid(int)"/> or <see cref="OnAfterGridAsync(int)"/>
/// </summary>
protected int OnBeforeGrid()
{ {
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed"); if (reader is null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once"); if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
var typedIdentity = identity.ForGrid(type, gridIndex); _resultIndexAndConsumedFlag |= CONSUMED_FLAG;
return ResultIndex;
}
private IEnumerable<T> ReadImpl<T>(Type type, bool buffered)
{
var index = OnBeforeGrid();
var typedIdentity = Identity.ForGrid(type, index);
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache); CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
var deserializer = cache.Deserializer; var deserializer = cache.Deserializer;
int hash = GetColumnHash(reader); int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash) if (deserializer.Func is null || deserializer.Hash != hash)
{ {
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false)); deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
cache.Deserializer = deserializer; cache.Deserializer = deserializer;
} }
IsConsumed = true; var result = ReadDeferred<T>(index, deserializer.Func, type);
var result = ReadDeferred<T>(gridIndex, deserializer.Func, type); return buffered ? result.ToList() : result;
return buffered ? result?.ToList() : result;
} }
private T ReadRow<T>(Type type, Row row) private T ReadRow<T>(Type type, Row row)
{ {
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed"); var index = OnBeforeGrid();
if (IsConsumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
IsConsumed = true;
T result = default; T result = default!;
if (reader.Read() && reader.FieldCount != 0) if (reader.Read() && reader.FieldCount != 0)
{ {
var typedIdentity = identity.ForGrid(type, gridIndex); var typedIdentity = Identity.ForGrid(type, index);
CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache); CacheInfo cache = GetCacheInfo(typedIdentity, null, addToCache);
var deserializer = cache.Deserializer; var deserializer = cache.Deserializer;
int hash = GetColumnHash(reader); int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash) if (deserializer.Func is null || deserializer.Hash != hash)
{ {
deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false)); deserializer = new DeserializerState(hash, GetDeserializer(type, reader, 0, -1, false));
cache.Deserializer = deserializer; cache.Deserializer = deserializer;
...@@ -194,15 +232,14 @@ private T ReadRow<T>(Type type, Row row) ...@@ -194,15 +232,14 @@ private T ReadRow<T>(Type type, Row row)
{ {
ThrowZeroRows(row); ThrowZeroRows(row);
} }
NextResult(); OnAfterGrid(index);
return result; return result;
} }
private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Delegate func, string splitOn) private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Delegate func, string splitOn)
{ {
var identity = this.identity.ForGrid<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(typeof(TReturn), gridIndex); var index = OnBeforeGrid();
var identity = Identity.ForGrid<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(typeof(TReturn), index);
IsConsumed = true;
try try
{ {
...@@ -213,13 +250,14 @@ private T ReadRow<T>(Type type, Row row) ...@@ -213,13 +250,14 @@ private T ReadRow<T>(Type type, Row row)
} }
finally finally
{ {
NextResult(); OnAfterGrid(index);
} }
} }
private IEnumerable<TReturn> MultiReadInternal<TReturn>(Type[] types, Func<object[], TReturn> map, string splitOn) private IEnumerable<TReturn> MultiReadInternal<TReturn>(Type[] types, Func<object[], TReturn> map, string splitOn)
{ {
var identity = this.identity.ForGrid(typeof(TReturn), types, gridIndex); var index = OnBeforeGrid();
var identity = Identity.ForGrid(typeof(TReturn), types, index);
try try
{ {
foreach (var r in MultiMapImpl<TReturn>(null, default, types, map, splitOn, reader, identity, false)) foreach (var r in MultiMapImpl<TReturn>(null, default, types, map, splitOn, reader, identity, false))
...@@ -229,7 +267,7 @@ private IEnumerable<TReturn> MultiReadInternal<TReturn>(Type[] types, Func<objec ...@@ -229,7 +267,7 @@ private IEnumerable<TReturn> MultiReadInternal<TReturn>(Type[] types, Func<objec
} }
finally finally
{ {
NextResult(); OnAfterGrid(index);
} }
} }
...@@ -356,48 +394,71 @@ private IEnumerable<T> ReadDeferred<T>(int index, Func<DbDataReader, object> des ...@@ -356,48 +394,71 @@ private IEnumerable<T> ReadDeferred<T>(int index, Func<DbDataReader, object> des
{ {
try try
{ {
while (index == gridIndex && reader.Read()) while (index == ResultIndex && reader?.Read() == true)
{ {
yield return ConvertTo<T>(deserializer(reader)); yield return ConvertTo<T>(deserializer(reader));
} }
} }
finally // finally so that First etc progresses things even when multiple rows finally // finally so that First etc progresses things even when multiple rows
{ {
if (index == gridIndex) OnAfterGrid(index);
{
NextResult();
}
} }
} }
private int gridIndex; //, readCount; const int CONSUMED_FLAG = 1 << 31;
private readonly IParameterCallbacks callbacks; private int _resultIndexAndConsumedFlag; //, readCount;
/// <summary>
/// Indicates the current result index
/// </summary>
protected int ResultIndex => _resultIndexAndConsumedFlag & ~CONSUMED_FLAG;
/// <summary> /// <summary>
/// Has the underlying reader been consumed? /// Has the underlying reader been consumed?
/// </summary> /// </summary>
public bool IsConsumed { get; private set; } /// <remarks>This also reports <c>true</c> if the current grid is actively being consumed</remarks>
public bool IsConsumed => (_resultIndexAndConsumedFlag & CONSUMED_FLAG) != 0;
/// <summary> /// <summary>
/// The command associated with the reader /// The command associated with the reader
/// </summary> /// </summary>
public IDbCommand Command { get; set; } public IDbCommand Command { get; set; }
private void NextResult() /// <summary>
/// The underlying reader
/// </summary>
protected DbDataReader Reader => reader;
/// <summary>
/// The cancellation token associated with this reader
/// </summary>
protected CancellationToken CancellationToken => cancel;
/// <summary>
/// Marks the current grid as consumed, and moves to the next result
/// </summary>
protected void OnAfterGrid(int index)
{ {
if (reader.NextResult()) if (index != ResultIndex)
{
// not our data!
}
else if (reader is null)
{
// nothing to do
}
else if (reader.NextResult())
{ {
// readCount++; // readCount++;
gridIndex++; _resultIndexAndConsumedFlag = index + 1;
IsConsumed = false;
} }
else else
{ {
// happy path; close the reader cleanly - no // happy path; close the reader cleanly - no
// need for "Cancel" etc // need for "Cancel" etc
reader.Dispose(); reader.Dispose();
reader = null; reader = null!;
callbacks?.OnCompleted(); onCompleted?.Invoke(state);
Dispose(); Dispose();
} }
} }
...@@ -407,25 +468,25 @@ private void NextResult() ...@@ -407,25 +468,25 @@ private void NextResult()
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
if (reader != null) if (reader is not null)
{ {
if (!reader.IsClosed) Command?.Cancel(); if (!reader.IsClosed) Command?.Cancel();
reader.Dispose(); reader.Dispose();
reader = null; reader = null!;
} }
if (Command != null) if (Command is not null)
{ {
Command.Dispose(); Command.Dispose();
Command = null; Command = null!;
} }
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T ConvertTo<T>(object value) => value switch private static T ConvertTo<T>(object? value) => value switch
{ {
T typed => typed, T typed => typed,
null or DBNull => default, null or DBNull => default!,
_ => (T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T), CultureInfo.InvariantCulture), _ => (T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T), CultureInfo.InvariantCulture),
}; };
} }
......
...@@ -23,9 +23,9 @@ public static IEnumerable<T> Parse<T>(this IDataReader reader) ...@@ -23,9 +23,9 @@ public static IEnumerable<T> Parse<T>(this IDataReader reader)
do do
{ {
object val = deser(dbReader); object val = deser(dbReader);
if (val == null || val is T) if (val is null || val is T)
{ {
yield return (T)val; yield return (T)val!;
} }
else else
{ {
...@@ -83,6 +83,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader) ...@@ -83,6 +83,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
#if DEBUG // make sure we're not using this internally #if DEBUG // make sure we're not using this internally
[Obsolete(nameof(DbDataReader) + " API should be preferred")] [Obsolete(nameof(DbDataReader) + " API should be preferred")]
#endif #endif
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
public static Func<IDataReader, object> GetRowParser(this IDataReader reader, Type type, public static Func<IDataReader, object> GetRowParser(this IDataReader reader, Type type,
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false) int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
{ {
...@@ -109,7 +110,8 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader) ...@@ -109,7 +110,8 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
#if DEBUG // make sure we're not using this internally #if DEBUG // make sure we're not using this internally
[Obsolete(nameof(DbDataReader) + " API should be preferred")] [Obsolete(nameof(DbDataReader) + " API should be preferred")]
#endif #endif
public static Func<IDataReader, T> GetRowParser<T>(this IDataReader reader, Type concreteType = null, [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
public static Func<IDataReader, T> GetRowParser<T>(this IDataReader reader, Type? concreteType = null,
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false) int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
{ {
concreteType ??= typeof(T); concreteType ??= typeof(T);
...@@ -174,7 +176,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader) ...@@ -174,7 +176,7 @@ public static IEnumerable<dynamic> Parse(this IDataReader reader)
/// public override int Type =&gt; 2; /// public override int Type =&gt; 2;
/// } /// }
/// </example> /// </example>
public static Func<DbDataReader, T> GetRowParser<T>(this DbDataReader reader, Type concreteType = null, public static Func<DbDataReader, T> GetRowParser<T>(this DbDataReader reader, Type? concreteType = null,
int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false) int startIndex = 0, int length = -1, bool returnNullIfFirstMissing = false)
{ {
concreteType ??= typeof(T); concreteType ??= typeof(T);
......
...@@ -23,17 +23,17 @@ public interface IMemberMap ...@@ -23,17 +23,17 @@ public interface IMemberMap
/// <summary> /// <summary>
/// Target property /// Target property
/// </summary> /// </summary>
PropertyInfo Property { get; } PropertyInfo? Property { get; }
/// <summary> /// <summary>
/// Target field /// Target field
/// </summary> /// </summary>
FieldInfo Field { get; } FieldInfo? Field { get; }
/// <summary> /// <summary>
/// Target constructor parameter /// Target constructor parameter
/// </summary> /// </summary>
ParameterInfo Parameter { get; } ParameterInfo? Parameter { get; }
} }
} }
} }
...@@ -11,7 +11,7 @@ public interface IParameterLookup : IDynamicParameters ...@@ -11,7 +11,7 @@ public interface IParameterLookup : IDynamicParameters
/// Get the value of the specified parameter (return null if not found) /// Get the value of the specified parameter (return null if not found)
/// </summary> /// </summary>
/// <param name="name">The name of the parameter to get.</param> /// <param name="name">The name of the parameter to get.</param>
object this[string name] { get; } object? this[string name] { get; }
} }
} }
} }
...@@ -23,7 +23,7 @@ public interface ITypeHandler ...@@ -23,7 +23,7 @@ public interface ITypeHandler
/// <param name="value">The value from the database</param> /// <param name="value">The value from the database</param>
/// <param name="destinationType">The type to parse to</param> /// <param name="destinationType">The type to parse to</param>
/// <returns>The typed value</returns> /// <returns>The typed value</returns>
object Parse(Type destinationType, object value); object? Parse(Type destinationType, object value);
} }
} }
} }
...@@ -16,7 +16,7 @@ public interface ITypeMap ...@@ -16,7 +16,7 @@ public interface ITypeMap
/// <param name="names">DataReader column names</param> /// <param name="names">DataReader column names</param>
/// <param name="types">DataReader column types</param> /// <param name="types">DataReader column types</param>
/// <returns>Matching constructor or default one</returns> /// <returns>Matching constructor or default one</returns>
ConstructorInfo FindConstructor(string[] names, Type[] types); ConstructorInfo? FindConstructor(string[] names, Type[] types);
/// <summary> /// <summary>
/// Returns a constructor which should *always* be used. /// Returns a constructor which should *always* be used.
...@@ -25,7 +25,7 @@ public interface ITypeMap ...@@ -25,7 +25,7 @@ public interface ITypeMap
/// ///
/// Use this class to force object creation away from parameterless constructors you don't control. /// Use this class to force object creation away from parameterless constructors you don't control.
/// </summary> /// </summary>
ConstructorInfo FindExplicitConstructor(); ConstructorInfo? FindExplicitConstructor();
/// <summary> /// <summary>
/// Gets mapping for constructor parameter /// Gets mapping for constructor parameter
...@@ -40,7 +40,7 @@ public interface ITypeMap ...@@ -40,7 +40,7 @@ public interface ITypeMap
/// </summary> /// </summary>
/// <param name="columnName">DataReader column name</param> /// <param name="columnName">DataReader column name</param>
/// <returns>Mapping implementation</returns> /// <returns>Mapping implementation</returns>
IMemberMap GetMember(string columnName); IMemberMap? GetMember(string columnName);
} }
} }
} }
...@@ -11,10 +11,10 @@ internal sealed class Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, ...@@ -11,10 +11,10 @@ internal sealed class Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth,
private static readonly int s_typeHash; private static readonly int s_typeHash;
private static readonly int s_typeCount = CountNonTrivial(out s_typeHash); private static readonly int s_typeCount = CountNonTrivial(out s_typeHash);
internal Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, int gridIndex = 0) internal Identity(string sql, CommandType? commandType, string connectionString, Type type, Type? parametersType, int gridIndex = 0)
: base(sql, commandType, connectionString, type, parametersType, s_typeHash, gridIndex) : base(sql, commandType, connectionString, type, parametersType, s_typeHash, gridIndex)
{} {}
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, int gridIndex = 0) internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type? parametersType, int gridIndex = 0)
: base(sql, commandType, connection.ConnectionString, type, parametersType, s_typeHash, gridIndex) : base(sql, commandType, connection.ConnectionString, type, parametersType, s_typeHash, gridIndex)
{ } { }
...@@ -54,12 +54,12 @@ internal sealed class IdentityWithTypes : Identity ...@@ -54,12 +54,12 @@ internal sealed class IdentityWithTypes : Identity
{ {
private readonly Type[] _types; private readonly Type[] _types;
internal IdentityWithTypes(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, Type[] otherTypes, int gridIndex = 0) internal IdentityWithTypes(string sql, CommandType? commandType, string connectionString, Type type, Type? parametersType, Type[] otherTypes, int gridIndex = 0)
: base(sql, commandType, connectionString, type, parametersType, HashTypes(otherTypes), gridIndex) : base(sql, commandType, connectionString, type, parametersType, HashTypes(otherTypes), gridIndex)
{ {
_types = otherTypes ?? Type.EmptyTypes; _types = otherTypes ?? Type.EmptyTypes;
} }
internal IdentityWithTypes(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes, int gridIndex = 0) internal IdentityWithTypes(string sql, CommandType? commandType, IDbConnection connection, Type type, Type? parametersType, Type[] otherTypes, int gridIndex = 0)
: base(sql, commandType, connection.ConnectionString, type, parametersType, HashTypes(otherTypes), gridIndex) : base(sql, commandType, connection.ConnectionString, type, parametersType, HashTypes(otherTypes), gridIndex)
{ {
_types = otherTypes ?? Type.EmptyTypes; _types = otherTypes ?? Type.EmptyTypes;
...@@ -72,7 +72,7 @@ internal IdentityWithTypes(string sql, CommandType? commandType, IDbConnection c ...@@ -72,7 +72,7 @@ internal IdentityWithTypes(string sql, CommandType? commandType, IDbConnection c
static int HashTypes(Type[] types) static int HashTypes(Type[] types)
{ {
var hashCode = 0; var hashCode = 0;
if (types != null) if (types is not null)
{ {
foreach (var t in types) foreach (var t in types)
{ {
...@@ -99,7 +99,7 @@ public class Identity : IEquatable<Identity> ...@@ -99,7 +99,7 @@ public class Identity : IEquatable<Identity>
new Identity(sql, commandType, connectionString, primaryType, parametersType, 0, gridIndex); new Identity(sql, commandType, connectionString, primaryType, parametersType, 0, gridIndex);
internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex) => internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex) =>
(otherTypes == null || otherTypes.Length == 0) (otherTypes is null || otherTypes.Length == 0)
? new Identity(sql, commandType, connectionString, primaryType, parametersType, 0, gridIndex) ? new Identity(sql, commandType, connectionString, primaryType, parametersType, 0, gridIndex)
: new IdentityWithTypes(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex); : new IdentityWithTypes(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);
...@@ -111,10 +111,10 @@ public class Identity : IEquatable<Identity> ...@@ -111,10 +111,10 @@ public class Identity : IEquatable<Identity>
public Identity ForDynamicParameters(Type type) => public Identity ForDynamicParameters(Type type) =>
new Identity(sql, commandType, connectionString, this.type, type, 0, -1); new Identity(sql, commandType, connectionString, this.type, type, 0, -1);
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType) internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type? type, Type? parametersType)
: this(sql, commandType, connection.ConnectionString, type, parametersType, 0, 0) { /* base call */ } : this(sql, commandType, connection.ConnectionString, type, parametersType, 0, 0) { /* base call */ }
private protected Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, int otherTypesHash, int gridIndex) private protected Identity(string sql, CommandType? commandType, string connectionString, Type? type, Type? parametersType, int otherTypesHash, int gridIndex)
{ {
this.sql = sql; this.sql = sql;
this.commandType = commandType; this.commandType = commandType;
...@@ -130,7 +130,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti ...@@ -130,7 +130,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti
hashCode = (hashCode * 23) + (sql?.GetHashCode() ?? 0); hashCode = (hashCode * 23) + (sql?.GetHashCode() ?? 0);
hashCode = (hashCode * 23) + (type?.GetHashCode() ?? 0); hashCode = (hashCode * 23) + (type?.GetHashCode() ?? 0);
hashCode = (hashCode * 23) + otherTypesHash; hashCode = (hashCode * 23) + otherTypesHash;
hashCode = (hashCode * 23) + (connectionString == null ? 0 : connectionStringComparer.GetHashCode(connectionString)); hashCode = (hashCode * 23) + (connectionString is null ? 0 : connectionStringComparer.GetHashCode(connectionString));
hashCode = (hashCode * 23) + (parametersType?.GetHashCode() ?? 0); hashCode = (hashCode * 23) + (parametersType?.GetHashCode() ?? 0);
} }
} }
...@@ -139,7 +139,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti ...@@ -139,7 +139,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti
/// Whether this <see cref="Identity"/> equals another. /// Whether this <see cref="Identity"/> equals another.
/// </summary> /// </summary>
/// <param name="obj">The other <see cref="object"/> to compare to.</param> /// <param name="obj">The other <see cref="object"/> to compare to.</param>
public override bool Equals(object obj) => Equals(obj as Identity); public override bool Equals(object? obj) => Equals(obj as Identity);
/// <summary> /// <summary>
/// The raw SQL command. /// The raw SQL command.
...@@ -164,7 +164,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti ...@@ -164,7 +164,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti
/// <summary> /// <summary>
/// This <see cref="Type"/> of this Identity. /// This <see cref="Type"/> of this Identity.
/// </summary> /// </summary>
public readonly Type type; public readonly Type? type;
/// <summary> /// <summary>
/// The connection string for this Identity. /// The connection string for this Identity.
...@@ -174,7 +174,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti ...@@ -174,7 +174,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti
/// <summary> /// <summary>
/// The type of the parameters object for this Identity. /// The type of the parameters object for this Identity.
/// </summary> /// </summary>
public readonly Type parametersType; public readonly Type? parametersType;
/// <summary> /// <summary>
/// Gets the hash code for this identity. /// Gets the hash code for this identity.
...@@ -192,7 +192,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti ...@@ -192,7 +192,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti
/// </summary> /// </summary>
/// <param name="other">The other <see cref="Identity"/> object to compare.</param> /// <param name="other">The other <see cref="Identity"/> object to compare.</param>
/// <returns>Whether the two are equal</returns> /// <returns>Whether the two are equal</returns>
public bool Equals(Identity other) public bool Equals(Identity? other)
{ {
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;
if (other is null) return false; if (other is null) return false;
......
using System.Threading; using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace Dapper namespace Dapper
{ {
...@@ -13,13 +14,14 @@ public static partial class SqlMapper ...@@ -13,13 +14,14 @@ public static partial class SqlMapper
/// <typeparam name="TValue">The value type of the cache.</typeparam> /// <typeparam name="TValue">The value type of the cache.</typeparam>
internal class Link<TKey, TValue> where TKey : class internal class Link<TKey, TValue> where TKey : class
{ {
public static bool TryGet(Link<TKey, TValue> link, TKey key, out TValue value) public static void Clear(ref Link<TKey, TValue>? head) => Interlocked.Exchange(ref head, null);
public static bool TryGet(Link<TKey, TValue>? link, TKey key, [NotNullWhen(true)] out TValue? value)
{ {
while (link != null) while (link is not null)
{ {
if ((object)key == (object)link.Key) if ((object)key == (object)link.Key)
{ {
value = link.Value; value = link.Value!;
return true; return true;
} }
link = link.Tail; link = link.Tail;
...@@ -28,13 +30,13 @@ public static bool TryGet(Link<TKey, TValue> link, TKey key, out TValue value) ...@@ -28,13 +30,13 @@ public static bool TryGet(Link<TKey, TValue> link, TKey key, out TValue value)
return false; return false;
} }
public static bool TryAdd(ref Link<TKey, TValue> head, TKey key, ref TValue value) public static bool TryAdd(ref Link<TKey, TValue>? head, TKey key, ref TValue value)
{ {
bool tryAgain; bool tryAgain;
do do
{ {
var snapshot = Interlocked.CompareExchange(ref head, null, null); var snapshot = Interlocked.CompareExchange(ref head, null, null);
if (TryGet(snapshot, key, out TValue found)) if (TryGet(snapshot, key, out TValue? found))
{ // existing match; report the existing value instead { // existing match; report the existing value instead
value = found; value = found;
return false; return false;
...@@ -46,7 +48,7 @@ public static bool TryAdd(ref Link<TKey, TValue> head, TKey key, ref TValue valu ...@@ -46,7 +48,7 @@ public static bool TryAdd(ref Link<TKey, TValue> head, TKey key, ref TValue valu
return true; return true;
} }
private Link(TKey key, TValue value, Link<TKey, TValue> tail) private Link(TKey key, TValue value, Link<TKey, TValue>? tail)
{ {
Key = key; Key = key;
Value = value; Value = value;
...@@ -55,7 +57,7 @@ private Link(TKey key, TValue value, Link<TKey, TValue> tail) ...@@ -55,7 +57,7 @@ private Link(TKey key, TValue value, Link<TKey, TValue> tail)
public TKey Key { get; } public TKey Key { get; }
public TValue Value { get; } public TValue Value { get; }
public Link<TKey, TValue> Tail { get; } public Link<TKey, TValue>? Tail { get; }
} }
} }
} }
using System; using System;
using System.Data; using System.Data;
using System.Threading;
namespace Dapper namespace Dapper
{ {
...@@ -63,7 +64,9 @@ static Settings() ...@@ -63,7 +64,9 @@ static Settings()
public static void SetDefaults() public static void SetDefaults()
{ {
CommandTimeout = null; CommandTimeout = null;
ApplyNullValues = false; ApplyNullValues = PadListExpansions = UseIncrementalPseudoPositionalParameterNames = false;
AllowedCommandBehaviors = DefaultAllowedCommandBehaviors;
FetchSize = InListStringSplitCount = -1;
} }
/// <summary> /// <summary>
...@@ -99,6 +102,26 @@ public static void SetDefaults() ...@@ -99,6 +102,26 @@ public static void SetDefaults()
/// instead of the original name; for most scenarios, this is ignored since the name is redundant, but "snowflake" requires this. /// instead of the original name; for most scenarios, this is ignored since the name is redundant, but "snowflake" requires this.
/// </summary> /// </summary>
public static bool UseIncrementalPseudoPositionalParameterNames { get; set; } public static bool UseIncrementalPseudoPositionalParameterNames { get; set; }
/// <summary>
/// If assigned a non-negative value, then that value is applied to any commands <c>FetchSize</c> property, if it exists;
/// see https://docs.oracle.com/en/database/oracle/oracle-database/18/odpnt/CommandFetchSize.html; note that this value
/// can only be set globally - it is not intended for frequent/contextual changing.
/// </summary>
public static long FetchSize
{
get => Volatile.Read(ref s_FetchSize);
set
{
if (Volatile.Read(ref s_FetchSize) != value)
{
Volatile.Write(ref s_FetchSize, value);
CommandDefinition.ResetCommandInitCache(); // if this setting is useful: we've invalidated things
}
}
}
private static long s_FetchSize = -1;
} }
} }
} }
...@@ -35,13 +35,13 @@ internal static void Purge() ...@@ -35,13 +35,13 @@ internal static void Purge()
internal static Func<DbDataReader, object> GetReader(Type type, DbDataReader reader, int startBound, int length, bool returnNullIfFirstMissing) internal static Func<DbDataReader, object> GetReader(Type type, DbDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
{ {
var found = (TypeDeserializerCache)byType[type]; var found = (TypeDeserializerCache?)byType[type];
if (found == null) if (found is null)
{ {
lock (byType) lock (byType)
{ {
found = (TypeDeserializerCache)byType[type]; found = (TypeDeserializerCache?)byType[type];
if (found == null) if (found is null)
{ {
byType[type] = found = new TypeDeserializerCache(type); byType[type] = found = new TypeDeserializerCache(type);
} }
...@@ -56,9 +56,9 @@ internal static void Purge() ...@@ -56,9 +56,9 @@ internal static void Purge()
{ {
private readonly int startBound, length; private readonly int startBound, length;
private readonly bool returnNullIfFirstMissing; private readonly bool returnNullIfFirstMissing;
private readonly DbDataReader reader; private readonly DbDataReader? reader;
private readonly string[] names; private readonly string[]? names;
private readonly Type[] types; private readonly Type[]? types;
private readonly int hashCode; private readonly int hashCode;
public DeserializerKey(int hashCode, int startBound, int length, bool returnNullIfFirstMissing, DbDataReader reader, bool copyDown) public DeserializerKey(int hashCode, int startBound, int length, bool returnNullIfFirstMissing, DbDataReader reader, bool copyDown)
...@@ -92,11 +92,11 @@ public DeserializerKey(int hashCode, int startBound, int length, bool returnNull ...@@ -92,11 +92,11 @@ public DeserializerKey(int hashCode, int startBound, int length, bool returnNull
public override string ToString() public override string ToString()
{ // only used in the debugger { // only used in the debugger
if (names != null) if (names is not null)
{ {
return string.Join(", ", names); return string.Join(", ", names);
} }
if (reader != null) if (reader is not null)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
int index = startBound; int index = startBound;
...@@ -107,10 +107,10 @@ public override string ToString() ...@@ -107,10 +107,10 @@ public override string ToString()
} }
return sb.ToString(); return sb.ToString();
} }
return base.ToString(); return base.ToString() ?? "";
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
=> obj is DeserializerKey key && Equals(key); => obj is DeserializerKey key && Equals(key);
public bool Equals(DeserializerKey other) public bool Equals(DeserializerKey other)
...@@ -143,10 +143,10 @@ public bool Equals(DeserializerKey other) ...@@ -143,10 +143,10 @@ public bool Equals(DeserializerKey other)
if (returnNullIfFirstMissing) hash *= -27; if (returnNullIfFirstMissing) hash *= -27;
// get a cheap key first: false means don't copy the values down // get a cheap key first: false means don't copy the values down
var key = new DeserializerKey(hash, startBound, length, returnNullIfFirstMissing, reader, false); var key = new DeserializerKey(hash, startBound, length, returnNullIfFirstMissing, reader, false);
Func<DbDataReader, object> deser; Func<DbDataReader, object>? deser;
lock (readers) lock (readers)
{ {
if (readers.TryGetValue(key, out deser)) return deser; if (readers.TryGetValue(key, out deser)) return deser!;
} }
deser = GetTypeDeserializerImpl(type, reader, startBound, length, returnNullIfFirstMissing); deser = GetTypeDeserializerImpl(type, reader, startBound, length, returnNullIfFirstMissing);
// get a more expensive key: true means copy the values down so it can be used as a key later // get a more expensive key: true means copy the values down so it can be used as a key later
......
...@@ -16,14 +16,14 @@ public abstract class TypeHandler<T> : ITypeHandler ...@@ -16,14 +16,14 @@ public abstract class TypeHandler<T> : ITypeHandler
/// </summary> /// </summary>
/// <param name="parameter">The parameter to configure</param> /// <param name="parameter">The parameter to configure</param>
/// <param name="value">Parameter value</param> /// <param name="value">Parameter value</param>
public abstract void SetValue(IDbDataParameter parameter, T value); public abstract void SetValue(IDbDataParameter parameter, T? value);
/// <summary> /// <summary>
/// Parse a database value back to a typed value /// Parse a database value back to a typed value
/// </summary> /// </summary>
/// <param name="value">The value from the database</param> /// <param name="value">The value from the database</param>
/// <returns>The typed value</returns> /// <returns>The typed value</returns>
public abstract T Parse(object value); public abstract T? Parse(object value);
void ITypeHandler.SetValue(IDbDataParameter parameter, object value) void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
{ {
...@@ -33,11 +33,11 @@ void ITypeHandler.SetValue(IDbDataParameter parameter, object value) ...@@ -33,11 +33,11 @@ void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
} }
else else
{ {
SetValue(parameter, (T)value); SetValue(parameter, (T?)value);
} }
} }
object ITypeHandler.Parse(Type destinationType, object value) object? ITypeHandler.Parse(Type destinationType, object value)
{ {
return Parse(value); return Parse(value);
} }
...@@ -66,9 +66,9 @@ public abstract class StringTypeHandler<T> : TypeHandler<T> ...@@ -66,9 +66,9 @@ public abstract class StringTypeHandler<T> : TypeHandler<T>
/// </summary> /// </summary>
/// <param name="parameter">The parameter to configure</param> /// <param name="parameter">The parameter to configure</param>
/// <param name="value">Parameter value</param> /// <param name="value">Parameter value</param>
public override void SetValue(IDbDataParameter parameter, T value) public override void SetValue(IDbDataParameter parameter, T? value)
{ {
parameter.Value = value == null ? (object)DBNull.Value : Format(value); parameter.Value = value is null ? (object)DBNull.Value : Format(value);
} }
/// <summary> /// <summary>
...@@ -78,7 +78,7 @@ public override void SetValue(IDbDataParameter parameter, T value) ...@@ -78,7 +78,7 @@ public override void SetValue(IDbDataParameter parameter, T value)
/// <returns>The typed value</returns> /// <returns>The typed value</returns>
public override T Parse(object value) public override T Parse(object value)
{ {
if (value == null || value is DBNull) return default; if (value is null || value is DBNull) return default!;
return Parse((string)value); return Parse((string)value);
} }
} }
......
...@@ -20,7 +20,7 @@ public static class TypeHandlerCache<T> ...@@ -20,7 +20,7 @@ public static class TypeHandlerCache<T>
/// </summary> /// </summary>
/// <param name="value">The object to parse.</param> /// <param name="value">The object to parse.</param>
[Obsolete(ObsoleteInternalUsageOnly, true)] [Obsolete(ObsoleteInternalUsageOnly, true)]
public static T Parse(object value) => (T)handler.Parse(typeof(T), value); public static T? Parse(object value) => (T?)handler.Parse(typeof(T), value);
/// <summary> /// <summary>
/// Not intended for direct usage. /// Not intended for direct usage.
...@@ -32,12 +32,10 @@ public static class TypeHandlerCache<T> ...@@ -32,12 +32,10 @@ public static class TypeHandlerCache<T>
internal static void SetHandler(ITypeHandler handler) internal static void SetHandler(ITypeHandler handler)
{ {
#pragma warning disable 618
TypeHandlerCache<T>.handler = handler; TypeHandlerCache<T>.handler = handler;
#pragma warning restore 618
} }
private static ITypeHandler handler; private static ITypeHandler handler = null!;
} }
} }
} }
此差异已折叠。
...@@ -8,7 +8,7 @@ namespace Dapper ...@@ -8,7 +8,7 @@ namespace Dapper
internal sealed class TableValuedParameter : SqlMapper.ICustomQueryParameter internal sealed class TableValuedParameter : SqlMapper.ICustomQueryParameter
{ {
private readonly DataTable table; private readonly DataTable table;
private readonly string typeName; private readonly string? typeName;
/// <summary> /// <summary>
/// Create a new instance of <see cref="TableValuedParameter"/>. /// Create a new instance of <see cref="TableValuedParameter"/>.
...@@ -21,7 +21,7 @@ internal sealed class TableValuedParameter : SqlMapper.ICustomQueryParameter ...@@ -21,7 +21,7 @@ internal sealed class TableValuedParameter : SqlMapper.ICustomQueryParameter
/// </summary> /// </summary>
/// <param name="table">The <see cref="DataTable"/> to create this parameter for.</param> /// <param name="table">The <see cref="DataTable"/> to create this parameter for.</param>
/// <param name="typeName">The name of the type this parameter is for.</param> /// <param name="typeName">The name of the type this parameter is for.</param>
public TableValuedParameter(DataTable table, string typeName) public TableValuedParameter(DataTable table, string? typeName)
{ {
this.table = table; this.table = table;
this.typeName = typeName; this.typeName = typeName;
...@@ -35,12 +35,12 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam ...@@ -35,12 +35,12 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
command.Parameters.Add(param); command.Parameters.Add(param);
} }
internal static void Set(IDbDataParameter parameter, DataTable table, string typeName) internal static void Set(IDbDataParameter parameter, DataTable? table, string? typeName)
{ {
#pragma warning disable 0618 #pragma warning disable 0618
parameter.Value = SqlMapper.SanitizeParameterValue(table); parameter.Value = SqlMapper.SanitizeParameterValue(table);
#pragma warning restore 0618 #pragma warning restore 0618
if (string.IsNullOrEmpty(typeName) && table != null) if (string.IsNullOrEmpty(typeName) && table is not null)
{ {
typeName = table.GetTypeName(); typeName = table.GetTypeName();
} }
......
...@@ -5,7 +5,7 @@ namespace Dapper ...@@ -5,7 +5,7 @@ namespace Dapper
{ {
internal static class TypeExtensions internal static class TypeExtensions
{ {
public static MethodInfo GetPublicInstanceMethod(this Type type, string name, Type[] types) public static MethodInfo? GetPublicInstanceMethod(this Type type, string name, Type[] types)
=> type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public, null, types, null); => type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public, null, types, null);
} }
} }
...@@ -22,7 +22,7 @@ public UdtTypeHandler(string udtTypeName) ...@@ -22,7 +22,7 @@ public UdtTypeHandler(string udtTypeName)
this.udtTypeName = udtTypeName; this.udtTypeName = udtTypeName;
} }
object ITypeHandler.Parse(Type destinationType, object value) object? ITypeHandler.Parse(Type destinationType, object value)
{ {
return value is DBNull ? null : value; return value is DBNull ? null : value;
} }
......
...@@ -34,18 +34,18 @@ private async static Task<T> ThrowDisposedAsync<T>() ...@@ -34,18 +34,18 @@ private async static Task<T> ThrowDisposedAsync<T>()
public override void Close() { } public override void Close() { }
public override DataTable GetSchemaTable() => ThrowDisposed<DataTable>(); public override DataTable GetSchemaTable() => ThrowDisposed<DataTable>();
#if PLAT_NO_REMOTING #if NET5_0_OR_GREATER
[Obsolete("This Remoting API is not supported and throws PlatformNotSupportedException.", DiagnosticId = "SYSLIB0010", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [Obsolete("This Remoting API is not supported and throws PlatformNotSupportedException.", DiagnosticId = "SYSLIB0010", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
#endif #endif
public override object InitializeLifetimeService() => ThrowDisposed<object>(); public override object InitializeLifetimeService() => ThrowDisposed<object>();
protected override void Dispose(bool disposing) { } protected override void Dispose(bool disposing) { }
public override bool GetBoolean(int ordinal) => ThrowDisposed<bool>(); public override bool GetBoolean(int ordinal) => ThrowDisposed<bool>();
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) => ThrowDisposed<long>(); public override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length) => ThrowDisposed<long>();
public override float GetFloat(int ordinal) => ThrowDisposed<float>(); public override float GetFloat(int ordinal) => ThrowDisposed<float>();
public override short GetInt16(int ordinal) => ThrowDisposed<short>(); public override short GetInt16(int ordinal) => ThrowDisposed<short>();
public override byte GetByte(int ordinal) => ThrowDisposed<byte>(); public override byte GetByte(int ordinal) => ThrowDisposed<byte>();
public override char GetChar(int ordinal) => ThrowDisposed<char>(); public override char GetChar(int ordinal) => ThrowDisposed<char>();
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) => ThrowDisposed<long>(); public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length) => ThrowDisposed<long>();
public override string GetDataTypeName(int ordinal) => ThrowDisposed<string>(); public override string GetDataTypeName(int ordinal) => ThrowDisposed<string>();
public override DateTime GetDateTime(int ordinal) => ThrowDisposed<DateTime>(); public override DateTime GetDateTime(int ordinal) => ThrowDisposed<DateTime>();
protected override DbDataReader GetDbDataReader(int ordinal) => ThrowDisposed<DbDataReader>(); protected override DbDataReader GetDbDataReader(int ordinal) => ThrowDisposed<DbDataReader>();
...@@ -83,13 +83,13 @@ internal sealed class DbWrappedReader : DbDataReader, IWrappedDataReader ...@@ -83,13 +83,13 @@ internal sealed class DbWrappedReader : DbDataReader, IWrappedDataReader
// the purpose of wrapping here is to allow closing a reader to *also* close // the purpose of wrapping here is to allow closing a reader to *also* close
// the command, without having to explicitly hand the command back to the // the command, without having to explicitly hand the command back to the
// caller // caller
public static DbDataReader Create(IDbCommand cmd, DbDataReader reader) public static DbDataReader Create(IDbCommand? cmd, DbDataReader reader)
{ {
if (cmd == null) return reader; // no need to wrap if no command if (cmd is null) return reader; // no need to wrap if no command
if (reader != null) return new DbWrappedReader(cmd, reader); if (reader is not null) return new DbWrappedReader(cmd, reader);
cmd.Dispose(); cmd.Dispose();
return null; // GIGO return null!; // GIGO
} }
private DbDataReader _reader; private DbDataReader _reader;
...@@ -108,9 +108,9 @@ public DbWrappedReader(IDbCommand cmd, DbDataReader reader) ...@@ -108,9 +108,9 @@ public DbWrappedReader(IDbCommand cmd, DbDataReader reader)
public override bool HasRows => _reader.HasRows; public override bool HasRows => _reader.HasRows;
public override void Close() => _reader.Close(); public override void Close() => _reader.Close();
public override DataTable GetSchemaTable() => _reader.GetSchemaTable(); public override DataTable? GetSchemaTable() => _reader.GetSchemaTable();
#if PLAT_NO_REMOTING #if NET5_0_OR_GREATER
[Obsolete("This Remoting API is not supported and throws PlatformNotSupportedException.", DiagnosticId = "SYSLIB0010", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [Obsolete("This Remoting API is not supported and throws PlatformNotSupportedException.", DiagnosticId = "SYSLIB0010", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
#endif #endif
public override object InitializeLifetimeService() => _reader.InitializeLifetimeService(); public override object InitializeLifetimeService() => _reader.InitializeLifetimeService();
...@@ -133,7 +133,7 @@ protected override void Dispose(bool disposing) ...@@ -133,7 +133,7 @@ protected override void Dispose(bool disposing)
_reader.Dispose(); _reader.Dispose();
_reader = DisposedReader.Instance; // all future ops are no-ops _reader = DisposedReader.Instance; // all future ops are no-ops
_cmd?.Dispose(); _cmd?.Dispose();
_cmd = null; _cmd = null!;
} }
} }
...@@ -143,12 +143,12 @@ protected override void Dispose(bool disposing) ...@@ -143,12 +143,12 @@ protected override void Dispose(bool disposing)
public override byte GetByte(int i) => _reader.GetByte(i); public override byte GetByte(int i) => _reader.GetByte(i);
public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) => public override long GetBytes(int i, long fieldOffset, byte[]? buffer, int bufferoffset, int length) =>
_reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length); _reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
public override char GetChar(int i) => _reader.GetChar(i); public override char GetChar(int i) => _reader.GetChar(i);
public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) => public override long GetChars(int i, long fieldoffset, char[]? buffer, int bufferoffset, int length) =>
_reader.GetChars(i, fieldoffset, buffer, bufferoffset, length); _reader.GetChars(i, fieldoffset, buffer, bufferoffset, length);
public override string GetDataTypeName(int i) => _reader.GetDataTypeName(i); public override string GetDataTypeName(int i) => _reader.GetDataTypeName(i);
...@@ -208,7 +208,7 @@ protected override void Dispose(bool disposing) ...@@ -208,7 +208,7 @@ protected override void Dispose(bool disposing)
public override Task<ReadOnlyCollection<DbColumn>> GetColumnSchemaAsync(CancellationToken cancellationToken = default) => _reader.GetColumnSchemaAsync(cancellationToken); public override Task<ReadOnlyCollection<DbColumn>> GetColumnSchemaAsync(CancellationToken cancellationToken = default) => _reader.GetColumnSchemaAsync(cancellationToken);
public override Task<DataTable> GetSchemaTableAsync(CancellationToken cancellationToken = default) => base.GetSchemaTableAsync(cancellationToken); public override Task<DataTable?> GetSchemaTableAsync(CancellationToken cancellationToken = default) => base.GetSchemaTableAsync(cancellationToken);
#endif #endif
} }
...@@ -224,9 +224,9 @@ public WrappedBasicReader(IDataReader reader) ...@@ -224,9 +224,9 @@ public WrappedBasicReader(IDataReader reader)
public override bool HasRows => true; // have to assume that we do public override bool HasRows => true; // have to assume that we do
public override void Close() => _reader.Close(); public override void Close() => _reader.Close();
public override DataTable GetSchemaTable() => _reader.GetSchemaTable(); public override DataTable? GetSchemaTable() => _reader.GetSchemaTable();
#if PLAT_NO_REMOTING #if NET5_0_OR_GREATER
[Obsolete("This Remoting API is not supported and throws PlatformNotSupportedException.", DiagnosticId = "SYSLIB0010", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [Obsolete("This Remoting API is not supported and throws PlatformNotSupportedException.", DiagnosticId = "SYSLIB0010", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
#endif #endif
public override object InitializeLifetimeService() => throw new NotSupportedException(); public override object InitializeLifetimeService() => throw new NotSupportedException();
...@@ -257,13 +257,13 @@ protected override void Dispose(bool disposing) ...@@ -257,13 +257,13 @@ protected override void Dispose(bool disposing)
public override byte GetByte(int i) => _reader.GetByte(i); public override byte GetByte(int i) => _reader.GetByte(i);
public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) => public override long GetBytes(int i, long fieldOffset, byte[]? buffer, int bufferoffset, int length) =>
_reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length); _reader.GetBytes(i, fieldOffset, buffer!, bufferoffset, length);
public override char GetChar(int i) => _reader.GetChar(i); public override char GetChar(int i) => _reader.GetChar(i);
public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) => public override long GetChars(int i, long fieldoffset, char[]? buffer, int bufferoffset, int length) =>
_reader.GetChars(i, fieldoffset, buffer, bufferoffset, length); _reader.GetChars(i, fieldoffset, buffer!, bufferoffset, length);
public override string GetDataTypeName(int i) => _reader.GetDataTypeName(i); public override string GetDataTypeName(int i) => _reader.GetDataTypeName(i);
...@@ -308,7 +308,7 @@ public override T GetFieldValue<T>(int ordinal) ...@@ -308,7 +308,7 @@ public override T GetFieldValue<T>(int ordinal)
{ {
value = null; value = null;
} }
return (T)value; return (T)value!;
} }
public override Task<T> GetFieldValueAsync<T>(int ordinal, CancellationToken cancellationToken) public override Task<T> GetFieldValueAsync<T>(int ordinal, CancellationToken cancellationToken)
{ {
...@@ -356,7 +356,7 @@ public override ValueTask DisposeAsync() ...@@ -356,7 +356,7 @@ public override ValueTask DisposeAsync()
public override Task<ReadOnlyCollection<DbColumn>> GetColumnSchemaAsync(CancellationToken cancellationToken = default) public override Task<ReadOnlyCollection<DbColumn>> GetColumnSchemaAsync(CancellationToken cancellationToken = default)
=> throw new NotSupportedException(); => throw new NotSupportedException();
public override Task<DataTable> GetSchemaTableAsync(CancellationToken cancellationToken = default) public override Task<DataTable?> GetSchemaTableAsync(CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(_reader.GetSchemaTable()); return Task.FromResult(_reader.GetSchemaTable());
......
...@@ -6,7 +6,7 @@ namespace Dapper ...@@ -6,7 +6,7 @@ namespace Dapper
{ {
internal abstract class XmlTypeHandler<T> : SqlMapper.StringTypeHandler<T> internal abstract class XmlTypeHandler<T> : SqlMapper.StringTypeHandler<T>
{ {
public override void SetValue(IDbDataParameter parameter, T value) public override void SetValue(IDbDataParameter parameter, T? value)
{ {
base.SetValue(parameter, value); base.SetValue(parameter, value);
parameter.DbType = DbType.Xml; parameter.DbType = DbType.Xml;
......
...@@ -19,11 +19,12 @@ ...@@ -19,11 +19,12 @@
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<DefaultLanguage>en-US</DefaultLanguage> <DefaultLanguage>en-US</DefaultLanguage>
<IncludeSymbols>false</IncludeSymbols> <IncludeSymbols>false</IncludeSymbols>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<LangVersion>9.0</LangVersion> <LangVersion>9.0</LangVersion>
<CheckEolTargetFramework>false</CheckEolTargetFramework> <CheckEolTargetFramework>false</CheckEolTargetFramework>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<PackageReadmeFile>readme.md</PackageReadmeFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
...@@ -36,12 +37,12 @@ ...@@ -36,12 +37,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" IncludeAssets="runtime; build; native; contentfiles; analyzers" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" IncludeAssets="runtime; build; native; contentfiles; analyzers" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/> <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All"/>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="all" /> <PackageReference Include="Nerdbank.GitVersioning" PrivateAssets="all" />
<None Include="../Dapper.png"> <None Include="../Dapper.png" Visible="false" Pack="true">
<Pack>True</Pack>
<PackagePath></PackagePath> <PackagePath></PackagePath>
</None> </None>
<None Include="../docs/readme.md" Link="readme.md" Pack="true" PackagePath="/" Visible="false" />
</ItemGroup> </ItemGroup>
</Project> </Project>
\ No newline at end of file
<Project>
<ItemGroup>
<!-- note: 6.2.0 has regressions; don't force the update -->
<PackageVersion Include="EntityFramework" Version="6.1.3" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="Microsoft.SqlServer.Types" Version="14.0.1016.290" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.6.133" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4" />
<PackageVersion Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
<!-- tests -->
<PackageVersion Include="Belgrade.Sql.Client" Version="1.1.4" />
<PackageVersion Include="BenchmarkDotNet" Version="0.13.7" />
<PackageVersion Include="Dashing" Version="2.10.0" />
<PackageVersion Include="Dapper.Contrib" Version="2.0.78" />
<PackageVersion Include="DevExpress.Xpo" Version="23.1.4" />
<PackageVersion Include="FirebirdSql.Data.FirebirdClient" Version="9.1.1" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.3.2" />
<PackageVersion Include="Iesi.Collections" Version="4.0.5" />
<PackageVersion Include="linq2db.SqlServer" Version="3.7.0" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.1.1" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="7.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageVersion Include="Mighty" Version="3.2.0" />
<PackageVersion Include="MySqlConnector" Version="2.2.7" />
<PackageVersion Include="NHibernate" Version="5.4.5" />
<PackageVersion Include="Norm.net" Version="5.3.7" />
<PackageVersion Include="Npgsql" Version="7.0.4" />
<PackageVersion Include="PetaPoco" Version="5.1.306" />
<PackageVersion Include="RepoDb.SqlServer" Version="1.13.1" />
<PackageVersion Include="ServiceStack.OrmLite.SqlServer" Version="6.10.0" />
<PackageVersion Include="Snowflake.Data" Version="2.1.0" />
<PackageVersion Include="SqlMarshal" Version="0.4.4" />
<PackageVersion Include="SubSonic" Version="3.0.0.4" />
<PackageVersion Include="Susanoo.SqlServer" Version="1.2.4.2" />
<PackageVersion Include="System.Data.SqlClient" Version="4.8.5" />
<PackageVersion Include="System.Data.SQLite" Version="1.0.118" />
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<PackageVersion Include="xunit" Version="2.5.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.0" />
</ItemGroup>
</Project>
\ No newline at end of file
...@@ -368,7 +368,7 @@ Ansi Strings and varchar ...@@ -368,7 +368,7 @@ Ansi Strings and varchar
Dapper supports varchar params, if you are executing a where clause on a varchar column using a param be sure to pass it in this way: Dapper supports varchar params, if you are executing a where clause on a varchar column using a param be sure to pass it in this way:
```csharp ```csharp
Query<Thing>("select * from Thing where Name = @Name", new {Name = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = true }); Query<Thing>("select * from Thing where Name = @Name", new {Name = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = true }});
``` ```
On SQL Server it is crucial to use the unicode when querying unicode and ANSI when querying non unicode. On SQL Server it is crucial to use the unicode when querying unicode and ANSI when querying non unicode.
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
namespace Dapper.Tests.Performance namespace Dapper.Tests.Performance
{ {
[Description("LINQ to DB")] [Description("LINQ to DB")]
public class Linq2DBBenchmarks : BenchmarkBase public class LinqToDBBenchmarks : BenchmarkBase // note To not 2 because the "2" confuses BDN CLI
{ {
private Linq2DBContext _dbContext; private Linq2DBContext _dbContext;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
namespace Dapper.Tests.Performance namespace Dapper.Tests.Performance
{ {
[Description("LINQ to SQL")] [Description("LINQ to SQL")]
public class Linq2SqlBenchmarks : BenchmarkBase public class LinqToSqlBenchmarks : BenchmarkBase // note To not 2 because the "2" confuses BDN CLI
{ {
private DataClassesDataContext Linq2SqlContext; private DataClassesDataContext Linq2SqlContext;
......
...@@ -12,7 +12,7 @@ public class RepoDbBenchmarks : BenchmarkBase ...@@ -12,7 +12,7 @@ public class RepoDbBenchmarks : BenchmarkBase
public void Setup() public void Setup()
{ {
BaseSetup(); BaseSetup();
SqlServerBootstrap.Initialize(); GlobalConfiguration.Setup().UseSqlServer();
ClassMapper.Add<Post>("Posts"); ClassMapper.Add<Post>("Posts");
} }
......
...@@ -3,30 +3,31 @@ ...@@ -3,30 +3,31 @@
<AssemblyName>Dapper.Tests.Performance</AssemblyName> <AssemblyName>Dapper.Tests.Performance</AssemblyName>
<Description>Dapper Core Performance Suite</Description> <Description>Dapper Core Performance Suite</Description>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks> <TargetFrameworks>net462;net5.0</TargetFrameworks>
<IsTestProject>false</IsTestProject> <IsTestProject>false</IsTestProject>
<NoWarn>$(NoWarn);IDE0063;IDE0034;IDE0059;IDE0060</NoWarn> <NoWarn>$(NoWarn);IDE0063;IDE0034;IDE0059;IDE0060</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dashing" Version="2.4.3" />
<PackageReference Include="Belgrade.Sql.Client" Version="1.1.4" />
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="DevExpress.Xpo" Version="20.2.3" />
<!--<PackageReference Include="BLToolkit" Version="4.3.6" />--> <!--<PackageReference Include="BLToolkit" Version="4.3.6" />-->
<PackageReference Include="EntityFramework" Version="6.4.4" />
<PackageReference Include="FirebirdSql.Data.FirebirdClient" Version="7.5.0" /> <PackageReference Include="Dashing" />
<PackageReference Include="linq2db.SqlServer" Version="3.1.6" /> <PackageReference Include="Belgrade.Sql.Client" />
<PackageReference Include="MySqlConnector" Version="1.1.0" /> <PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="NHibernate" Version="5.3.5" /> <PackageReference Include="DevExpress.Xpo" />
<PackageReference Include="Iesi.Collections" Version="4.0.4" /> <PackageReference Include="EntityFramework" VersionOverride="6.4.4"/>
<PackageReference Include="Mighty" Version="3.1.3" /> <PackageReference Include="FirebirdSql.Data.FirebirdClient" />
<PackageReference Include="Npgsql" Version="5.0.0" /> <PackageReference Include="linq2db.SqlServer" />
<PackageReference Include="PetaPoco" Version="5.1.306" /> <PackageReference Include="MySqlConnector" />
<PackageReference Include="RepoDb.SqlServer" Version="1.1.4" /> <PackageReference Include="NHibernate" />
<PackageReference Include="ServiceStack.OrmLite.SqlServer" Version="5.10.2" /> <PackageReference Include="Iesi.Collections" />
<PackageReference Include="SqlMarshal" Version="0.2.0" /> <PackageReference Include="Mighty" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.3" /> <PackageReference Include="Npgsql" />
<PackageReference Include="System.Data.SQLite" Version="1.0.113.6" /> <PackageReference Include="PetaPoco" />
<PackageReference Include="RepoDb.SqlServer" />
<PackageReference Include="ServiceStack.OrmLite.SqlServer" />
<PackageReference Include="SqlMarshal" />
<PackageReference Include="System.Data.SqlClient" />
<PackageReference Include="System.Data.SQLite" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="NHibernate\*.xml" /> <EmbeddedResource Include="NHibernate\*.xml" />
...@@ -38,16 +39,16 @@ ...@@ -38,16 +39,16 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'"> <ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<ProjectReference Include="../../Dapper.EntityFramework/Dapper.EntityFramework.csproj" /> <ProjectReference Include="../../Dapper.EntityFramework/Dapper.EntityFramework.csproj" />
<PackageReference Include="Microsoft.SqlServer.Types" Version="14.0.1016.290" /> <PackageReference Include="Microsoft.SqlServer.Types" />
<PackageReference Include="SubSonic" Version="3.0.0.4" /> <PackageReference Include="SubSonic" />
<PackageReference Include="Susanoo.SqlServer" Version="1.2.4.2" /> <PackageReference Include="Susanoo.SqlServer" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[3.1.10]" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" VersionOverride="[3.1.10]" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Data.Linq" /> <Reference Include="System.Data.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net462'"> <ItemGroup Condition="'$(TargetFramework)' != 'net462'">
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Norm.net" Version="3.2.0" /> <PackageReference Include="Norm.net" />
</ItemGroup> </ItemGroup>
</Project> </Project>
using System.ComponentModel;
using BenchmarkDotNet.Attributes;
namespace Dapper.Tests.Performance
{
[Description("Dapper cache impact")]
[MemoryDiagnoser]
public class DapperCacheImpact : BenchmarkBase
{
[GlobalSetup]
public void Setup() => BaseSetup();
private object args = new { Id = 42, Name = "abc" };
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
// note: custom BDN setup means [Params] is awkward; unroll manually instead
[Benchmark]
public void ExecuteNoParameters_Cache() => _connection.Execute(new CommandDefinition("select '42' as Id, 'abc' as Name", flags: CommandFlags.None));
[Benchmark]
public void ExecuteParameters_Cache() => _connection.Execute(new CommandDefinition("select @id as Id, @name as Name", args, flags: CommandFlags.None));
[Benchmark]
public void QueryFirstNoParameters_Cache() => _connection.QueryFirst<Foo>(new CommandDefinition("select '42' as Id, 'abc' as Name", flags: CommandFlags.None));
[Benchmark]
public void QueryFirstParameters_Cache() => _connection.QueryFirst<Foo>(new CommandDefinition("select @id as Id, @name as Name", args, flags: CommandFlags.None));
[Benchmark]
public void ExecuteNoParameters_NoCache() => _connection.Execute(new CommandDefinition("select '42' as Id, 'abc' as Name", flags: CommandFlags.NoCache));
[Benchmark]
public void ExecuteParameters_NoCache() => _connection.Execute(new CommandDefinition("select @id as Id, @name as Name", args, flags: CommandFlags.NoCache));
[Benchmark]
public void QueryFirstNoParameters_NoCache() => _connection.QueryFirst<Foo>(new CommandDefinition("select '42' as Id, 'abc' as Name", flags: CommandFlags.NoCache));
[Benchmark]
public void QueryFirstParameters_NoCache() => _connection.QueryFirst<Foo>(new CommandDefinition("select @id as Id, @name as Name", args, flags: CommandFlags.NoCache));
}
}
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../Dapper/Dapper.csproj" /> <ProjectReference Include="../../Dapper/Dapper.csproj" />
<PackageReference Include="Dapper.Contrib" Version="2.0.78" /> <PackageReference Include="Dapper.Contrib" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'"> <ItemGroup Condition="'$(TargetFramework)' == 'net461'">
......
...@@ -24,6 +24,23 @@ Note: to get the latest pre-release build, add ` -Pre` to the end of the command ...@@ -24,6 +24,23 @@ Note: to get the latest pre-release build, add ` -Pre` to the end of the command
(note: new PRs will not be merged until they add release note wording here) (note: new PRs will not be merged until they add release note wording here)
### 2.1.4
- add untyped `GridReader.ReadUnbufferedAsync` API (#1958 via @mgravell)
- tweak NRT annotations on type-handler API (#1960 via @mgravell, fixes #1959)
### 2.1.1
- add NRT annotations (#1928 via @mgravell)
- extend `GridReader` API to allow it to be subclassed by external consumers (#1928 via @mgravell)
- support `$` as a parameter prefix (#1952 via @Giorgi)
- add public API tracking (#1948 via @mgravell)
### 2.0.151
- add global `FetchSize` setting for use with Oracle (#1946 via mgravell, fixes #1945) (also add some missing logic in `Settings.Reset()`)
- add underscore handling with constructors (#1786 via @jo-goro, fixes #818; also #1947 via mgravell)
### 2.0.143 ### 2.0.143
- add missing non-generic `AsyncEnumerable<dynamic> QueryUnbufferedAsync(...)` API (#1925 via mgravell, fixes #1922) - add missing non-generic `AsyncEnumerable<dynamic> QueryUnbufferedAsync(...)` API (#1925 via mgravell, fixes #1922)
......
# Dapper
Dapper is a simple micro-ORM used to simplify working with ADO.NET; if you like SQL but dislike the boilerplate of ADO.NET: Dapper is for you!
As a simple example:
``` c#
string region = ...
var customers = connection.Query<Customer>(
"select * from Customers where Region = @region", // SQL
new { region } // parameters
).AsList();
```
But all the execute/single-row/scalar/async/etc functionality you would expect: is there as extension methods on your `DbConnection`.
See [GitHub](https://github.com/DapperLib/Dapper) for more information and examples.
\ No newline at end of file
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
<configuration> <configuration>
<packageSources> <packageSources>
<clear /> <clear />
<add key="npgsql" value="https://www.myget.org/F/npgsql-unstable/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" /> <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources> </packageSources>
</configuration> </configuration>
\ No newline at end of file
此差异已折叠。
...@@ -59,9 +59,9 @@ public void TestNoDefaultConstructorWithChar() ...@@ -59,9 +59,9 @@ public void TestNoDefaultConstructorWithChar()
const char c1 = 'ą'; const char c1 = 'ą';
const char c3 = 'ó'; const char c3 = 'ó';
NoDefaultConstructorWithChar nodef = connection.Query<NoDefaultConstructorWithChar>("select @c1 c1, @c2 c2, @c3 c3", new { c1 = c1, c2 = (char?)null, c3 = c3 }).First(); NoDefaultConstructorWithChar nodef = connection.Query<NoDefaultConstructorWithChar>("select @c1 c1, @c2 c2, @c3 c3", new { c1 = c1, c2 = (char?)null, c3 = c3 }).First();
Assert.Equal(nodef.Char1, c1); Assert.Equal(c1, nodef.Char1);
Assert.Null(nodef.Char2); Assert.Null(nodef.Char2);
Assert.Equal(nodef.Char3, c3); Assert.Equal(c3, nodef.Char3);
} }
[Fact] [Fact]
...@@ -132,6 +132,7 @@ private class MultipleConstructors ...@@ -132,6 +132,7 @@ private class MultipleConstructors
{ {
public MultipleConstructors() public MultipleConstructors()
{ {
B = default!;
} }
public MultipleConstructors(int a, string b) public MultipleConstructors(int a, string b)
...@@ -157,7 +158,7 @@ public ConstructorsWithAccessModifiers(int a, string b) ...@@ -157,7 +158,7 @@ public ConstructorsWithAccessModifiers(int a, string b)
} }
public int A { get; set; } public int A { get; set; }
public string B { get; set; } public string? B { get; set; }
} }
private class NoDefaultConstructor private class NoDefaultConstructor
...@@ -220,5 +221,45 @@ public void TestWithNonPublicConstructor() ...@@ -220,5 +221,45 @@ public void TestWithNonPublicConstructor()
var output = connection.Query<WithPrivateConstructor>("select 1 as Foo").First(); var output = connection.Query<WithPrivateConstructor>("select 1 as Foo").First();
Assert.Equal(1, output.Foo); Assert.Equal(1, output.Foo);
} }
[Fact]
public void CtorWithUnderscores()
{
var obj = connection.QueryFirst<Type_ParamsWithUnderscores>("select 'abc' as FIRST_NAME, 'def' as LAST_NAME");
Assert.NotNull(obj);
Assert.Equal("abc", obj.FirstName);
Assert.Equal("def", obj.LastName);
}
[Fact]
public void CtorWithoutUnderscores()
{
DefaultTypeMap.MatchNamesWithUnderscores = true;
var obj = connection.QueryFirst<Type_ParamsWithoutUnderscores>("select 'abc' as FIRST_NAME, 'def' as LAST_NAME");
Assert.NotNull(obj);
Assert.Equal("abc", obj.FirstName);
Assert.Equal("def", obj.LastName);
}
class Type_ParamsWithUnderscores
{
public string FirstName { get; }
public string LastName { get; }
public Type_ParamsWithUnderscores(string first_name, string last_name)
{
FirstName = first_name;
LastName = last_name;
}
}
class Type_ParamsWithoutUnderscores
{
public string FirstName { get; }
public string LastName { get; }
public Type_ParamsWithoutUnderscores(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
} }
} }
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<PropertyGroup> <PropertyGroup>
<AssemblyName>Dapper.Tests</AssemblyName> <AssemblyName>Dapper.Tests</AssemblyName>
<Description>Dapper Core Test Suite</Description> <Description>Dapper Core Test Suite</Description>
<TargetFrameworks>net462;net472;net6.0</TargetFrameworks> <TargetFrameworks>net472;net6.0</TargetFrameworks>
<DefineConstants>$(DefineConstants);MSSQLCLIENT</DefineConstants> <DefineConstants>$(DefineConstants);MSSQLCLIENT</DefineConstants>
<NoWarn>$(NoWarn);IDE0017;IDE0034;IDE0037;IDE0039;IDE0042;IDE0044;IDE0051;IDE0052;IDE0059;IDE0060;IDE0063;IDE1006;xUnit1004;CA1806;CA1816;CA1822;CA1825;CA2208</NoWarn> <NoWarn>$(NoWarn);IDE0017;IDE0034;IDE0037;IDE0039;IDE0042;IDE0044;IDE0051;IDE0052;IDE0059;IDE0060;IDE0063;IDE1006;xUnit1004;CA1806;CA1816;CA1822;CA1825;CA2208</NoWarn>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net472'"> <PropertyGroup Condition="'$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net472'">
...@@ -14,14 +15,17 @@ ...@@ -14,14 +15,17 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../Dapper.ProviderTools/Dapper.ProviderTools.csproj" /> <ProjectReference Include="../../Dapper.ProviderTools/Dapper.ProviderTools.csproj" />
<ProjectReference Include="../../Dapper.SqlBuilder/Dapper.SqlBuilder.csproj" /> <ProjectReference Include="../../Dapper.SqlBuilder/Dapper.SqlBuilder.csproj" />
<PackageReference Include="FirebirdSql.Data.FirebirdClient" Version="7.5.0" /> <PackageReference Include="FirebirdSql.Data.FirebirdClient" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="2.0.1" /> <PackageReference Include="Microsoft.Data.SqlClient" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.0" /> <PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="MySqlConnector" Version="1.1.0" /> <PackageReference Include="MySqlConnector" />
<PackageReference Include="Npgsql" Version="6.0.0" /> <PackageReference Include="Npgsql" />
<PackageReference Include="Snowflake.Data" Version="2.0.3" /> <PackageReference Include="System.Data.SqlClient" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" /> <PackageReference Include="System.ValueTuple" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net462'">
<PackageReference Include="Snowflake.Data" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net472'"> <ItemGroup Condition="'$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net472'">
......
...@@ -131,7 +131,7 @@ union all ...@@ -131,7 +131,7 @@ union all
var foo = (Discriminated_Foo)result[0]; var foo = (Discriminated_Foo)result[0];
Assert.Equal("abc", foo.Name); Assert.Equal("abc", foo.Name);
var bar = (Discriminated_Bar)result[1]; var bar = (Discriminated_Bar)result[1];
Assert.Equal(bar.Value, (float)4.0); Assert.Equal((float)4.0, bar.Value);
} }
[Fact] [Fact]
...@@ -170,7 +170,7 @@ union all ...@@ -170,7 +170,7 @@ union all
var foo = (Discriminated_Foo)result[0]; var foo = (Discriminated_Foo)result[0];
Assert.Equal("abc", foo.Name); Assert.Equal("abc", foo.Name);
var bar = (Discriminated_Bar)result[1]; var bar = (Discriminated_Bar)result[1];
Assert.Equal(bar.Value, (float)4.0); Assert.Equal((float)4.0, bar.Value);
} }
[Fact] [Fact]
...@@ -195,7 +195,7 @@ union all ...@@ -195,7 +195,7 @@ union all
do do
{ {
DiscriminatedWithMultiMapping_BaseType obj = null; DiscriminatedWithMultiMapping_BaseType? obj = null;
switch (reader.GetInt32(col)) switch (reader.GetInt32(col))
{ {
case 1: case 1:
...@@ -207,7 +207,7 @@ union all ...@@ -207,7 +207,7 @@ union all
} }
Assert.NotNull(obj); Assert.NotNull(obj);
obj.HazNameIdObject = toHaz(reader); obj!.HazNameIdObject = toHaz(reader);
result.Add(obj); result.Add(obj);
} while (reader.Read()); } while (reader.Read());
...@@ -219,10 +219,12 @@ union all ...@@ -219,10 +219,12 @@ union all
Assert.Equal(2, result[1].Type); Assert.Equal(2, result[1].Type);
var foo = (DiscriminatedWithMultiMapping_Foo)result[0]; var foo = (DiscriminatedWithMultiMapping_Foo)result[0];
Assert.Equal("abc", foo.Name); Assert.Equal("abc", foo.Name);
Assert.NotNull(foo.HazNameIdObject);
Assert.Equal(1, foo.HazNameIdObject.Id); Assert.Equal(1, foo.HazNameIdObject.Id);
Assert.Equal("zxc", foo.HazNameIdObject.Name); Assert.Equal("zxc", foo.HazNameIdObject!.Name);
var bar = (DiscriminatedWithMultiMapping_Bar)result[1]; var bar = (DiscriminatedWithMultiMapping_Bar)result[1];
Assert.Equal(bar.Value, (float)4.0); Assert.Equal((float)4.0, bar.Value);
Assert.NotNull(bar.HazNameIdObject);
Assert.Equal(2, bar.HazNameIdObject.Id); Assert.Equal(2, bar.HazNameIdObject.Id);
Assert.Equal("qwe", bar.HazNameIdObject.Name); Assert.Equal("qwe", bar.HazNameIdObject.Name);
} }
...@@ -247,7 +249,7 @@ union all ...@@ -247,7 +249,7 @@ union all
do do
{ {
DiscriminatedWithMultiMapping_BaseType obj = null; DiscriminatedWithMultiMapping_BaseType? obj = null;
switch (reader.GetInt32(col)) switch (reader.GetInt32(col))
{ {
case 1: case 1:
...@@ -271,10 +273,12 @@ union all ...@@ -271,10 +273,12 @@ union all
Assert.Equal(2, result[1].Type); Assert.Equal(2, result[1].Type);
var foo = (DiscriminatedWithMultiMapping_Foo)result[0]; var foo = (DiscriminatedWithMultiMapping_Foo)result[0];
Assert.Equal("abc", foo.Name); Assert.Equal("abc", foo.Name);
Assert.NotNull(foo.HazNameIdObject);
Assert.Equal(1, foo.HazNameIdObject.Id); Assert.Equal(1, foo.HazNameIdObject.Id);
Assert.Equal("zxc", foo.HazNameIdObject.Name); Assert.Equal("zxc", foo.HazNameIdObject.Name);
var bar = (DiscriminatedWithMultiMapping_Bar)result[1]; var bar = (DiscriminatedWithMultiMapping_Bar)result[1];
Assert.Equal(bar.Value, (float)4.0); Assert.Equal((float)4.0, bar.Value);
Assert.NotNull(bar.HazNameIdObject);
Assert.Equal(2, bar.HazNameIdObject.Id); Assert.Equal(2, bar.HazNameIdObject.Id);
Assert.Equal("qwe", bar.HazNameIdObject.Name); Assert.Equal("qwe", bar.HazNameIdObject.Name);
} }
...@@ -286,7 +290,7 @@ private abstract class Discriminated_BaseType ...@@ -286,7 +290,7 @@ private abstract class Discriminated_BaseType
private class Discriminated_Foo : Discriminated_BaseType private class Discriminated_Foo : Discriminated_BaseType
{ {
public string Name { get; set; } public string? Name { get; set; }
public override int Type => 1; public override int Type => 1;
} }
...@@ -298,19 +302,19 @@ private class Discriminated_Bar : Discriminated_BaseType ...@@ -298,19 +302,19 @@ private class Discriminated_Bar : Discriminated_BaseType
private abstract class DiscriminatedWithMultiMapping_BaseType : Discriminated_BaseType private abstract class DiscriminatedWithMultiMapping_BaseType : Discriminated_BaseType
{ {
public abstract HazNameId HazNameIdObject { get; set; } public abstract HazNameId? HazNameIdObject { get; set; }
} }
private class DiscriminatedWithMultiMapping_Foo : DiscriminatedWithMultiMapping_BaseType private class DiscriminatedWithMultiMapping_Foo : DiscriminatedWithMultiMapping_BaseType
{ {
public override HazNameId HazNameIdObject { get; set; } public override HazNameId? HazNameIdObject { get; set; }
public string Name { get; set; } public string? Name { get; set; }
public override int Type => 1; public override int Type => 1;
} }
private class DiscriminatedWithMultiMapping_Bar : DiscriminatedWithMultiMapping_BaseType private class DiscriminatedWithMultiMapping_Bar : DiscriminatedWithMultiMapping_BaseType
{ {
public override HazNameId HazNameIdObject { get; set; } public override HazNameId? HazNameIdObject { get; set; }
public float Value { get; set; } public float Value { get; set; }
public override int Type => 2; public override int Type => 2;
} }
......
...@@ -20,7 +20,7 @@ public void Issue261_Decimals() ...@@ -20,7 +20,7 @@ public void Issue261_Decimals()
parameters.Add("c", dbType: DbType.Decimal, direction: ParameterDirection.Output, precision: 10, scale: 5); parameters.Add("c", dbType: DbType.Decimal, direction: ParameterDirection.Output, precision: 10, scale: 5);
connection.Execute("create proc #Issue261 @c decimal(10,5) OUTPUT as begin set @c=11.884 end"); connection.Execute("create proc #Issue261 @c decimal(10,5) OUTPUT as begin set @c=11.884 end");
connection.Execute("#Issue261", parameters, commandType: CommandType.StoredProcedure); connection.Execute("#Issue261", parameters, commandType: CommandType.StoredProcedure);
var c = parameters.Get<Decimal>("c"); var c = parameters.Get<decimal>("c");
Assert.Equal(11.884M, c); Assert.Equal(11.884M, c);
} }
...@@ -34,11 +34,9 @@ private void Issue261_Decimals_ADONET(bool setPrecisionScaleViaAbstractApi) ...@@ -34,11 +34,9 @@ private void Issue261_Decimals_ADONET(bool setPrecisionScaleViaAbstractApi)
{ {
try try
{ {
using (var cmd = connection.CreateCommand()) using var cmd = connection.CreateCommand();
{ cmd.CommandText = "create proc #Issue261Direct @c decimal(10,5) OUTPUT as begin set @c=11.884 end";
cmd.CommandText = "create proc #Issue261Direct @c decimal(10,5) OUTPUT as begin set @c=11.884 end"; cmd.ExecuteNonQuery();
cmd.ExecuteNonQuery();
}
} }
catch { /* we don't care that it already exists */ } catch { /* we don't care that it already exists */ }
......
...@@ -92,18 +92,17 @@ private class TestEnumClassNoNull ...@@ -92,18 +92,17 @@ private class TestEnumClassNoNull
[Fact] [Fact]
public void AdoNetEnumValue() public void AdoNetEnumValue()
{ {
using (var cmd = connection.CreateCommand()) using var cmd = connection.CreateCommand();
{ cmd.CommandText = "select @foo";
cmd.CommandText = "select @foo"; var p = cmd.CreateParameter();
var p = cmd.CreateParameter(); p.ParameterName = "@foo";
p.ParameterName = "@foo"; p.DbType = DbType.Int32; // it turns out that this is the key piece; setting the DbType
p.DbType = DbType.Int32; // it turns out that this is the key piece; setting the DbType p.Value = AnEnum.B;
p.Value = AnEnum.B; cmd.Parameters.Add(p);
cmd.Parameters.Add(p); object? value = cmd.ExecuteScalar();
object value = cmd.ExecuteScalar(); Assert.NotNull(value);
AnEnum val = (AnEnum)value; AnEnum val = (AnEnum)value;
Assert.Equal(AnEnum.B, val); Assert.Equal(AnEnum.B, val);
}
} }
[Fact] [Fact]
......
...@@ -41,7 +41,7 @@ public FactLongRunningAttribute() ...@@ -41,7 +41,7 @@ public FactLongRunningAttribute()
#endif #endif
} }
public string Url { get; private set; } public string? Url { get; private set; }
} }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
...@@ -59,14 +59,12 @@ public FactRequiredCompatibilityLevelAttribute(int level) : base() ...@@ -59,14 +59,12 @@ public FactRequiredCompatibilityLevelAttribute(int level) : base()
public static readonly int DetectedLevel; public static readonly int DetectedLevel;
static FactRequiredCompatibilityLevelAttribute() static FactRequiredCompatibilityLevelAttribute()
{ {
using (var conn = DatabaseProvider<SystemSqlClientProvider>.Instance.GetOpenConnection()) using var conn = DatabaseProvider<SystemSqlClientProvider>.Instance.GetOpenConnection();
try
{ {
try DetectedLevel = conn.QuerySingle<int>("SELECT compatibility_level FROM sys.databases where name = DB_NAME()");
{
DetectedLevel = conn.QuerySingle<int>("SELECT compatibility_level FROM sys.databases where name = DB_NAME()");
}
catch { /* don't care */ }
} }
catch { /* don't care */ }
} }
} }
...@@ -84,20 +82,18 @@ public FactUnlessCaseSensitiveDatabaseAttribute() : base() ...@@ -84,20 +82,18 @@ public FactUnlessCaseSensitiveDatabaseAttribute() : base()
public static readonly bool IsCaseSensitive; public static readonly bool IsCaseSensitive;
static FactUnlessCaseSensitiveDatabaseAttribute() static FactUnlessCaseSensitiveDatabaseAttribute()
{ {
using (var conn = DatabaseProvider<SystemSqlClientProvider>.Instance.GetOpenConnection()) using var conn = DatabaseProvider<SystemSqlClientProvider>.Instance.GetOpenConnection();
try
{ {
try conn.Execute("declare @i int; set @I = 1;");
{ }
conn.Execute("declare @i int; set @I = 1;"); catch (Exception ex) when (ex.GetType().Name == "SqlException")
} {
catch (Exception ex) when (ex.GetType().Name == "SqlException") int err = ((dynamic)ex).Number;
{ if (err == 137)
int err = ((dynamic)ex).Number; IsCaseSensitive = true;
if (err == 137) else
IsCaseSensitive = true; throw;
else
throw;
}
} }
} }
} }
......
...@@ -24,14 +24,14 @@ public static void DapperEnumValue(IDbConnection connection) ...@@ -24,14 +24,14 @@ public static void DapperEnumValue(IDbConnection connection)
// test passing as int, reading as AnEnum // test passing as int, reading as AnEnum
var k = (int)connection.QuerySingle<AnEnum>("select @v, @y, @z", new { v = (int)AnEnum.B, y = (int?)(int)AnEnum.B, z = (int?)null }); var k = (int)connection.QuerySingle<AnEnum>("select @v, @y, @z", new { v = (int)AnEnum.B, y = (int?)(int)AnEnum.B, z = (int?)null });
Assert.Equal(k, (int)AnEnum.B); Assert.Equal((int)AnEnum.B, k);
args = new DynamicParameters(); args = new DynamicParameters();
args.Add("v", (int)AnEnum.B); args.Add("v", (int)AnEnum.B);
args.Add("y", (int)AnEnum.B); args.Add("y", (int)AnEnum.B);
args.Add("z", null); args.Add("z", null);
k = (int)connection.QuerySingle<AnEnum>("select @v, @y, @z", args); k = (int)connection.QuerySingle<AnEnum>("select @v, @y, @z", args);
Assert.Equal(k, (int)AnEnum.B); Assert.Equal((int)AnEnum.B, k);
} }
public static void TestDateTime(DbConnection connection) public static void TestDateTime(DbConnection connection)
......
...@@ -86,7 +86,7 @@ public void LiteralReplacement() ...@@ -86,7 +86,7 @@ public void LiteralReplacement()
var count = connection.Query<int>("select count(1) from #literal1 where id={=foo}", new { foo = 123 }).Single(); var count = connection.Query<int>("select count(1) from #literal1 where id={=foo}", new { foo = 123 }).Single();
Assert.Equal(1, count); Assert.Equal(1, count);
int sum = connection.Query<int>("select sum(id) + sum(foo) from #literal1").Single(); int sum = connection.Query<int>("select sum(id) + sum(foo) from #literal1").Single();
Assert.Equal(sum, 123 + 456 + 1 + 2 + 3 + 4); Assert.Equal(123 + 456 + 1 + 2 + 3 + 4, sum);
} }
[Fact] [Fact]
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。