From 4e20b8ccc83a3f73c1294f0af0fd85fa462c47ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 00:35:27 +0000 Subject: [PATCH 01/23] Initial plan From 486de5d4403284de15e22bb68157678b5d5163c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 00:48:06 +0000 Subject: [PATCH 02/23] Add specific diagnostic for multi-map queries (DAP050) Co-authored-by: devedse <2350015+devedse@users.noreply.github.com> --- docs/rules/DAP050.md | 102 ++++++++++++++++++ .../DapperAnalyzer.Diagnostics.cs | 1 + .../CodeAnalysis/DapperAnalyzer.cs | 12 ++- .../DapperInterceptorGenerator.cs | 2 +- .../Internal/Inspection.cs | 3 +- test/Dapper.AOT.Test/Verifiers/DAP001.cs | 3 +- 6 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 docs/rules/DAP050.md diff --git a/docs/rules/DAP050.md b/docs/rules/DAP050.md new file mode 100644 index 00000000..34524cb8 --- /dev/null +++ b/docs/rules/DAP050.md @@ -0,0 +1,102 @@ +# DAP050 + +Multi-map queries (using `Query` with multiple type parameters and the `splitOn` parameter) are not currently supported by Dapper.AOT. + +## What are multi-map queries? + +Multi-map queries in Dapper allow you to map a single row to multiple objects based on a column delimiter. For example: + +```csharp +var users = connection.Query( + "SELECT * FROM Users U INNER JOIN Addresses A ON U.Id = A.UserId", + (user, address) => { + user.Address = address; + return user; + }, + splitOn: "Id"); +``` + +The `splitOn` parameter tells Dapper which column marks the boundary between objects (default is "Id"). + +## Why isn't this supported? + +Multi-map queries require complex runtime logic to: +- Split columns based on the `splitOn` parameter +- Read multiple objects from a single row +- Apply the mapping function to combine them + +This functionality is planned for a future release of Dapper.AOT. + +## Workarounds + +Until multi-map support is added, you have several options: + +### Option 1: Use manual mapping + +Instead of relying on Dapper's multi-map feature, query all columns and manually map: + +```csharp +public class UserWithAddress +{ + public int UserId { get; set; } + public string UserName { get; set; } + public int AddressId { get; set; } + public string Street { get; set; } + public string City { get; set; } +} + +var results = connection.Query( + "SELECT U.Id as UserId, U.Name as UserName, A.Id as AddressId, A.Street, A.City " + + "FROM Users U INNER JOIN Addresses A ON U.Id = A.UserId"); + +var users = results.Select(r => new User +{ + Id = r.UserId, + Name = r.UserName, + Address = new Address + { + Id = r.AddressId, + Street = r.Street, + City = r.City + } +}).ToList(); +``` + +### Option 2: Use multiple queries + +Instead of joining in SQL, fetch related data separately: + +```csharp +var users = connection.Query("SELECT * FROM Users").ToList(); +var addresses = connection.Query
("SELECT * FROM Addresses").ToList(); + +// Manual joining in memory +foreach (var user in users) +{ + user.Address = addresses.FirstOrDefault(a => a.UserId == user.Id); +} +``` + +### Option 3: Disable Dapper.AOT for specific methods + +If you need multi-map functionality, you can disable AOT for specific methods: + +```csharp +[DapperAot(false)] +public IEnumerable GetUsersWithAddresses(IDbConnection connection) +{ + return connection.Query( + "SELECT * FROM Users U INNER JOIN Addresses A ON U.Id = A.UserId", + (user, address) => { + user.Address = address; + return user; + }, + splitOn: "Id"); +} +``` + +Note: This will fall back to regular Dapper, which uses runtime code generation and is **not compatible with Native AOT scenarios**. + +## Future Support + +We're actively working on adding multi-map query support to Dapper.AOT. If this feature is important for your project, please [vote or comment on the issue](https://github.com/DapperLib/DapperAOT/issues) to help us prioritize development. diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs index 48990691..4ecd7c5f 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs @@ -57,6 +57,7 @@ public static readonly DiagnosticDescriptor AmbiguousFields = LibraryWarning("DAP047", "Ambiguous fields", "Fields have same name '{0}' after normalization and can be conflated"), MoveFromDbString = LibraryWarning("DAP048", "Move from DbString to DbValue", "DbString achieves the same as [DbValue] does. Use it instead."), UnableToBindQueryColumns = LibraryError("DAP049", "Unable to bind query columns", "Something went terribly wrong"), + MultiMapNotSupported = LibraryInfo("DAP050", "Multi-map queries not currently supported", "Multi-map queries (Query with splitOn parameter) are not currently supported by Dapper.AOT"), // SQL parse specific GeneralSqlError = SqlWarning("DAP200", "SQL error", "SQL error: {0}"), diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs index d798745e..9d3f2038 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs @@ -198,12 +198,16 @@ private void ValidateDapperMethod(in OperationAnalysisContext ctx, IOperation sq if (aotEnabled) { OnDapperAotHit(); // all good for AOT - if (flags.HasAny(OperationFlags.NotAotSupported)) + if (flags.HasAny(OperationFlags.MultiMap)) + { + ctx.ReportDiagnostic(Diagnostic.Create(Diagnostics.MultiMapNotSupported, location)); + } + else if (flags.HasAny(OperationFlags.NotAotSupported)) { ctx.ReportDiagnostic(Diagnostic.Create(Diagnostics.UnsupportedMethod, location, invoke.GetSignature())); } } - else if (!aotAttribExists && !flags.HasAny(OperationFlags.NotAotSupported)) + else if (!aotAttribExists && !flags.HasAny(OperationFlags.NotAotSupported) && !flags.HasAny(OperationFlags.MultiMap)) { // we might have been able to do more, but Dapper.AOT wasn't enabled OnDapperAotMiss(location); @@ -557,6 +561,10 @@ internal static Location SharedParseArgsAndFlags(in ParseState ctx, IInvocationO case "concreteType" when arg.Value is IDefaultValueOperation || (arg.ConstantValue.HasValue && arg.ConstantValue.Value is null): // nothing to do break; + case "map": + case "splitOn": + // multi-map parameters - handled separately + break; case "commandType": if (TryGetConstantValue(arg, out int? ct)) { diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index 595282a6..69ebe14b 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -98,7 +98,7 @@ internal bool PreFilter(SyntaxNode node, CancellationToken cancellationToken) if (ctx.Node is not InvocationExpressionSyntax ie || ctx.SemanticModel.GetOperation(ie) is not IInvocationOperation op || !op.IsDapperMethod(out var flags) - || flags.HasAny(OperationFlags.NotAotSupported | OperationFlags.DoNotGenerate) + || flags.HasAny(OperationFlags.NotAotSupported | OperationFlags.DoNotGenerate | OperationFlags.MultiMap) || !Inspection.IsEnabled(ctx, op, Types.DapperAotAttribute, out var aotAttribExists)) { return null; diff --git a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs index ed114976..620d2242 100644 --- a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs +++ b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs @@ -1244,7 +1244,7 @@ public static bool IsDapperMethod(this IInvocationOperation operation, out Opera { case "Query": flags |= OperationFlags.Query; - if (method.Arity > 1) flags |= OperationFlags.NotAotSupported; + if (method.Arity > 1) flags |= OperationFlags.MultiMap; break; case "QueryAsync": case "QueryUnbufferedAsync": @@ -1597,5 +1597,6 @@ enum OperationFlags QueryMultiple = 1 << 22, GetRowParser = 1 << 23, StrictTypes = 1 << 24, + MultiMap = 1 << 25, // multi-map query (Query) NotAotSupported = 1 << 31, } diff --git a/test/Dapper.AOT.Test/Verifiers/DAP001.cs b/test/Dapper.AOT.Test/Verifiers/DAP001.cs index fabc72fc..da60332c 100644 --- a/test/Dapper.AOT.Test/Verifiers/DAP001.cs +++ b/test/Dapper.AOT.Test/Verifiers/DAP001.cs @@ -22,6 +22,5 @@ public void Foo(DbConnection conn) } } """, DefaultConfig, - [Diagnostic(Diagnostics.UnsupportedMethod).WithLocation(0) - .WithArguments("SqlMapper.Query(IDbConnection, string, Func, object?, IDbTransaction?, bool, string, int?, CommandType?)")]); + [Diagnostic(Diagnostics.MultiMapNotSupported).WithLocation(0)]); } \ No newline at end of file From 18e68b799efbd8d47816ae13570b9fdad144e03e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 01:13:08 +0000 Subject: [PATCH 03/23] Revert diagnostic-only approach, prepare for full multi-map implementation Co-authored-by: devedse <2350015+devedse@users.noreply.github.com> --- docs/rules/DAP050.md | 102 ------------------ .../DapperAnalyzer.Diagnostics.cs | 1 - .../CodeAnalysis/DapperAnalyzer.cs | 12 +-- .../DapperInterceptorGenerator.cs | 2 +- .../Internal/Inspection.cs | 3 +- test/Dapper.AOT.Test/Verifiers/DAP001.cs | 3 +- 6 files changed, 6 insertions(+), 117 deletions(-) delete mode 100644 docs/rules/DAP050.md diff --git a/docs/rules/DAP050.md b/docs/rules/DAP050.md deleted file mode 100644 index 34524cb8..00000000 --- a/docs/rules/DAP050.md +++ /dev/null @@ -1,102 +0,0 @@ -# DAP050 - -Multi-map queries (using `Query` with multiple type parameters and the `splitOn` parameter) are not currently supported by Dapper.AOT. - -## What are multi-map queries? - -Multi-map queries in Dapper allow you to map a single row to multiple objects based on a column delimiter. For example: - -```csharp -var users = connection.Query( - "SELECT * FROM Users U INNER JOIN Addresses A ON U.Id = A.UserId", - (user, address) => { - user.Address = address; - return user; - }, - splitOn: "Id"); -``` - -The `splitOn` parameter tells Dapper which column marks the boundary between objects (default is "Id"). - -## Why isn't this supported? - -Multi-map queries require complex runtime logic to: -- Split columns based on the `splitOn` parameter -- Read multiple objects from a single row -- Apply the mapping function to combine them - -This functionality is planned for a future release of Dapper.AOT. - -## Workarounds - -Until multi-map support is added, you have several options: - -### Option 1: Use manual mapping - -Instead of relying on Dapper's multi-map feature, query all columns and manually map: - -```csharp -public class UserWithAddress -{ - public int UserId { get; set; } - public string UserName { get; set; } - public int AddressId { get; set; } - public string Street { get; set; } - public string City { get; set; } -} - -var results = connection.Query( - "SELECT U.Id as UserId, U.Name as UserName, A.Id as AddressId, A.Street, A.City " + - "FROM Users U INNER JOIN Addresses A ON U.Id = A.UserId"); - -var users = results.Select(r => new User -{ - Id = r.UserId, - Name = r.UserName, - Address = new Address - { - Id = r.AddressId, - Street = r.Street, - City = r.City - } -}).ToList(); -``` - -### Option 2: Use multiple queries - -Instead of joining in SQL, fetch related data separately: - -```csharp -var users = connection.Query("SELECT * FROM Users").ToList(); -var addresses = connection.Query
("SELECT * FROM Addresses").ToList(); - -// Manual joining in memory -foreach (var user in users) -{ - user.Address = addresses.FirstOrDefault(a => a.UserId == user.Id); -} -``` - -### Option 3: Disable Dapper.AOT for specific methods - -If you need multi-map functionality, you can disable AOT for specific methods: - -```csharp -[DapperAot(false)] -public IEnumerable GetUsersWithAddresses(IDbConnection connection) -{ - return connection.Query( - "SELECT * FROM Users U INNER JOIN Addresses A ON U.Id = A.UserId", - (user, address) => { - user.Address = address; - return user; - }, - splitOn: "Id"); -} -``` - -Note: This will fall back to regular Dapper, which uses runtime code generation and is **not compatible with Native AOT scenarios**. - -## Future Support - -We're actively working on adding multi-map query support to Dapper.AOT. If this feature is important for your project, please [vote or comment on the issue](https://github.com/DapperLib/DapperAOT/issues) to help us prioritize development. diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs index 4ecd7c5f..48990691 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.Diagnostics.cs @@ -57,7 +57,6 @@ public static readonly DiagnosticDescriptor AmbiguousFields = LibraryWarning("DAP047", "Ambiguous fields", "Fields have same name '{0}' after normalization and can be conflated"), MoveFromDbString = LibraryWarning("DAP048", "Move from DbString to DbValue", "DbString achieves the same as [DbValue] does. Use it instead."), UnableToBindQueryColumns = LibraryError("DAP049", "Unable to bind query columns", "Something went terribly wrong"), - MultiMapNotSupported = LibraryInfo("DAP050", "Multi-map queries not currently supported", "Multi-map queries (Query with splitOn parameter) are not currently supported by Dapper.AOT"), // SQL parse specific GeneralSqlError = SqlWarning("DAP200", "SQL error", "SQL error: {0}"), diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs index 9d3f2038..d798745e 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs @@ -198,16 +198,12 @@ private void ValidateDapperMethod(in OperationAnalysisContext ctx, IOperation sq if (aotEnabled) { OnDapperAotHit(); // all good for AOT - if (flags.HasAny(OperationFlags.MultiMap)) - { - ctx.ReportDiagnostic(Diagnostic.Create(Diagnostics.MultiMapNotSupported, location)); - } - else if (flags.HasAny(OperationFlags.NotAotSupported)) + if (flags.HasAny(OperationFlags.NotAotSupported)) { ctx.ReportDiagnostic(Diagnostic.Create(Diagnostics.UnsupportedMethod, location, invoke.GetSignature())); } } - else if (!aotAttribExists && !flags.HasAny(OperationFlags.NotAotSupported) && !flags.HasAny(OperationFlags.MultiMap)) + else if (!aotAttribExists && !flags.HasAny(OperationFlags.NotAotSupported)) { // we might have been able to do more, but Dapper.AOT wasn't enabled OnDapperAotMiss(location); @@ -561,10 +557,6 @@ internal static Location SharedParseArgsAndFlags(in ParseState ctx, IInvocationO case "concreteType" when arg.Value is IDefaultValueOperation || (arg.ConstantValue.HasValue && arg.ConstantValue.Value is null): // nothing to do break; - case "map": - case "splitOn": - // multi-map parameters - handled separately - break; case "commandType": if (TryGetConstantValue(arg, out int? ct)) { diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index 69ebe14b..595282a6 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -98,7 +98,7 @@ internal bool PreFilter(SyntaxNode node, CancellationToken cancellationToken) if (ctx.Node is not InvocationExpressionSyntax ie || ctx.SemanticModel.GetOperation(ie) is not IInvocationOperation op || !op.IsDapperMethod(out var flags) - || flags.HasAny(OperationFlags.NotAotSupported | OperationFlags.DoNotGenerate | OperationFlags.MultiMap) + || flags.HasAny(OperationFlags.NotAotSupported | OperationFlags.DoNotGenerate) || !Inspection.IsEnabled(ctx, op, Types.DapperAotAttribute, out var aotAttribExists)) { return null; diff --git a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs index 620d2242..ed114976 100644 --- a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs +++ b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs @@ -1244,7 +1244,7 @@ public static bool IsDapperMethod(this IInvocationOperation operation, out Opera { case "Query": flags |= OperationFlags.Query; - if (method.Arity > 1) flags |= OperationFlags.MultiMap; + if (method.Arity > 1) flags |= OperationFlags.NotAotSupported; break; case "QueryAsync": case "QueryUnbufferedAsync": @@ -1597,6 +1597,5 @@ enum OperationFlags QueryMultiple = 1 << 22, GetRowParser = 1 << 23, StrictTypes = 1 << 24, - MultiMap = 1 << 25, // multi-map query (Query) NotAotSupported = 1 << 31, } diff --git a/test/Dapper.AOT.Test/Verifiers/DAP001.cs b/test/Dapper.AOT.Test/Verifiers/DAP001.cs index da60332c..fabc72fc 100644 --- a/test/Dapper.AOT.Test/Verifiers/DAP001.cs +++ b/test/Dapper.AOT.Test/Verifiers/DAP001.cs @@ -22,5 +22,6 @@ public void Foo(DbConnection conn) } } """, DefaultConfig, - [Diagnostic(Diagnostics.MultiMapNotSupported).WithLocation(0)]); + [Diagnostic(Diagnostics.UnsupportedMethod).WithLocation(0) + .WithArguments("SqlMapper.Query(IDbConnection, string, Func, object?, IDbTransaction?, bool, string, int?, CommandType?)")]); } \ No newline at end of file From 36ca4c84225931ad42270ecddbb2b5f3d2bba7c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 01:25:42 +0000 Subject: [PATCH 04/23] WIP: Add initial multi-map query support - runtime and code generation framework Co-authored-by: devedse <2350015+devedse@users.noreply.github.com> --- .../CodeAnalysis/DapperAnalyzer.cs | 2 + .../DapperInterceptorGenerator.MultiMap.cs | 140 +++++++++ .../DapperInterceptorGenerator.cs | 4 + .../Internal/Inspection.cs | 8 +- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 282 ++++++++++++++++++ 5 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs create mode 100644 src/Dapper.AOT/CommandT.QueryMultiMap.cs diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs index d798745e..a01dc298 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs @@ -554,6 +554,8 @@ internal static Location SharedParseArgsAndFlags(in ParseState ctx, IInvocationO case "startIndex": case "length": case "returnNullIfFirstMissing": + case "map": // multi-map function parameter + case "splitOn": // multi-map split column parameter case "concreteType" when arg.Value is IDefaultValueOperation || (arg.ConstantValue.HasValue && arg.ConstantValue.Value is null): // nothing to do break; diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs new file mode 100644 index 00000000..39a8e06d --- /dev/null +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs @@ -0,0 +1,140 @@ +using Dapper.Internal; +using Dapper.Internal.Roslyn; +using Microsoft.CodeAnalysis; +using System.Collections.Immutable; + +namespace Dapper.CodeAnalysis; + +public sealed partial class DapperInterceptorGenerator +{ + static void WriteMultiMapImplementation( + CodeWriter sb, + IMethodSymbol method, + OperationFlags flags, + OperationFlags commandTypeMode, + ITypeSymbol? parameterType, + string map, bool cache, + in ImmutableArray methodParameters, + in CommandFactoryState factories, + in RowReaderState readers, + string? fixedSql, + AdditionalCommandState? additionalCommandState) + { + var typeArgs = method.TypeArguments; + var arity = typeArgs.Length; + + // The return type is the last type argument + var returnType = typeArgs[arity - 1]; + + sb.Append("return "); + if (flags.HasAll(OperationFlags.Async | OperationFlags.Query | OperationFlags.Buffered)) + { + sb.Append("global::Dapper.DapperAotExtensions.AsEnumerableAsync(").Indent(false).NewLine(); + } + + // Create the Command + sb.Append("global::Dapper.DapperAotExtensions.Command(cnn, ").Append(Forward(methodParameters, "transaction")).Append(", "); + if (fixedSql is not null) + { + sb.AppendVerbatimLiteral(fixedSql).Append(", "); + } + else + { + sb.Append("sql, "); + } + if (commandTypeMode == 0) + { + if (HasParam(methodParameters, "command")) + { + sb.Append("command.GetValueOrDefault()"); + } + else + { + sb.Append("default"); + } + } + else + { + sb.Append("global::System.Data.CommandType.").Append(commandTypeMode.ToString()); + } + sb.Append(", ").Append(Forward(methodParameters, "commandTimeout")).Append(HasParam(methodParameters, "commandTimeout") ? ".GetValueOrDefault()" : "").Append(", "); + if (flags.HasAny(OperationFlags.HasParameters)) + { + var index = factories.GetIndex(parameterType!, map, cache, additionalCommandState, out var subIndex); + sb.Append("CommandFactory").Append(index).Append(".Instance").Append(subIndex); + } + else + { + sb.Append("DefaultCommandFactory"); + } + sb.Append(")."); + + // Call the appropriate QueryBuffered/QueryBufferedAsync method + bool isAsync = flags.HasAny(OperationFlags.Async); + sb.Append("QueryBuffered"); + if (isAsync) + { + sb.Append("Async"); + } + + // Add type parameters + sb.Append("<"); + for (int i = 0; i < arity; i++) + { + if (i > 0) sb.Append(", "); + sb.Append(typeArgs[i]); + } + sb.Append(">("); + + // Add arguments: args, map, factory1, factory2, ..., splitOn, rowCountHint + WriteTypedArg(sb, parameterType).Append(", "); + sb.Append(Forward(methodParameters, "map")).Append(", "); + + // Add row factories for each type (except the return type) + for (int i = 0; i < arity - 1; i++) + { + var typeArg = typeArgs[i]; + sb.AppendReader(typeArg, readers, flags, additionalCommandState?.QueryColumns ?? default); + sb.Append(", "); + } + + // Add splitOn parameter + if (HasParam(methodParameters, "splitOn")) + { + sb.Append(Forward(methodParameters, "splitOn")); + } + else + { + sb.Append("\"Id\""); + } + + // Add rowCountHint if needed + if (flags.HasAny(OperationFlags.Query) && additionalCommandState is { HasRowCountHint: true }) + { + if (additionalCommandState.RowCountHintMemberName is null) + { + sb.Append(", rowCountHint: ").Append(additionalCommandState.RowCountHint); + } + else + { + // member-based hint; add after args + sb.Append(", rowCountHint: ").Append("args.").Append(additionalCommandState.RowCountHintMemberName); + } + } + + // Add cancellationToken for async + if (isAsync && HasParam(methodParameters, "cancellationToken")) + { + sb.Append(", cancellationToken: ").Append(Forward(methodParameters, "cancellationToken")); + } + + sb.Append(")"); + + if (flags.HasAll(OperationFlags.Async | OperationFlags.Query | OperationFlags.Buffered)) + { + sb.Append(")"); + } + + sb.Append(";").NewLine(); + } +} diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index 595282a6..951c156b 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -396,6 +396,10 @@ internal void Generate(in GenerateState ctx) { WriteGetRowParser(sb, resultType, readers, grp.Key.Flags, grp.Key.AdditionalCommandState?.QueryColumns ?? default); } + else if (flags.HasAny(OperationFlags.MultiMap)) + { + WriteMultiMapImplementation(sb, method, flags, commandTypeMode, parameterType, grp.Key.ParameterMap, grp.Key.UniqueLocation is not null, methodParameters, factories, readers, fixedSql, additionalCommandState); + } else if (!TryWriteMultiExecImplementation(sb, flags, commandTypeMode, parameterType, grp.Key.ParameterMap, grp.Key.UniqueLocation is not null, methodParameters, factories, fixedSql, additionalCommandState)) { WriteSingleImplementation(sb, method, resultType, flags, commandTypeMode, parameterType, grp.Key.ParameterMap, grp.Key.UniqueLocation is not null, methodParameters, factories, readers, fixedSql, additionalCommandState); diff --git a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs index ed114976..b91b530a 100644 --- a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs +++ b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs @@ -1244,7 +1244,7 @@ public static bool IsDapperMethod(this IInvocationOperation operation, out Opera { case "Query": flags |= OperationFlags.Query; - if (method.Arity > 1) flags |= OperationFlags.NotAotSupported; + if (method.Arity > 1) flags |= OperationFlags.MultiMap; break; case "QueryAsync": case "QueryUnbufferedAsync": @@ -1305,6 +1305,11 @@ public static bool TryGetConstantValue(IOperation op, out T? value) { return typeArgs[0]; } + // For multi-map queries, the last type argument is the return type + if (typeArgs.Length > 1 && flags.HasAny(OperationFlags.MultiMap)) + { + return typeArgs[typeArgs.Length - 1]; + } } return null; } @@ -1597,5 +1602,6 @@ enum OperationFlags QueryMultiple = 1 << 22, GetRowParser = 1 << 23, StrictTypes = 1 << 24, + MultiMap = 1 << 25, // multi-map queries (Query) NotAotSupported = 1 << 31, } diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs new file mode 100644 index 00000000..7677161a --- /dev/null +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -0,0 +1,282 @@ +using Dapper.Internal; +using System; +using System.Collections.Generic; +using System.Data; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Dapper; + +partial struct Command +{ + /// + /// Reads buffered rows from a multi-map query with 2 types + /// + public List QueryBuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + string splitOn = "Id", + int rowCountHint = 0) + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + List results; + if (state.Reader.Read()) + { + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var split = FindSplit(state.Reader, splitOn, tokenState1); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, split); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + results.Add(map(obj1, obj2)); + } + while (state.Reader.Read()); + state.Return(); + } + else + { + results = []; + } + + // consume entire results (avoid unobserved TDS error messages) + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + return results; + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 2 types (async) + /// + public async Task> QueryBufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + string splitOn = "Id", + int rowCountHint = 0, + CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + List results; + if (await state.Reader.ReadAsync(cancellationToken)) + { + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var split = FindSplit(state.Reader, splitOn, tokenState1); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, split); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + results.Add(map(obj1, obj2)); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + else + { + results = []; + } + + // consume entire results (avoid unobserved TDS error messages) + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + return results; + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Finds the column index where the split should occur based on column name + /// + private static int FindSplit(System.Data.Common.DbDataReader reader, string splitOn, object tokenState) + { + // Default split is after finding the first occurrence of the splitOn column + var fieldCount = reader.FieldCount; + var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + for (int i = 0; i < fieldCount; i++) + { + var name = reader.GetName(i); + foreach (var split in splitColumns) + { + if (StringHashing.NormalizedEquals(name, split)) + { + return i; + } + } + } + + // If not found, split in the middle + return fieldCount / 2; + } + + /// + /// Finds multiple split points based on splitOn column names + /// + private static int[] FindSplits(System.Data.Common.DbDataReader reader, string splitOn, int count) + { + var splits = new int[count]; + var fieldCount = reader.FieldCount; + var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + int splitIndex = 0; + int lastSplit = 0; + + for (int i = 0; i < fieldCount && splitIndex < count; i++) + { + var name = reader.GetName(i); + foreach (var split in splitColumns) + { + if (StringHashing.NormalizedEquals(name, split)) + { + splits[splitIndex++] = i; + lastSplit = i; + break; + } + } + } + + // Fill remaining splits evenly if not all found + if (splitIndex < count) + { + var remaining = count - splitIndex; + var step = (fieldCount - lastSplit) / (remaining + 1); + for (int i = 0; i < remaining; i++) + { + lastSplit += step; + splits[splitIndex++] = lastSplit; + } + } + + return splits; + } + + /// + /// Reads buffered rows from a multi-map query with 3 types + /// + public List QueryBuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + string splitOn = "Id", + int rowCountHint = 0) + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + List results; + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 2); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[1]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + results.Add(map(obj1, obj2, obj3)); + } + while (state.Reader.Read()); + state.Return(); + } + else + { + results = []; + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + return results; + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 3 types (async) + /// + public async Task> QueryBufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + string splitOn = "Id", + int rowCountHint = 0, + CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + List results; + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 2); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[1]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + results.Add(map(obj1, obj2, obj3)); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + else + { + results = []; + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + return results; + } + finally + { + await state.DisposeAsync(); + } + } +} From e4a56fd94b1485834a379ff89c2ed9334e02aa88 Mon Sep 17 00:00:00 2001 From: Devedse Date: Wed, 10 Dec 2025 20:24:37 +0100 Subject: [PATCH 05/23] Refactor multi-map query handling and update related tests for improved clarity and functionality --- .../DapperInterceptorGenerator.Single.cs | 26 +-- .../Internal/Inspection.cs | 7 +- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 28 ++- .../Interceptors/QueryMultiType.output.cs | 170 ++++++++++++++++++ .../QueryMultiType.output.netfx.cs | 170 ++++++++++++++++++ test/Dapper.AOT.Test/Verifiers/DAP001.cs | 4 +- 6 files changed, 383 insertions(+), 22 deletions(-) create mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs create mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.Single.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.Single.cs index 58c3f806..c0f2269c 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.Single.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.Single.cs @@ -152,19 +152,6 @@ static void WriteSingleImplementation( } } sb.Append(";").NewLine(); - - static CodeWriter WriteTypedArg(CodeWriter sb, ITypeSymbol? parameterType) - { - if (parameterType is null || parameterType.IsAnonymousType) - { - sb.Append("param"); - } - else - { - sb.Append("(").Append(parameterType).Append(")param!"); - } - return sb; - } } private static bool HasParam(ImmutableArray methodParameters, string name) @@ -181,4 +168,17 @@ private static bool HasParam(ImmutableArray methodParameters, private static string Forward(ImmutableArray methodParameters, string name) => HasParam(methodParameters, name) ? name : "default"; + + private static CodeWriter WriteTypedArg(CodeWriter sb, ITypeSymbol? parameterType) + { + if (parameterType is null || parameterType.IsAnonymousType) + { + sb.Append("param"); + } + else + { + sb.Append("(").Append(parameterType).Append(")param!"); + } + return sb; + } } \ No newline at end of file diff --git a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs index b91b530a..7ecd5787 100644 --- a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs +++ b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs @@ -1244,7 +1244,12 @@ public static bool IsDapperMethod(this IInvocationOperation operation, out Opera { case "Query": flags |= OperationFlags.Query; - if (method.Arity > 1) flags |= OperationFlags.MultiMap; + if (method.Arity > 1) + { + flags |= OperationFlags.MultiMap; + // Multi-map is only supported for 2-3 types (arity 3-4: T1, T2, TReturn or T1, T2, T3, TReturn) + if (method.Arity > 4) flags |= OperationFlags.NotAotSupported; + } break; case "QueryAsync": case "QueryUnbufferedAsync": diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 7677161a..9ace97a5 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -31,7 +31,7 @@ public List QueryBuffered( { var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); var split = FindSplit(state.Reader, splitOn, tokenState1); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, split); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); results = RowFactory.GetRowBuffer(rowCountHint); do @@ -82,7 +82,7 @@ public async Task> QueryBufferedAsync( { var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); var split = FindSplit(state.Reader, splitOn, tokenState1); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, split); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); results = RowFactory.GetRowBuffer(rowCountHint); do @@ -117,7 +117,15 @@ private static int FindSplit(System.Data.Common.DbDataReader reader, string spli { // Default split is after finding the first occurrence of the splitOn column var fieldCount = reader.FieldCount; +#if NET5_0_OR_GREATER var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); +#else + var splitColumns = splitOn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int idx = 0; idx < splitColumns.Length; idx++) + { + splitColumns[idx] = splitColumns[idx].Trim(); + } +#endif for (int i = 0; i < fieldCount; i++) { @@ -142,7 +150,15 @@ private static int[] FindSplits(System.Data.Common.DbDataReader reader, string s { var splits = new int[count]; var fieldCount = reader.FieldCount; +#if NET5_0_OR_GREATER var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); +#else + var splitColumns = splitOn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int idx = 0; idx < splitColumns.Length; idx++) + { + splitColumns[idx] = splitColumns[idx].Trim(); + } +#endif int splitIndex = 0; int lastSplit = 0; @@ -198,8 +214,8 @@ public List QueryBuffered( { var splits = FindSplits(state.Reader, splitOn, 2); var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[1]); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); results = RowFactory.GetRowBuffer(rowCountHint); do @@ -251,8 +267,8 @@ public async Task> QueryBufferedAsync( { var splits = FindSplits(state.Reader, splitOn, 2); var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Tokens, splits[1]); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); results = RowFactory.GetRowBuffer(rowCountHint); do diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs new file mode 100644 index 00000000..57e4871a --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs @@ -0,0 +1,170 @@ +#nullable enable +#pragma warning disable IDE0078 // unnecessary suppression is necessary +#pragma warning disable CS9270 // SDK-dependent change to interceptors usage +namespace Dapper.AOT // interceptors must be in a known namespace +{ + file static class DapperGeneratedInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 12, 24)] + internal static global::System.Collections.Generic.IEnumerable Query0(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 13, 24)] + internal static global::System.Collections.Generic.IEnumerable Query1(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 19, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync2(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + private class CommonCommandFactory : global::Dapper.CommandFactory + { + public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) + { + var cmd = base.GetCommand(connection, sql, commandType, args); + // apply special per-provider command initialization logic for OracleCommand + if (cmd is global::Oracle.ManagedDataAccess.Client.OracleCommand cmd0) + { + cmd0.BindByName = true; + cmd0.InitialLONGFetchSize = -1; + + } + return cmd; + } + + } + + private static readonly CommonCommandFactory DefaultCommandFactory = new(); + + private sealed class RowFactory0 : global::Dapper.RowFactory + { + internal static readonly RowFactory0 Instance = new(); + private RowFactory0() {} + public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) + { + for (int i = 0; i < tokens.Length; i++) + { + int token = -1; + var name = reader.GetName(columnOffset); + var type = reader.GetFieldType(columnOffset); + switch (NormalizedHash(name)) + { + case 4245442695U when NormalizedEquals(name, "x"): + token = type == typeof(int) ? 0 : 3; // two tokens for right-typed and type-flexible + break; + case 4228665076U when NormalizedEquals(name, "y"): + token = type == typeof(string) ? 1 : 4; + break; + case 4278997933U when NormalizedEquals(name, "z"): + token = type == typeof(double) ? 2 : 5; + break; + + } + tokens[i] = token; + columnOffset++; + + } + return null; + } + public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + { + global::Foo.Customer result = new(); + foreach (var token in tokens) + { + switch (token) + { + case 0: + result.X = reader.GetInt32(columnOffset); + break; + case 3: + result.X = GetValue(reader, columnOffset); + break; + case 1: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); + break; + case 4: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); + break; + case 2: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); + break; + case 5: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); + break; + + } + columnOffset++; + + } + return result; + + } + + } + + + } + } + namespace System.Runtime.CompilerServices +{ + // this type is needed by the compiler to implement interceptors - it doesn't need to + // come from the runtime itself, though + + [global::System.Diagnostics.Conditional("DEBUG")] // not needed post-build, so: evaporate + [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] + sealed file class InterceptsLocationAttribute : global::System.Attribute + { + public InterceptsLocationAttribute(string path, int lineNumber, int columnNumber) + { + _ = path; + _ = lineNumber; + _ = columnNumber; + } + } +} \ No newline at end of file diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs new file mode 100644 index 00000000..57e4871a --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs @@ -0,0 +1,170 @@ +#nullable enable +#pragma warning disable IDE0078 // unnecessary suppression is necessary +#pragma warning disable CS9270 // SDK-dependent change to interceptors usage +namespace Dapper.AOT // interceptors must be in a known namespace +{ + file static class DapperGeneratedInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 12, 24)] + internal static global::System.Collections.Generic.IEnumerable Query0(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 13, 24)] + internal static global::System.Collections.Generic.IEnumerable Query1(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 19, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync2(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + private class CommonCommandFactory : global::Dapper.CommandFactory + { + public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) + { + var cmd = base.GetCommand(connection, sql, commandType, args); + // apply special per-provider command initialization logic for OracleCommand + if (cmd is global::Oracle.ManagedDataAccess.Client.OracleCommand cmd0) + { + cmd0.BindByName = true; + cmd0.InitialLONGFetchSize = -1; + + } + return cmd; + } + + } + + private static readonly CommonCommandFactory DefaultCommandFactory = new(); + + private sealed class RowFactory0 : global::Dapper.RowFactory + { + internal static readonly RowFactory0 Instance = new(); + private RowFactory0() {} + public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) + { + for (int i = 0; i < tokens.Length; i++) + { + int token = -1; + var name = reader.GetName(columnOffset); + var type = reader.GetFieldType(columnOffset); + switch (NormalizedHash(name)) + { + case 4245442695U when NormalizedEquals(name, "x"): + token = type == typeof(int) ? 0 : 3; // two tokens for right-typed and type-flexible + break; + case 4228665076U when NormalizedEquals(name, "y"): + token = type == typeof(string) ? 1 : 4; + break; + case 4278997933U when NormalizedEquals(name, "z"): + token = type == typeof(double) ? 2 : 5; + break; + + } + tokens[i] = token; + columnOffset++; + + } + return null; + } + public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + { + global::Foo.Customer result = new(); + foreach (var token in tokens) + { + switch (token) + { + case 0: + result.X = reader.GetInt32(columnOffset); + break; + case 3: + result.X = GetValue(reader, columnOffset); + break; + case 1: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); + break; + case 4: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); + break; + case 2: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); + break; + case 5: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); + break; + + } + columnOffset++; + + } + return result; + + } + + } + + + } + } + namespace System.Runtime.CompilerServices +{ + // this type is needed by the compiler to implement interceptors - it doesn't need to + // come from the runtime itself, though + + [global::System.Diagnostics.Conditional("DEBUG")] // not needed post-build, so: evaporate + [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] + sealed file class InterceptsLocationAttribute : global::System.Attribute + { + public InterceptsLocationAttribute(string path, int lineNumber, int columnNumber) + { + _ = path; + _ = lineNumber; + _ = columnNumber; + } + } +} \ No newline at end of file diff --git a/test/Dapper.AOT.Test/Verifiers/DAP001.cs b/test/Dapper.AOT.Test/Verifiers/DAP001.cs index fabc72fc..3f79e36d 100644 --- a/test/Dapper.AOT.Test/Verifiers/DAP001.cs +++ b/test/Dapper.AOT.Test/Verifiers/DAP001.cs @@ -17,11 +17,11 @@ class SomeCode { public void Foo(DbConnection conn) { - _ = conn.{|#0:Query|}("proc", null!); + _ = conn.{|#0:Query|}("proc", null!); _ = conn.Query("proc"); } } """, DefaultConfig, [Diagnostic(Diagnostics.UnsupportedMethod).WithLocation(0) - .WithArguments("SqlMapper.Query(IDbConnection, string, Func, object?, IDbTransaction?, bool, string, int?, CommandType?)")]); + .WithArguments("SqlMapper.Query(IDbConnection, string, Func, object?, IDbTransaction?, bool, string, int?, CommandType?)")]); } \ No newline at end of file From f6760404143bb4ff67d98769409a13e1ab1a6458 Mon Sep 17 00:00:00 2001 From: Devedse Date: Wed, 10 Dec 2025 20:47:24 +0100 Subject: [PATCH 06/23] Refactor multi-map implementation to streamline SQL command generation and improve readability --- .../DapperInterceptorGenerator.MultiMap.cs | 6 +- .../Interceptors/QueryMultiType.output.cs | 172 +++++++++--------- 2 files changed, 88 insertions(+), 90 deletions(-) diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs index 39a8e06d..8e3f91b9 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs @@ -128,13 +128,11 @@ static void WriteMultiMapImplementation( sb.Append(", cancellationToken: ").Append(Forward(methodParameters, "cancellationToken")); } - sb.Append(")"); - if (flags.HasAll(OperationFlags.Async | OperationFlags.Query | OperationFlags.Buffered)) { - sb.Append(")"); + sb.Append(")").Outdent(false); } - + sb.Append(")"); sb.Append(";").NewLine(); } } diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs index 57e4871a..f67d8631 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs @@ -46,112 +46,112 @@ file static class DapperGeneratedInterceptors return global::Dapper.DapperAotExtensions.AsEnumerableAsync( global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, splitOn)); - } + } - [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) - { - // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap - // returns data: global::Foo.Customer - global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); - global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); - global::System.Diagnostics.Debug.Assert(buffered is true); - global::System.Diagnostics.Debug.Assert(param is null); + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); - return global::Dapper.DapperAotExtensions.AsEnumerableAsync( - global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); - } + } - private class CommonCommandFactory : global::Dapper.CommandFactory + private class CommonCommandFactory : global::Dapper.CommandFactory + { + public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) + { + var cmd = base.GetCommand(connection, sql, commandType, args); + // apply special per-provider command initialization logic for OracleCommand + if (cmd is global::Oracle.ManagedDataAccess.Client.OracleCommand cmd0) { - public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) - { - var cmd = base.GetCommand(connection, sql, commandType, args); - // apply special per-provider command initialization logic for OracleCommand - if (cmd is global::Oracle.ManagedDataAccess.Client.OracleCommand cmd0) - { - cmd0.BindByName = true; - cmd0.InitialLONGFetchSize = -1; - - } - return cmd; - } + cmd0.BindByName = true; + cmd0.InitialLONGFetchSize = -1; } + return cmd; + } + + } - private static readonly CommonCommandFactory DefaultCommandFactory = new(); + private static readonly CommonCommandFactory DefaultCommandFactory = new(); - private sealed class RowFactory0 : global::Dapper.RowFactory + private sealed class RowFactory0 : global::Dapper.RowFactory + { + internal static readonly RowFactory0 Instance = new(); + private RowFactory0() {} + public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) + { + for (int i = 0; i < tokens.Length; i++) { - internal static readonly RowFactory0 Instance = new(); - private RowFactory0() {} - public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) + int token = -1; + var name = reader.GetName(columnOffset); + var type = reader.GetFieldType(columnOffset); + switch (NormalizedHash(name)) { - for (int i = 0; i < tokens.Length; i++) - { - int token = -1; - var name = reader.GetName(columnOffset); - var type = reader.GetFieldType(columnOffset); - switch (NormalizedHash(name)) - { - case 4245442695U when NormalizedEquals(name, "x"): - token = type == typeof(int) ? 0 : 3; // two tokens for right-typed and type-flexible - break; - case 4228665076U when NormalizedEquals(name, "y"): - token = type == typeof(string) ? 1 : 4; - break; - case 4278997933U when NormalizedEquals(name, "z"): - token = type == typeof(double) ? 2 : 5; - break; - - } - tokens[i] = token; - columnOffset++; - - } - return null; + case 4245442695U when NormalizedEquals(name, "x"): + token = type == typeof(int) ? 0 : 3; // two tokens for right-typed and type-flexible + break; + case 4228665076U when NormalizedEquals(name, "y"): + token = type == typeof(string) ? 1 : 4; + break; + case 4278997933U when NormalizedEquals(name, "z"): + token = type == typeof(double) ? 2 : 5; + break; + } - public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + tokens[i] = token; + columnOffset++; + + } + return null; + } + public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + { + global::Foo.Customer result = new(); + foreach (var token in tokens) + { + switch (token) { - global::Foo.Customer result = new(); - foreach (var token in tokens) - { - switch (token) - { - case 0: - result.X = reader.GetInt32(columnOffset); - break; - case 3: - result.X = GetValue(reader, columnOffset); - break; - case 1: - result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); - break; - case 4: - result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); - break; - case 2: - result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); - break; - case 5: - result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); - break; - - } - columnOffset++; - - } - return result; + case 0: + result.X = reader.GetInt32(columnOffset); + break; + case 3: + result.X = GetValue(reader, columnOffset); + break; + case 1: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); + break; + case 4: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); + break; + case 2: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); + break; + case 5: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); + break; } + columnOffset++; } - + return result; } + } - namespace System.Runtime.CompilerServices + + + } +} +namespace System.Runtime.CompilerServices { // this type is needed by the compiler to implement interceptors - it doesn't need to // come from the runtime itself, though From ea90e131687517f8181560bc287578665028ade5 Mon Sep 17 00:00:00 2001 From: Devedse Date: Wed, 10 Dec 2025 22:48:04 +0100 Subject: [PATCH 07/23] Enhance type handling in DapperInterceptorGenerator and add AppendTypeForNew method in CodeWriter --- .../DapperInterceptorGenerator.cs | 4 +- .../Internal/CodeWriter.cs | 18 ++ .../QueryMultiType.output.netfx.cs | 172 +++++++++--------- 3 files changed, 106 insertions(+), 88 deletions(-) diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index 951c156b..63236bfc 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -1013,7 +1013,7 @@ void WriteReadMethod(in GenerateState context) if (useConstructorDeferred) { // `return new Type(member0, member1, member2, ...);` - sb.Append("return new ").Append(type).Append('('); + sb.Append("return new ").AppendTypeForNew(type).Append('('); WriteDeferredMethodArgs(); sb.Append(')'); WriteDeferredInitialization(); @@ -1035,7 +1035,7 @@ void WriteReadMethod(in GenerateState context) // Member1 = value1, // Member2 = value2 // } - sb.Append("return new ").Append(type); + sb.Append("return new ").AppendTypeForNew(type); WriteDeferredInitialization(); sb.Append(";").Outdent(); } diff --git a/src/Dapper.AOT.Analyzers/Internal/CodeWriter.cs b/src/Dapper.AOT.Analyzers/Internal/CodeWriter.cs index 7f3b03e1..a67b26f9 100644 --- a/src/Dapper.AOT.Analyzers/Internal/CodeWriter.cs +++ b/src/Dapper.AOT.Analyzers/Internal/CodeWriter.cs @@ -122,6 +122,24 @@ public CodeWriter Append(ITypeSymbol? value, bool anonToTuple = false) return this; } + // Append type for use in 'new Type()' expressions, stripping nullable annotation + public CodeWriter AppendTypeForNew(ITypeSymbol? value) + { + if (value is null) + { } + else if (value.IsAnonymousType) + { + Append(value.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + } + else + { + // Strip nullable annotation for object creation (can't do 'new Type?()') + var nonNullable = value.WithNullableAnnotation(NullableAnnotation.NotAnnotated); + Append(GetTypeName(nonNullable)); + } + return this; + } + private void AppendAsValueTuple(ITypeSymbol value) { var members = value.GetMembers(); diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs index 57e4871a..f67d8631 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs @@ -46,112 +46,112 @@ file static class DapperGeneratedInterceptors return global::Dapper.DapperAotExtensions.AsEnumerableAsync( global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, splitOn)); - } + } - [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) - { - // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap - // returns data: global::Foo.Customer - global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); - global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); - global::System.Diagnostics.Debug.Assert(buffered is true); - global::System.Diagnostics.Debug.Assert(param is null); + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); - return global::Dapper.DapperAotExtensions.AsEnumerableAsync( - global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); - } + } - private class CommonCommandFactory : global::Dapper.CommandFactory + private class CommonCommandFactory : global::Dapper.CommandFactory + { + public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) + { + var cmd = base.GetCommand(connection, sql, commandType, args); + // apply special per-provider command initialization logic for OracleCommand + if (cmd is global::Oracle.ManagedDataAccess.Client.OracleCommand cmd0) { - public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) - { - var cmd = base.GetCommand(connection, sql, commandType, args); - // apply special per-provider command initialization logic for OracleCommand - if (cmd is global::Oracle.ManagedDataAccess.Client.OracleCommand cmd0) - { - cmd0.BindByName = true; - cmd0.InitialLONGFetchSize = -1; - - } - return cmd; - } + cmd0.BindByName = true; + cmd0.InitialLONGFetchSize = -1; } + return cmd; + } + + } - private static readonly CommonCommandFactory DefaultCommandFactory = new(); + private static readonly CommonCommandFactory DefaultCommandFactory = new(); - private sealed class RowFactory0 : global::Dapper.RowFactory + private sealed class RowFactory0 : global::Dapper.RowFactory + { + internal static readonly RowFactory0 Instance = new(); + private RowFactory0() {} + public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) + { + for (int i = 0; i < tokens.Length; i++) { - internal static readonly RowFactory0 Instance = new(); - private RowFactory0() {} - public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) + int token = -1; + var name = reader.GetName(columnOffset); + var type = reader.GetFieldType(columnOffset); + switch (NormalizedHash(name)) { - for (int i = 0; i < tokens.Length; i++) - { - int token = -1; - var name = reader.GetName(columnOffset); - var type = reader.GetFieldType(columnOffset); - switch (NormalizedHash(name)) - { - case 4245442695U when NormalizedEquals(name, "x"): - token = type == typeof(int) ? 0 : 3; // two tokens for right-typed and type-flexible - break; - case 4228665076U when NormalizedEquals(name, "y"): - token = type == typeof(string) ? 1 : 4; - break; - case 4278997933U when NormalizedEquals(name, "z"): - token = type == typeof(double) ? 2 : 5; - break; - - } - tokens[i] = token; - columnOffset++; - - } - return null; + case 4245442695U when NormalizedEquals(name, "x"): + token = type == typeof(int) ? 0 : 3; // two tokens for right-typed and type-flexible + break; + case 4228665076U when NormalizedEquals(name, "y"): + token = type == typeof(string) ? 1 : 4; + break; + case 4278997933U when NormalizedEquals(name, "z"): + token = type == typeof(double) ? 2 : 5; + break; + } - public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + tokens[i] = token; + columnOffset++; + + } + return null; + } + public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + { + global::Foo.Customer result = new(); + foreach (var token in tokens) + { + switch (token) { - global::Foo.Customer result = new(); - foreach (var token in tokens) - { - switch (token) - { - case 0: - result.X = reader.GetInt32(columnOffset); - break; - case 3: - result.X = GetValue(reader, columnOffset); - break; - case 1: - result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); - break; - case 4: - result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); - break; - case 2: - result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); - break; - case 5: - result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); - break; - - } - columnOffset++; - - } - return result; + case 0: + result.X = reader.GetInt32(columnOffset); + break; + case 3: + result.X = GetValue(reader, columnOffset); + break; + case 1: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); + break; + case 4: + result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); + break; + case 2: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); + break; + case 5: + result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); + break; } + columnOffset++; } - + return result; } + } - namespace System.Runtime.CompilerServices + + + } +} +namespace System.Runtime.CompilerServices { // this type is needed by the compiler to implement interceptors - it doesn't need to // come from the runtime itself, though From e7b55bc69b5946c7258b8493d85353d456966c7d Mon Sep 17 00:00:00 2001 From: Devedse Date: Wed, 10 Dec 2025 23:50:30 +0100 Subject: [PATCH 08/23] Refactor Tokenize method in interceptors to handle available columns and initialize remaining tokens - Updated the Tokenize method across multiple interceptors to calculate the number of available columns based on the reader's FieldCount and the columnOffset. - Introduced a tokenCount variable to limit the loop iteration to the lesser of the tokens length and available columns. - Added logic to initialize any remaining tokens to -1 (unmapped) after processing available columns, ensuring that all tokens are accounted for. --- .../DapperInterceptorGenerator.cs | 20 +++- .../Interceptors/Cancellation.output.cs | 9 +- .../Interceptors/ColumnAttribute.output.cs | 9 +- .../Interceptors/CommandProperties.output.cs | 9 +- .../Interceptors/DateOnly.net6.output.cs | 9 +- .../Interceptors/DbString.output.cs | 9 +- .../Interceptors/GetRowParser.output.cs | 9 +- .../Interceptors/GlobalFetchSize.output.cs | 9 +- .../Interceptors/InheritedMembers.output.cs | 9 +- .../Interceptors/MiscDiagnostics.output.cs | 27 ++++- .../Interceptors/NonFactoryMethod.output.cs | 9 +- .../Interceptors/Query.output.cs | 9 +- ...ustomConstructionWithConstructor.output.cs | 108 ++++++++++++++++-- ...tomConstructionWithFactoryMethod.output.cs | 36 +++++- .../Interceptors/QueryDetection.output.cs | 9 +- .../Interceptors/QueryMultiType.output.cs | 9 +- .../Interceptors/QueryStrictBind.output.cs | 28 ++++- .../Interceptors/RequiredProperties.output.cs | 9 +- .../Interceptors/RowCountHint.output.cs | 9 +- .../Interceptors/Single.output.cs | 9 +- .../Interceptors/Techempower.output.cs | 9 +- .../Interceptors/TopLevelStatements.output.cs | 9 +- .../Interceptors/TsqlTips.output.cs | 9 +- 23 files changed, 337 insertions(+), 44 deletions(-) diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index 63236bfc..4a7bf43a 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -822,7 +822,10 @@ void WriteTokenizeMethod() sb.Append("public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset)").Indent().NewLine(); if (queryColumns.IsDefault) // need to apply full map { - sb.Append("for (int i = 0; i < tokens.Length; i++)").Indent().NewLine() + // Limit tokens to available columns starting from columnOffset + sb.Append("int availableColumns = reader.FieldCount - columnOffset;").NewLine() + .Append("int tokenCount = global::System.Math.Min(tokens.Length, availableColumns);").NewLine() + .Append("for (int i = 0; i < tokenCount; i++)").Indent().NewLine() .Append("int token = -1;").NewLine() .Append("var name = reader.GetName(columnOffset);").NewLine() .Append("var type = reader.GetFieldType(columnOffset);").NewLine() @@ -854,7 +857,10 @@ void WriteTokenizeMethod() sb.Outdent().NewLine() .Append("tokens[i] = token;").NewLine() .Append("columnOffset++;").NewLine() - .Outdent().NewLine(); + .Outdent().NewLine() + .Append("// Initialize remaining tokens to -1 (unmapped)").NewLine() + .Append("for (int i = tokenCount; i < tokens.Length; i++)").Indent().NewLine() + .Append("tokens[i] = -1;").Outdent().NewLine(); } else { @@ -866,7 +872,9 @@ void WriteTokenizeMethod() else { sb.Append("// pre-defined columns, but still needs type map").NewLine(); - sb.Append("for (int i = 0; i < tokens.Length; i++)").Indent().NewLine() + sb.Append("int availableColumns = reader.FieldCount - columnOffset;").NewLine() + .Append("int tokenCount = global::System.Math.Min(tokens.Length, availableColumns);").NewLine() + .Append("for (int i = 0; i < tokenCount; i++)").Indent().NewLine() .Append("var type = reader.GetFieldType(columnOffset);").NewLine() .Append("tokens[i] = i switch").Indent().NewLine(); for (int i = 0; i < members.Length;i++) @@ -878,7 +886,11 @@ void WriteTokenizeMethod() .Append(" : ").Append(i + map.Members.Length).Append(",").NewLine(); } } - sb.Append("_ => -1,").Outdent().Append(";").Outdent().NewLine(); + sb.Append("_ => -1,").Outdent().Append(";").NewLine() + .Append("columnOffset++;").Outdent().NewLine() + .Append("// Initialize remaining tokens to -1 (unmapped)").NewLine() + .Append("for (int i = tokenCount; i < tokens.Length; i++)").Indent().NewLine() + .Append("tokens[i] = -1;").Outdent().NewLine(); } } diff --git a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs index 5e7b41f8..55c4ddb7 100644 --- a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs @@ -59,7 +59,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -75,6 +77,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs index 95e4a990..77f139df 100644 --- a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs @@ -44,7 +44,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -72,6 +74,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs index 04baf13c..6f59fc96 100644 --- a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs @@ -111,7 +111,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -133,6 +135,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs index 65050cd5..58149f12 100644 --- a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs @@ -62,7 +62,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -84,6 +86,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.User Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs index a9352496..1d035afe 100644 --- a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs @@ -62,7 +62,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -84,6 +86,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs index ae7dcb16..b8ee0861 100644 --- a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs @@ -52,7 +52,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -71,6 +73,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::HazNameId Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs index f3df7464..65897a01 100644 --- a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs @@ -46,7 +46,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -62,6 +64,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeApp.SomeQueryType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs index 06c42433..a1654c44 100644 --- a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs @@ -59,7 +59,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -78,6 +80,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Entity1 Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs index 7585f734..c446e148 100644 --- a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs @@ -135,7 +135,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -151,6 +153,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeCode.InternalNesting.SomePublicType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -183,7 +190,9 @@ private sealed class RowFactory1 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -199,6 +208,11 @@ private RowFactory1() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeCode.InternalNesting.SomeInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -231,7 +245,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -247,6 +263,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeCode.InternalNesting.SomeProtectedInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs index cdc7f275..9b205aea 100644 --- a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs @@ -59,7 +59,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -147,6 +149,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::UsageLinker.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/Query.output.cs b/test/Dapper.AOT.Test/Interceptors/Query.output.cs index 9fec9750..b9b1b4e3 100644 --- a/test/Dapper.AOT.Test/Interceptors/Query.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Query.output.cs @@ -163,7 +163,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -185,6 +187,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs index df82c4d3..e720b335 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs @@ -198,7 +198,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -220,6 +222,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.ParameterlessCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -264,7 +271,9 @@ private sealed class RowFactory1 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -286,6 +295,11 @@ private RowFactory1() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.GetOnlyPropertiesViaConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -330,7 +344,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -352,6 +368,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordClass Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -396,7 +417,9 @@ private sealed class RowFactory3 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -418,6 +441,11 @@ private RowFactory3() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordClassSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -462,7 +490,9 @@ private sealed class RowFactory4 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -484,6 +514,11 @@ private RowFactory4() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordStruct Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -528,7 +563,9 @@ private sealed class RowFactory5 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -550,6 +587,11 @@ private RowFactory5() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordStructSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -594,7 +636,9 @@ private sealed class RowFactory6 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -616,6 +660,11 @@ private RowFactory6() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.InitPropsOnly Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -665,7 +714,9 @@ private sealed class RowFactory7 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -687,6 +738,11 @@ private RowFactory7() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.InitPropsAndDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -735,7 +791,9 @@ private sealed class RowFactory8 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -757,6 +815,11 @@ private RowFactory8() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.OnlyNonDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -801,7 +864,9 @@ private sealed class RowFactory9 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -823,6 +888,11 @@ private RowFactory9() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.SingleDefaultCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -867,7 +937,9 @@ private sealed class RowFactory10 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -889,6 +961,11 @@ private RowFactory10() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MultipleDapperAotCtors Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -933,7 +1010,9 @@ private sealed class RowFactory11 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -955,6 +1034,11 @@ private RowFactory11() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.SingleDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs index 2690c419..adbcae90 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs @@ -86,7 +86,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -108,6 +110,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.PublicPropertiesNoConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -152,7 +159,9 @@ private sealed class RowFactory1 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -174,6 +183,11 @@ private RowFactory1() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MultipleDapperAotFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -218,7 +232,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -240,6 +256,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.SingleFactoryNotMarkedWithDapperAot Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -284,7 +305,9 @@ private sealed class RowFactory3 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -306,6 +329,11 @@ private RowFactory3() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MultipleStandardFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs index 3a49981c..1962c288 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs @@ -129,7 +129,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -148,6 +150,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs index f67d8631..3a3d6ae7 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs @@ -88,7 +88,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -110,6 +112,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs index 34e4571c..93b0c7ea 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs @@ -89,7 +89,9 @@ private RowFactory0() {} { global::System.Diagnostics.Debug.Assert(tokens.Length >= 5, "Query columns count mismatch"); // pre-defined columns, but still needs type map - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { var type = reader.GetFieldType(columnOffset); tokens[i] = i switch @@ -99,6 +101,12 @@ private RowFactory0() {} 4 => type == typeof(string) ? 4 : 7, _ => -1, }; + columnOffset++; + } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; } return null; } @@ -185,7 +193,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -207,6 +217,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -242,7 +257,9 @@ private sealed class RowFactory3 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -264,6 +281,11 @@ private RowFactory3() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs index 063e3ab0..8b380b83 100644 --- a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs @@ -45,7 +45,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -67,6 +69,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs index c6165e55..0fb64d7a 100644 --- a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs @@ -76,7 +76,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -98,6 +100,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/Single.output.cs b/test/Dapper.AOT.Test/Interceptors/Single.output.cs index 635767a1..6e7330a1 100644 --- a/test/Dapper.AOT.Test/Interceptors/Single.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Single.output.cs @@ -146,7 +146,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -168,6 +170,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs index 2377f028..1599da02 100644 --- a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs @@ -73,7 +73,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -92,6 +94,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::World Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs index 83e5bfea..c44052ff 100644 --- a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs @@ -44,7 +44,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -63,6 +65,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeThing Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) diff --git a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs index 1be948cd..91895346 100644 --- a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs @@ -203,7 +203,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -219,6 +221,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) From a753a0a74f1c4b58948d606428faaec6cd7a4b73 Mon Sep 17 00:00:00 2001 From: Devedse Date: Thu, 11 Dec 2025 01:40:48 +0100 Subject: [PATCH 09/23] Enhance Dapper.AOT Interceptors to Handle Unmapped Columns - Added handling for unmapped columns in various interceptors by introducing a case for -1, which skips unmapped columns during data reading. - Updated Tokenize methods to initialize remaining tokens to -1 for unmapped columns. - Adjusted output files for QueryCustomConstructionWithFactoryMethod, QueryDetection, QueryMultiType, RequiredProperties, RowCountHint, Single, Techempower, TopLevelStatements, and TsqlTips to reflect these changes. - Bumped version to 1.0.59 in version.json. --- .../DapperInterceptorGenerator.cs | 5 + .../Interceptors/Cancellation.output.cs | 3 + .../Interceptors/Cancellation.output.netfx.cs | 12 +- .../Interceptors/ColumnAttribute.output.cs | 3 + .../ColumnAttribute.output.netfx.cs | 12 +- .../Interceptors/CommandProperties.output.cs | 3 + .../CommandProperties.output.netfx.cs | 12 +- .../Interceptors/DateOnly.net6.output.cs | 3 + .../Interceptors/DbString.output.cs | 3 + .../Interceptors/DbString.output.netfx.cs | 12 +- .../Interceptors/GetRowParser.output.cs | 3 + .../Interceptors/GetRowParser.output.netfx.cs | 12 +- .../Interceptors/GlobalFetchSize.output.cs | 3 + .../GlobalFetchSize.output.netfx.cs | 12 +- .../Interceptors/InheritedMembers.output.cs | 3 + .../InheritedMembers.output.netfx.cs | 12 +- .../Interceptors/MiscDiagnostics.output.cs | 9 ++ .../MiscDiagnostics.output.netfx.cs | 36 ++++- .../Interceptors/NonFactoryMethod.output.cs | 3 + .../NonFactoryMethod.output.netfx.cs | 12 +- .../Interceptors/Query.output.cs | 3 + .../Interceptors/Query.output.netfx.cs | 12 +- ...ustomConstructionWithConstructor.output.cs | 36 +++++ ...onstructionWithConstructor.output.netfx.cs | 144 ++++++++++++++++-- ...tomConstructionWithFactoryMethod.output.cs | 12 ++ ...structionWithFactoryMethod.output.netfx.cs | 48 +++++- .../Interceptors/QueryDetection.output.cs | 3 + .../QueryDetection.output.netfx.cs | 12 +- .../Interceptors/QueryMultiType.output.cs | 3 + .../QueryMultiType.output.netfx.cs | 12 +- .../QueryMultiType.output.netfx.txt | 4 + .../Interceptors/QueryMultiType.output.txt | 4 + .../Interceptors/QueryStrictBind.output.cs | 12 ++ .../QueryStrictBind.output.netfx.cs | 40 ++++- .../Interceptors/RequiredProperties.output.cs | 3 + .../RequiredProperties.output.netfx.cs | 12 +- .../Interceptors/RowCountHint.output.cs | 3 + .../Interceptors/RowCountHint.output.netfx.cs | 12 +- .../Interceptors/Single.output.cs | 3 + .../Interceptors/Single.output.netfx.cs | 12 +- .../Interceptors/Techempower.output.cs | 3 + .../Interceptors/Techempower.output.netfx.cs | 12 +- .../Interceptors/TopLevelStatements.output.cs | 3 + .../TopLevelStatements.output.netfx.cs | 12 +- .../Interceptors/TsqlTips.output.cs | 3 + .../Interceptors/TsqlTips.output.netfx.cs | 12 +- version.json | 2 +- 47 files changed, 570 insertions(+), 40 deletions(-) create mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt create mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index 4a7bf43a..e7a1fa88 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -1006,6 +1006,11 @@ void WriteReadMethod(in GenerateState context) token++; } + // Handle unmapped columns (token = -1) + sb.Append("case -1:").NewLine().Indent(false) + .Append("// unmapped column, skip").NewLine() + .Append("break;").NewLine().Outdent(false); + sb.Outdent().NewLine().Append("columnOffset++;").NewLine().Outdent().NewLine(); if (useDeferredConstruction) diff --git a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs index 55c4ddb7..26b3e0c1 100644 --- a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs @@ -97,6 +97,9 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs index 5e7b41f8..26b3e0c1 100644 --- a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs @@ -59,7 +59,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -75,6 +77,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -90,6 +97,9 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs index 77f139df..17c8256b 100644 --- a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs @@ -118,6 +118,9 @@ private RowFactory0() {} case 9: result.E = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs index 95e4a990..17c8256b 100644 --- a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs @@ -44,7 +44,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -72,6 +74,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -111,6 +118,9 @@ private RowFactory0() {} case 9: result.E = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs index 6f59fc96..931584ab 100644 --- a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs @@ -167,6 +167,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs index 04baf13c..931584ab 100644 --- a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs @@ -111,7 +111,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -133,6 +135,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -160,6 +167,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs index 58149f12..2fae9e77 100644 --- a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs @@ -118,6 +118,9 @@ private RowFactory0() {} case 5: result.BirthDate = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs index 1d035afe..5309b48b 100644 --- a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs @@ -118,6 +118,9 @@ private RowFactory0() {} case 5: result.ProductNumber = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs index a9352496..5309b48b 100644 --- a/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs @@ -62,7 +62,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -84,6 +86,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -111,6 +118,9 @@ private RowFactory0() {} case 5: result.ProductNumber = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs index b8ee0861..d30aff0f 100644 --- a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs @@ -99,6 +99,9 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs index ae7dcb16..d30aff0f 100644 --- a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs @@ -52,7 +52,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -71,6 +73,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::HazNameId Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -92,6 +99,9 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs index 65897a01..2388e395 100644 --- a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs @@ -84,6 +84,9 @@ private RowFactory0() {} case 1: result.Dummy = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs index f3df7464..2388e395 100644 --- a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs @@ -46,7 +46,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -62,6 +64,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeApp.SomeQueryType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -77,6 +84,9 @@ private RowFactory0() {} case 1: result.Dummy = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs index a1654c44..f126ca82 100644 --- a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs @@ -106,6 +106,9 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs index 06c42433..f126ca82 100644 --- a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs @@ -59,7 +59,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -78,6 +80,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Entity1 Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -99,6 +106,9 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs index c446e148..8d13c9a6 100644 --- a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs @@ -173,6 +173,9 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -228,6 +231,9 @@ private RowFactory1() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -283,6 +289,9 @@ private RowFactory2() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs index 7585f734..8d13c9a6 100644 --- a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs @@ -135,7 +135,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -151,6 +153,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeCode.InternalNesting.SomePublicType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -166,6 +173,9 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -183,7 +193,9 @@ private sealed class RowFactory1 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -199,6 +211,11 @@ private RowFactory1() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeCode.InternalNesting.SomeInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -214,6 +231,9 @@ private RowFactory1() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -231,7 +251,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -247,6 +269,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeCode.InternalNesting.SomeProtectedInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -262,6 +289,9 @@ private RowFactory2() {} case 1: result.Id = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs index 9b205aea..da0f2aa9 100644 --- a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs @@ -313,6 +313,9 @@ private RowFactory0() {} case 49: result.ModifiedDate = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs index cdc7f275..da0f2aa9 100644 --- a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs @@ -59,7 +59,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -147,6 +149,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::UsageLinker.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -306,6 +313,9 @@ private RowFactory0() {} case 49: result.ModifiedDate = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Query.output.cs b/test/Dapper.AOT.Test/Interceptors/Query.output.cs index b9b1b4e3..726d37db 100644 --- a/test/Dapper.AOT.Test/Interceptors/Query.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Query.output.cs @@ -219,6 +219,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs index f39237c0..5b64c540 100644 --- a/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs @@ -135,7 +135,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -157,6 +159,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -184,6 +191,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs index e720b335..18fbca73 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs @@ -254,6 +254,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -329,6 +332,9 @@ private RowFactory1() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -400,6 +406,9 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -475,6 +484,9 @@ private RowFactory3() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -546,6 +558,9 @@ private RowFactory4() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -621,6 +636,9 @@ private RowFactory5() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -694,6 +712,9 @@ private RowFactory6() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -772,6 +793,9 @@ private RowFactory7() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -847,6 +871,9 @@ private RowFactory8() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -920,6 +947,9 @@ private RowFactory9() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -993,6 +1023,9 @@ private RowFactory10() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -1066,6 +1099,9 @@ private RowFactory11() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs index df82c4d3..18fbca73 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs @@ -198,7 +198,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -220,6 +222,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.ParameterlessCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -247,6 +254,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -264,7 +274,9 @@ private sealed class RowFactory1 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -286,6 +298,11 @@ private RowFactory1() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.GetOnlyPropertiesViaConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -315,6 +332,9 @@ private RowFactory1() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -330,7 +350,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -352,6 +374,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordClass Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -379,6 +406,9 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -396,7 +426,9 @@ private sealed class RowFactory3 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -418,6 +450,11 @@ private RowFactory3() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordClassSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -447,6 +484,9 @@ private RowFactory3() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -462,7 +502,9 @@ private sealed class RowFactory4 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -484,6 +526,11 @@ private RowFactory4() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordStruct Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -511,6 +558,9 @@ private RowFactory4() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -528,7 +578,9 @@ private sealed class RowFactory5 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -550,6 +602,11 @@ private RowFactory5() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.RecordStructSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -579,6 +636,9 @@ private RowFactory5() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -594,7 +654,9 @@ private sealed class RowFactory6 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -616,6 +678,11 @@ private RowFactory6() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.InitPropsOnly Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -645,6 +712,9 @@ private RowFactory6() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -665,7 +735,9 @@ private sealed class RowFactory7 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -687,6 +759,11 @@ private RowFactory7() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.InitPropsAndDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -716,6 +793,9 @@ private RowFactory7() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -735,7 +815,9 @@ private sealed class RowFactory8 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -757,6 +839,11 @@ private RowFactory8() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.OnlyNonDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -784,6 +871,9 @@ private RowFactory8() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -801,7 +891,9 @@ private sealed class RowFactory9 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -823,6 +915,11 @@ private RowFactory9() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.SingleDefaultCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -850,6 +947,9 @@ private RowFactory9() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -867,7 +967,9 @@ private sealed class RowFactory10 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -889,6 +991,11 @@ private RowFactory10() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MultipleDapperAotCtors Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -916,6 +1023,9 @@ private RowFactory10() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -933,7 +1043,9 @@ private sealed class RowFactory11 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -955,6 +1067,11 @@ private RowFactory11() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.SingleDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -982,6 +1099,9 @@ private RowFactory11() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs index adbcae90..2c5826ef 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs @@ -144,6 +144,9 @@ private RowFactory0() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -215,6 +218,9 @@ private RowFactory1() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -288,6 +294,9 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -361,6 +370,9 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs index 2690c419..2c5826ef 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs @@ -86,7 +86,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -108,6 +110,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.PublicPropertiesNoConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -137,6 +144,9 @@ private RowFactory0() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -152,7 +162,9 @@ private sealed class RowFactory1 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -174,6 +186,11 @@ private RowFactory1() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MultipleDapperAotFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -201,6 +218,9 @@ private RowFactory1() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -218,7 +238,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -240,6 +262,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.SingleFactoryNotMarkedWithDapperAot Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -267,6 +294,9 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -284,7 +314,9 @@ private sealed class RowFactory3 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -306,6 +338,11 @@ private RowFactory3() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.MultipleStandardFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -333,6 +370,9 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs index 1962c288..e32c166a 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs @@ -176,6 +176,9 @@ private RowFactory0() {} case 3: result.Name = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs index 3a49981c..e32c166a 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs @@ -129,7 +129,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -148,6 +150,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -169,6 +176,9 @@ private RowFactory0() {} case 3: result.Name = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs index 3a3d6ae7..4a30871b 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs @@ -144,6 +144,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs index f67d8631..4a30871b 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs @@ -88,7 +88,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -110,6 +112,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -137,6 +144,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt new file mode 100644 index 00000000..0d284670 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt @@ -0,0 +1,4 @@ +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 4 of 4 possible call-sites using 4 interceptors, 0 commands and 1 readers diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt new file mode 100644 index 00000000..0d284670 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt @@ -0,0 +1,4 @@ +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 4 of 4 possible call-sites using 4 interceptors, 0 commands and 1 readers diff --git a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs index 93b0c7ea..6e8c8951 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs @@ -135,6 +135,9 @@ private RowFactory0() {} case 7: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -175,6 +178,9 @@ private RowFactory1() {} case 4: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -240,6 +246,9 @@ private RowFactory2() {} case 2: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -313,6 +322,9 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs index 34e4571c..6e8c8951 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs @@ -89,7 +89,9 @@ private RowFactory0() {} { global::System.Diagnostics.Debug.Assert(tokens.Length >= 5, "Query columns count mismatch"); // pre-defined columns, but still needs type map - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { var type = reader.GetFieldType(columnOffset); tokens[i] = i switch @@ -99,6 +101,12 @@ private RowFactory0() {} 4 => type == typeof(string) ? 4 : 7, _ => -1, }; + columnOffset++; + } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; } return null; } @@ -127,6 +135,9 @@ private RowFactory0() {} case 7: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -167,6 +178,9 @@ private RowFactory1() {} case 4: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -185,7 +199,9 @@ private sealed class RowFactory2 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -207,6 +223,11 @@ private RowFactory2() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -225,6 +246,9 @@ private RowFactory2() {} case 2: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; @@ -242,7 +266,9 @@ private sealed class RowFactory3 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -264,6 +290,11 @@ private RowFactory3() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -291,6 +322,9 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs index 8b380b83..28f1c2d8 100644 --- a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs @@ -103,6 +103,9 @@ private RowFactory0() {} case 5: value2 = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs index 063e3ab0..28f1c2d8 100644 --- a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs @@ -45,7 +45,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -67,6 +69,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -96,6 +103,9 @@ private RowFactory0() {} case 5: value2 = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs index 0fb64d7a..66536875 100644 --- a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs @@ -132,6 +132,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs index c6165e55..66536875 100644 --- a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs @@ -76,7 +76,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -98,6 +100,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -125,6 +132,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Single.output.cs b/test/Dapper.AOT.Test/Interceptors/Single.output.cs index 6e7330a1..32696980 100644 --- a/test/Dapper.AOT.Test/Interceptors/Single.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Single.output.cs @@ -202,6 +202,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs index 635767a1..32696980 100644 --- a/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs @@ -146,7 +146,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -168,6 +170,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -195,6 +202,9 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs index 1599da02..891e48a5 100644 --- a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs @@ -120,6 +120,9 @@ private RowFactory0() {} case 3: result.RandomNumber = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs index 2377f028..891e48a5 100644 --- a/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs @@ -73,7 +73,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -92,6 +94,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::World Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -113,6 +120,9 @@ private RowFactory0() {} case 3: result.RandomNumber = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs index c44052ff..c784baf4 100644 --- a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs @@ -92,6 +92,9 @@ private RowFactory0() {} case 3: value1 = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs index 83e5bfea..c784baf4 100644 --- a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs @@ -44,7 +44,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory private RowFactory0() {} public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -63,6 +65,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::SomeThing Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -85,6 +92,9 @@ private RowFactory0() {} case 3: value1 = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs index 91895346..bfbf1f3d 100644 --- a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs @@ -241,6 +241,9 @@ private RowFactory0() {} case 1: result.Balance = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs index 1be948cd..bfbf1f3d 100644 --- a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs @@ -203,7 +203,9 @@ private sealed class RowFactory0 : global::Dapper.RowFactory tokens, int columnOffset) { - for (int i = 0; i < tokens.Length; i++) + int availableColumns = reader.FieldCount - columnOffset; + int tokenCount = global::System.Math.Min(tokens.Length, availableColumns); + for (int i = 0; i < tokenCount; i++) { int token = -1; var name = reader.GetName(columnOffset); @@ -219,6 +221,11 @@ private RowFactory0() {} columnOffset++; } + // Initialize remaining tokens to -1 (unmapped) + for (int i = tokenCount; i < tokens.Length; i++) + { + tokens[i] = -1; + } return null; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) @@ -234,6 +241,9 @@ private RowFactory0() {} case 1: result.Balance = GetValue(reader, columnOffset); break; + case -1: + // unmapped column, skip + break; } columnOffset++; diff --git a/version.json b/version.json index 24082eee..24ff5712 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.0", + "version": "1.0.59", "assemblyVersion": "1.0.0.0", "publicReleaseRefSpec": [ "^refs/heads/main$", From 2502e740b2d416db011c4912122a56bd10f49935 Mon Sep 17 00:00:00 2001 From: Devedse Date: Thu, 11 Dec 2025 01:50:59 +0100 Subject: [PATCH 10/23] Cleanup --- .../Interceptors/QueryMultiType.output.netfx.txt | 4 ---- test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt | 4 ---- version.json | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt delete mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt deleted file mode 100644 index 0d284670..00000000 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt +++ /dev/null @@ -1,4 +0,0 @@ -Generator produced 1 diagnostics: - -Hidden DAP000 L1 C1 -Dapper.AOT handled 4 of 4 possible call-sites using 4 interceptors, 0 commands and 1 readers diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt deleted file mode 100644 index 0d284670..00000000 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt +++ /dev/null @@ -1,4 +0,0 @@ -Generator produced 1 diagnostics: - -Hidden DAP000 L1 C1 -Dapper.AOT handled 4 of 4 possible call-sites using 4 interceptors, 0 commands and 1 readers diff --git a/version.json b/version.json index 24ff5712..24082eee 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.0.59", + "version": "1.0", "assemblyVersion": "1.0.0.0", "publicReleaseRefSpec": [ "^refs/heads/main$", From 7b29d7c58deb3547cc033b8f4b2f079e5483b990 Mon Sep 17 00:00:00 2001 From: Devedse Date: Thu, 11 Dec 2025 02:15:45 +0100 Subject: [PATCH 11/23] Refactor code structure for improved readability and maintainability --- .../DapperInterceptorGenerator.MultiMap.cs | 18 +- .../Internal/Inspection.cs | 4 +- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 1058 +++++++++++++++++ 3 files changed, 1074 insertions(+), 6 deletions(-) diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs index 8e3f91b9..12d3b731 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs @@ -69,9 +69,19 @@ static void WriteMultiMapImplementation( } sb.Append(")."); - // Call the appropriate QueryBuffered/QueryBufferedAsync method + // Call the appropriate QueryBuffered/QueryBufferedAsync/QueryUnbuffered/QueryUnbufferedAsync method bool isAsync = flags.HasAny(OperationFlags.Async); - sb.Append("QueryBuffered"); + bool isUnbuffered = flags.HasAny(OperationFlags.Unbuffered); + + if (isUnbuffered) + { + sb.Append("QueryUnbuffered"); + } + else + { + sb.Append("QueryBuffered"); + } + if (isAsync) { sb.Append("Async"); @@ -108,8 +118,8 @@ static void WriteMultiMapImplementation( sb.Append("\"Id\""); } - // Add rowCountHint if needed - if (flags.HasAny(OperationFlags.Query) && additionalCommandState is { HasRowCountHint: true }) + // Add rowCountHint if needed (only for buffered queries) + if (!isUnbuffered && flags.HasAny(OperationFlags.Query) && additionalCommandState is { HasRowCountHint: true }) { if (additionalCommandState.RowCountHintMemberName is null) { diff --git a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs index 7ecd5787..6064520a 100644 --- a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs +++ b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs @@ -1247,8 +1247,8 @@ public static bool IsDapperMethod(this IInvocationOperation operation, out Opera if (method.Arity > 1) { flags |= OperationFlags.MultiMap; - // Multi-map is only supported for 2-3 types (arity 3-4: T1, T2, TReturn or T1, T2, T3, TReturn) - if (method.Arity > 4) flags |= OperationFlags.NotAotSupported; + // Multi-map is supported for 2-7 types (arity 3-8: T1, T2, TReturn through T1, T2, T3, T4, T5, T6, T7, TReturn) + if (method.Arity > 8) flags |= OperationFlags.NotAotSupported; } break; case "QueryAsync": diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 9ace97a5..620fb9b6 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -295,4 +295,1062 @@ public async Task> QueryBufferedAsync( await state.DisposeAsync(); } } + + /// + /// Reads buffered rows from a multi-map query with 4 types + /// + public List QueryBuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + string splitOn = "Id", + int rowCountHint = 0) + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + List results; + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 3); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + results.Add(map(obj1, obj2, obj3, obj4)); + } + while (state.Reader.Read()); + state.Return(); + } + else + { + results = []; + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + return results; + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 4 types (async) + /// + public async Task> QueryBufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + string splitOn = "Id", + int rowCountHint = 0, + CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + List results; + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 3); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + results.Add(map(obj1, obj2, obj3, obj4)); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + else + { + results = []; + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + return results; + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 5 types + /// + public List QueryBuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + string splitOn = "Id", + int rowCountHint = 0) + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + List results; + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 4); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + results.Add(map(obj1, obj2, obj3, obj4, obj5)); + } + while (state.Reader.Read()); + state.Return(); + } + else + { + results = []; + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + return results; + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 5 types (async) + /// + public async Task> QueryBufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + string splitOn = "Id", + int rowCountHint = 0, + CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + List results; + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 4); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + results.Add(map(obj1, obj2, obj3, obj4, obj5)); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + else + { + results = []; + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + return results; + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 6 types + /// + public List QueryBuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + string splitOn = "Id", + int rowCountHint = 0) + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + List results; + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 5); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6)); + } + while (state.Reader.Read()); + state.Return(); + } + else + { + results = []; + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + return results; + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 6 types (async) + /// + public async Task> QueryBufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + string splitOn = "Id", + int rowCountHint = 0, + CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + List results; + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 5); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6)); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + else + { + results = []; + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + return results; + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 7 types + /// + public List QueryBuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + [DapperAot] RowFactory? factory7 = null, + string splitOn = "Id", + int rowCountHint = 0) + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + List results; + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 6); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6, obj7)); + } + while (state.Reader.Read()); + state.Return(); + } + else + { + results = []; + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + return results; + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads buffered rows from a multi-map query with 7 types (async) + /// + public async Task> QueryBufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + [DapperAot] RowFactory? factory7 = null, + string splitOn = "Id", + int rowCountHint = 0, + CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + List results; + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 6); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + + results = RowFactory.GetRowBuffer(rowCountHint); + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6, obj7)); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + else + { + results = []; + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + return results; + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 2 types + /// + public IEnumerable QueryUnbuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + string splitOn = "Id") + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + if (state.Reader.Read()) + { + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var split = FindSplit(state.Reader, splitOn, tokenState1); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + yield return map(obj1, obj2); + } + while (state.Reader.Read()); + state.Return(); + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 2 types (async) + /// + public async IAsyncEnumerable QueryUnbufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + string splitOn = "Id", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + if (await state.Reader.ReadAsync(cancellationToken)) + { + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var split = FindSplit(state.Reader, splitOn, tokenState1); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + yield return map(obj1, obj2); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 3 types + /// + public IEnumerable QueryUnbuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + string splitOn = "Id") + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 2); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + yield return map(obj1, obj2, obj3); + } + while (state.Reader.Read()); + state.Return(); + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 3 types (async) + /// + public async IAsyncEnumerable QueryUnbufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + string splitOn = "Id", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 2); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + yield return map(obj1, obj2, obj3); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 4 types + /// + public IEnumerable QueryUnbuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + string splitOn = "Id") + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 3); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + yield return map(obj1, obj2, obj3, obj4); + } + while (state.Reader.Read()); + state.Return(); + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 4 types (async) + /// + public async IAsyncEnumerable QueryUnbufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + string splitOn = "Id", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 3); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + yield return map(obj1, obj2, obj3, obj4); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 5 types + /// + public IEnumerable QueryUnbuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + string splitOn = "Id") + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 4); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + yield return map(obj1, obj2, obj3, obj4, obj5); + } + while (state.Reader.Read()); + state.Return(); + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 5 types (async) + /// + public async IAsyncEnumerable QueryUnbufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + string splitOn = "Id", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 4); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + yield return map(obj1, obj2, obj3, obj4, obj5); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 6 types + /// + public IEnumerable QueryUnbuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + string splitOn = "Id") + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 5); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + yield return map(obj1, obj2, obj3, obj4, obj5, obj6); + } + while (state.Reader.Read()); + state.Return(); + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 6 types (async) + /// + public async IAsyncEnumerable QueryUnbufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + string splitOn = "Id", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 5); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + yield return map(obj1, obj2, obj3, obj4, obj5, obj6); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + } + finally + { + await state.DisposeAsync(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 7 types + /// + public IEnumerable QueryUnbuffered( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + [DapperAot] RowFactory? factory7 = null, + string splitOn = "Id") + { + SyncQueryState state = default; + try + { + state.ExecuteReader(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess); + + if (state.Reader.Read()) + { + var splits = FindSplits(state.Reader, splitOn, 6); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + yield return map(obj1, obj2, obj3, obj4, obj5, obj6, obj7); + } + while (state.Reader.Read()); + state.Return(); + } + + while (state.Reader.NextResult()) { } + PostProcessAndRecycle(ref state, args, state.Reader.CloseAndCapture()); + } + finally + { + state.Dispose(); + } + } + + /// + /// Reads unbuffered rows from a multi-map query with 7 types (async) + /// + public async IAsyncEnumerable QueryUnbufferedAsync( + TArgs args, + Func map, + [DapperAot] RowFactory? factory1 = null, + [DapperAot] RowFactory? factory2 = null, + [DapperAot] RowFactory? factory3 = null, + [DapperAot] RowFactory? factory4 = null, + [DapperAot] RowFactory? factory5 = null, + [DapperAot] RowFactory? factory6 = null, + [DapperAot] RowFactory? factory7 = null, + string splitOn = "Id", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + AsyncQueryState state = new(); + try + { + cancellationToken = GetCancellationToken(args, cancellationToken); + await state.ExecuteReaderAsync(GetCommand(args), CommandBehavior.SingleResult | CommandBehavior.SequentialAccess, cancellationToken); + + if (await state.Reader.ReadAsync(cancellationToken)) + { + var splits = FindSplits(state.Reader, splitOn, 6); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + + do + { + var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + yield return map(obj1, obj2, obj3, obj4, obj5, obj6, obj7); + } + while (await state.Reader.ReadAsync(cancellationToken)); + state.Return(); + } + + while (await state.Reader.NextResultAsync(cancellationToken)) { } + PostProcessAndRecycle(state, args, await state.Reader.CloseAndCaptureAsync()); + } + finally + { + await state.DisposeAsync(); + } + } } From 29210e8b373cede9397e08340cc773d13912d962 Mon Sep 17 00:00:00 2001 From: Devedse Date: Fri, 12 Dec 2025 21:01:12 +0100 Subject: [PATCH 12/23] Cleanup --- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 43 +++--------------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 620fb9b6..06e9289e 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -30,7 +30,7 @@ public List QueryBuffered( if (state.Reader.Read()) { var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var split = FindSplit(state.Reader, splitOn, tokenState1); + var split = FindSplits(state.Reader, splitOn, 1)[0]; var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); results = RowFactory.GetRowBuffer(rowCountHint); @@ -80,9 +80,9 @@ public async Task> QueryBufferedAsync( List results; if (await state.Reader.ReadAsync(cancellationToken)) { + var splits = FindSplits(state.Reader, splitOn, 1); var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var split = FindSplit(state.Reader, splitOn, tokenState1); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); results = RowFactory.GetRowBuffer(rowCountHint); do @@ -110,39 +110,6 @@ public async Task> QueryBufferedAsync( } } - /// - /// Finds the column index where the split should occur based on column name - /// - private static int FindSplit(System.Data.Common.DbDataReader reader, string splitOn, object tokenState) - { - // Default split is after finding the first occurrence of the splitOn column - var fieldCount = reader.FieldCount; -#if NET5_0_OR_GREATER - var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); -#else - var splitColumns = splitOn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - for (int idx = 0; idx < splitColumns.Length; idx++) - { - splitColumns[idx] = splitColumns[idx].Trim(); - } -#endif - - for (int i = 0; i < fieldCount; i++) - { - var name = reader.GetName(i); - foreach (var split in splitColumns) - { - if (StringHashing.NormalizedEquals(name, split)) - { - return i; - } - } - } - - // If not found, split in the middle - return fieldCount / 2; - } - /// /// Finds multiple split points based on splitOn column names /// @@ -790,7 +757,7 @@ public IEnumerable QueryUnbuffered( if (state.Reader.Read()) { var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var split = FindSplit(state.Reader, splitOn, tokenState1); + var split = FindSplits(state.Reader, splitOn, 1)[0]; var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); do @@ -832,7 +799,7 @@ public async IAsyncEnumerable QueryUnbufferedAsync( if (await state.Reader.ReadAsync(cancellationToken)) { var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var split = FindSplit(state.Reader, splitOn, tokenState1); + var split = FindSplits(state.Reader, splitOn, 1)[0]; var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); do From 0234f3d2cc6a80180752569d912e303873257cc3 Mon Sep 17 00:00:00 2001 From: Devedse Date: Fri, 12 Dec 2025 21:03:24 +0100 Subject: [PATCH 13/23] Fix split index usage in QueryBuffered method to correctly read second object --- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 06e9289e..614f1846 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -88,7 +88,7 @@ public async Task> QueryBufferedAsync( do { var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); results.Add(map(obj1, obj2)); } while (await state.Reader.ReadAsync(cancellationToken)); From ca820c004592b2af0572f09643082c01b1f91b04 Mon Sep 17 00:00:00 2001 From: Devedse Date: Fri, 12 Dec 2025 21:09:02 +0100 Subject: [PATCH 14/23] Fix Query method signature in DAP001 verifier to support additional generic parameters --- test/Dapper.AOT.Test/Verifiers/DAP001.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Dapper.AOT.Test/Verifiers/DAP001.cs b/test/Dapper.AOT.Test/Verifiers/DAP001.cs index 3f79e36d..ec350165 100644 --- a/test/Dapper.AOT.Test/Verifiers/DAP001.cs +++ b/test/Dapper.AOT.Test/Verifiers/DAP001.cs @@ -17,11 +17,11 @@ class SomeCode { public void Foo(DbConnection conn) { - _ = conn.{|#0:Query|}("proc", null!); + _ = conn.{|#0:Query|}("proc", null!); _ = conn.Query("proc"); } } """, DefaultConfig, [Diagnostic(Diagnostics.UnsupportedMethod).WithLocation(0) - .WithArguments("SqlMapper.Query(IDbConnection, string, Func, object?, IDbTransaction?, bool, string, int?, CommandType?)")]); + .WithArguments("SqlMapper.Query(IDbConnection, string, Func, object?, IDbTransaction?, bool, string, int?, CommandType?)")]); } \ No newline at end of file From 818c774c2673bc7cb448fcbb3dac33e2ec4f2aa8 Mon Sep 17 00:00:00 2001 From: Devedse Date: Fri, 12 Dec 2025 21:22:23 +0100 Subject: [PATCH 15/23] Refactor FindSplits method to improve clarity and maintainability --- .../DapperInterceptorGenerator.MultiMap.cs | 3 - src/Dapper.AOT/CommandT.QueryMultiMap.cs | 99 ++++++++++--------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs index 12d3b731..53b5d56f 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.MultiMap.cs @@ -23,9 +23,6 @@ static void WriteMultiMapImplementation( var typeArgs = method.TypeArguments; var arity = typeArgs.Length; - // The return type is the last type argument - var returnType = typeArgs[arity - 1]; - sb.Append("return "); if (flags.HasAll(OperationFlags.Async | OperationFlags.Query | OperationFlags.Buffered)) { diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 614f1846..39ed4fe2 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -10,6 +10,56 @@ namespace Dapper; partial struct Command { + /// + /// Finds multiple split points based on splitOn column names + /// + private static int[] FindSplits(System.Data.Common.DbDataReader reader, string splitOn, int count) + { + var splits = new int[count]; + var fieldCount = reader.FieldCount; +#if NET5_0_OR_GREATER + var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); +#else + var splitColumns = splitOn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int idx = 0; idx < splitColumns.Length; idx++) + { + splitColumns[idx] = splitColumns[idx].Trim(); + } +#endif + + int splitIndex = 0; + int lastSplit = 0; + + for (int i = 0; i < fieldCount && splitIndex < count; i++) + { + var name = reader.GetName(i); + foreach (var split in splitColumns) + { + if (StringHashing.NormalizedEquals(name, split)) + { + splits[splitIndex++] = i; + lastSplit = i; + break; + } + } + } + + // Fill remaining splits evenly if not all found + if (splitIndex < count) + { + var remaining = count - splitIndex; + var step = (fieldCount - lastSplit) / (remaining + 1); + for (int i = 0; i < remaining; i++) + { + lastSplit += step; + splits[splitIndex++] = lastSplit; + } + } + + return splits; + } + + /// /// Reads buffered rows from a multi-map query with 2 types /// @@ -110,55 +160,6 @@ public async Task> QueryBufferedAsync( } } - /// - /// Finds multiple split points based on splitOn column names - /// - private static int[] FindSplits(System.Data.Common.DbDataReader reader, string splitOn, int count) - { - var splits = new int[count]; - var fieldCount = reader.FieldCount; -#if NET5_0_OR_GREATER - var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); -#else - var splitColumns = splitOn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - for (int idx = 0; idx < splitColumns.Length; idx++) - { - splitColumns[idx] = splitColumns[idx].Trim(); - } -#endif - - int splitIndex = 0; - int lastSplit = 0; - - for (int i = 0; i < fieldCount && splitIndex < count; i++) - { - var name = reader.GetName(i); - foreach (var split in splitColumns) - { - if (StringHashing.NormalizedEquals(name, split)) - { - splits[splitIndex++] = i; - lastSplit = i; - break; - } - } - } - - // Fill remaining splits evenly if not all found - if (splitIndex < count) - { - var remaining = count - splitIndex; - var step = (fieldCount - lastSplit) / (remaining + 1); - for (int i = 0; i < remaining; i++) - { - lastSplit += step; - splits[splitIndex++] = lastSplit; - } - } - - return splits; - } - /// /// Reads buffered rows from a multi-map query with 3 types /// From 132b1261b09c0d70271cf29c9ae6646c02566ea9 Mon Sep 17 00:00:00 2001 From: Devedse Date: Fri, 12 Dec 2025 23:47:02 +0100 Subject: [PATCH 16/23] Refactor interceptors to improve token handling and eliminate unmapped column skips - Updated interceptor methods across multiple files to replace foreach loops with for loops for better performance and clarity. - Changed return statements from null to tokenCount in Read methods to ensure proper handling of token counts. - Removed unnecessary comments regarding unmapped columns, streamlining the code. - Added new query methods (Query2 to Query5 and QueryAsync6 to QueryAsync11) to enhance multi-mapping capabilities for the Foo.Customer type. - Ensured all changes maintain the integrity of the Dapper.AOT functionality while improving readability and maintainability. --- .../DapperInterceptorGenerator.cs | 22 +- .../Interceptors/Cancellation.output.cs | 9 +- .../Interceptors/Cancellation.output.netfx.cs | 9 +- .../Interceptors/ColumnAttribute.output.cs | 9 +- .../ColumnAttribute.output.netfx.cs | 9 +- .../Interceptors/CommandProperties.output.cs | 9 +- .../CommandProperties.output.netfx.cs | 9 +- .../Interceptors/DateOnly.net6.output.cs | 9 +- .../DateOnly.net6.output.netfx.cs | 190 ------------------ .../DateOnly.net6.output.netfx.txt | 52 ----- .../Interceptors/DbString.output.cs | 9 +- .../Interceptors/DbString.output.netfx.cs | 9 +- .../Interceptors/GetRowParser.output.cs | 9 +- .../Interceptors/GetRowParser.output.netfx.cs | 9 +- .../Interceptors/GlobalFetchSize.output.cs | 9 +- .../GlobalFetchSize.output.netfx.cs | 9 +- .../Interceptors/InheritedMembers.output.cs | 9 +- .../InheritedMembers.output.netfx.cs | 9 +- .../Interceptors/MiscDiagnostics.output.cs | 27 ++- .../MiscDiagnostics.output.netfx.cs | 27 ++- .../Interceptors/NonFactoryMethod.output.cs | 9 +- .../NonFactoryMethod.output.netfx.cs | 9 +- .../Interceptors/Query.output.cs | 9 +- .../Interceptors/Query.output.netfx.cs | 9 +- ...ustomConstructionWithConstructor.output.cs | 108 +++++----- ...onstructionWithConstructor.output.netfx.cs | 108 +++++----- ...tomConstructionWithFactoryMethod.output.cs | 36 ++-- ...structionWithFactoryMethod.output.netfx.cs | 36 ++-- .../Interceptors/QueryDetection.output.cs | 9 +- .../QueryDetection.output.netfx.cs | 9 +- .../Interceptors/QueryMultiType.output.cs | 129 +++++++++++- .../QueryMultiType.output.netfx.cs | 129 +++++++++++- .../QueryMultiType.output.netfx.txt | 4 + .../Interceptors/QueryMultiType.output.txt | 4 + .../Interceptors/QueryStrictBind.output.cs | 30 ++- .../QueryStrictBind.output.netfx.cs | 30 ++- .../Interceptors/RequiredProperties.output.cs | 9 +- .../RequiredProperties.output.netfx.cs | 9 +- .../Interceptors/RowCountHint.output.cs | 9 +- .../Interceptors/RowCountHint.output.netfx.cs | 9 +- .../Interceptors/Single.output.cs | 9 +- .../Interceptors/Single.output.netfx.cs | 9 +- .../Interceptors/Techempower.output.cs | 9 +- .../Interceptors/Techempower.output.netfx.cs | 9 +- .../Interceptors/TopLevelStatements.output.cs | 9 +- .../TopLevelStatements.output.netfx.cs | 9 +- .../Interceptors/TsqlTips.output.cs | 9 +- .../Interceptors/TsqlTips.output.netfx.cs | 9 +- 48 files changed, 570 insertions(+), 659 deletions(-) delete mode 100644 test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.cs delete mode 100644 test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.txt create mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt create mode 100644 test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index e7a1fa88..e8fca305 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -860,7 +860,8 @@ void WriteTokenizeMethod() .Outdent().NewLine() .Append("// Initialize remaining tokens to -1 (unmapped)").NewLine() .Append("for (int i = tokenCount; i < tokens.Length; i++)").Indent().NewLine() - .Append("tokens[i] = -1;").Outdent().NewLine(); + .Append("tokens[i] = -1;").Outdent().NewLine() + .Append("return tokenCount;").Outdent().NewLine(); } else { @@ -868,6 +869,7 @@ void WriteTokenizeMethod() if (flags.HasAny(OperationFlags.StrictTypes)) { sb.Append("// (no mapping applied for strict types and pre-defined columns)").NewLine(); + sb.Append("return null;").Outdent().NewLine(); } else { @@ -890,11 +892,10 @@ void WriteTokenizeMethod() .Append("columnOffset++;").Outdent().NewLine() .Append("// Initialize remaining tokens to -1 (unmapped)").NewLine() .Append("for (int i = tokenCount; i < tokens.Length; i++)").Indent().NewLine() - .Append("tokens[i] = -1;").Outdent().NewLine(); + .Append("tokens[i] = -1;").Outdent().NewLine() + .Append("return tokenCount;").Outdent().NewLine(); } } - - sb.Append("return null;").Outdent().NewLine(); } void WriteReadMethod(in GenerateState context) { @@ -950,13 +951,15 @@ void WriteReadMethod(in GenerateState context) { // no mapping involved - simple ordinal iteration sb.Append("int lim = global::System.Math.Min(tokens.Length, ").Append(queryColumns.Length).Append(");").NewLine() - .Append("for (int token = 0; token < lim; token++) // query-columns predefined"); + .Append("for (int token = 0; token < lim; token++) // query-columns predefined").Indent().NewLine(); } else { - sb.Append("foreach (var token in tokens)"); + sb.Append("int tokenCount = state is int count ? count : tokens.Length;").NewLine() + .Append("for (int i = 0; i < tokenCount; i++)").Indent().NewLine() + .Append("var token = tokens[i];").NewLine(); } - sb.Indent().NewLine().Append("switch (token)").Indent().NewLine(); + sb.Append("switch (token)").Indent().NewLine(); token = 0; foreach (var member in members) @@ -1006,11 +1009,6 @@ void WriteReadMethod(in GenerateState context) token++; } - // Handle unmapped columns (token = -1) - sb.Append("case -1:").NewLine().Indent(false) - .Append("// unmapped column, skip").NewLine() - .Append("break;").NewLine().Outdent(false); - sb.Outdent().NewLine().Append("columnOffset++;").NewLine().Outdent().NewLine(); if (useDeferredConstruction) diff --git a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs index 26b3e0c1..1dc0aa02 100644 --- a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs @@ -82,13 +82,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MyType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -97,9 +99,6 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs index 26b3e0c1..1dc0aa02 100644 --- a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs @@ -82,13 +82,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MyType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -97,9 +99,6 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs index 17c8256b..05d23a0f 100644 --- a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs @@ -79,13 +79,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MyType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -118,9 +120,6 @@ private RowFactory0() {} case 9: result.E = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs index 17c8256b..05d23a0f 100644 --- a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs @@ -79,13 +79,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MyType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -118,9 +120,6 @@ private RowFactory0() {} case 9: result.E = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs index 931584ab..0b81f09a 100644 --- a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs @@ -140,13 +140,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -167,9 +169,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs index 931584ab..0b81f09a 100644 --- a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs @@ -140,13 +140,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -167,9 +169,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs index 2fae9e77..e844c648 100644 --- a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs @@ -91,13 +91,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.User Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.User result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -118,9 +120,6 @@ private RowFactory0() {} case 5: result.BirthDate = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.cs deleted file mode 100644 index c1abf85c..00000000 --- a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.cs +++ /dev/null @@ -1,190 +0,0 @@ -#nullable enable -namespace Dapper.AOT // interceptors must be in a known namespace -{ - file static class DapperGeneratedInterceptors - { - [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\DateOnly.net6.input.cs", 12, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType) - { - // Query, Async, TypedResult, HasParameters, Buffered, Text, BindResultsByName, KnownParameters - // takes parameter: - // parameter map: BirthDate - // returns data: global::Foo.User - global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); - global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.Text); - global::System.Diagnostics.Debug.Assert(param is not null); - - return global::Dapper.DapperAotExtensions.AsEnumerableAsync( - global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.Text, commandTimeout.GetValueOrDefault(), CommandFactory0.Instance).QueryBufferedAsync(param, RowFactory0.Instance)); - - } - - [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\DateOnly.net6.input.cs", 17, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync1(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType) - { - // Query, Async, TypedResult, HasParameters, Buffered, Text, BindResultsByName, KnownParameters - // takes parameter: global::Foo.QueryModel - // parameter map: BirthDate - // returns data: global::Foo.User - global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); - global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.Text); - global::System.Diagnostics.Debug.Assert(param is not null); - - return global::Dapper.DapperAotExtensions.AsEnumerableAsync( - global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.Text, commandTimeout.GetValueOrDefault(), CommandFactory1.Instance).QueryBufferedAsync((global::Foo.QueryModel)param!, RowFactory0.Instance)); - - } - - private class CommonCommandFactory : global::Dapper.CommandFactory - { - public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) - { - var cmd = base.GetCommand(connection, sql, commandType, args); - // apply special per-provider command initialization logic for OracleCommand - if (cmd is global::Oracle.ManagedDataAccess.Client.OracleCommand cmd0) - { - cmd0.BindByName = true; - cmd0.InitialLONGFetchSize = -1; - - } - return cmd; - } - - } - - private static readonly CommonCommandFactory DefaultCommandFactory = new(); - - private sealed class RowFactory0 : global::Dapper.RowFactory - { - internal static readonly RowFactory0 Instance = new(); - private RowFactory0() {} - public override object? Tokenize(global::System.Data.Common.DbDataReader reader, global::System.Span tokens, int columnOffset) - { - for (int i = 0; i < tokens.Length; i++) - { - int token = -1; - var name = reader.GetName(columnOffset); - var type = reader.GetFieldType(columnOffset); - switch (NormalizedHash(name)) - { - case 926444256U when NormalizedEquals(name, "id"): - token = type == typeof(int) ? 0 : 3; // two tokens for right-typed and type-flexible - break; - case 2369371622U when NormalizedEquals(name, "name"): - token = type == typeof(string) ? 1 : 4; - break; - case 4237030186U when NormalizedEquals(name, "birthdate"): - token = type == typeof(DateOnly) ? 2 : 5; - break; - - } - tokens[i] = token; - columnOffset++; - - } - return null; - } - public override global::Foo.User Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) - { - global::Foo.User result = new(); - foreach (var token in tokens) - { - switch (token) - { - case 0: - result.Id = reader.GetInt32(columnOffset); - break; - case 3: - result.Id = GetValue(reader, columnOffset); - break; - case 1: - result.Name = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); - break; - case 4: - result.Name = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); - break; - case 2: - result.BirthDate = reader.IsDBNull(columnOffset) ? (DateOnly?)null : reader.GetFieldValue(columnOffset); - break; - case 5: - result.BirthDate = reader.IsDBNull(columnOffset) ? (DateOnly?)null : GetValue(reader, columnOffset); - break; - - } - columnOffset++; - - } - return result; - - } - - } - - private sealed class CommandFactory0 : CommonCommandFactory // - { - internal static readonly CommandFactory0 Instance = new(); - public override void AddParameters(in global::Dapper.UnifiedCommand cmd, object? args) - { - var typed = Cast(args, static () => new { BirthDate = default()! }); // expected shape - var ps = cmd.Parameters; - global::System.Data.Common.DbParameter p; - p = cmd.CreateParameter(); - p.ParameterName = "BirthDate"; - p.Direction = global::System.Data.ParameterDirection.Input; - p.Value = AsValue(typed.BirthDate); - ps.Add(p); - - } - public override void UpdateParameters(in global::Dapper.UnifiedCommand cmd, object? args) - { - var typed = Cast(args, static () => new { BirthDate = default()! }); // expected shape - var ps = cmd.Parameters; - ps[0].Value = AsValue(typed.BirthDate); - - } - - } - - private sealed class CommandFactory1 : CommonCommandFactory - { - internal static readonly CommandFactory1 Instance = new(); - public override void AddParameters(in global::Dapper.UnifiedCommand cmd, global::Foo.QueryModel args) - { - var ps = cmd.Parameters; - global::System.Data.Common.DbParameter p; - p = cmd.CreateParameter(); - p.ParameterName = "BirthDate"; - p.Direction = global::System.Data.ParameterDirection.Input; - p.Value = AsValue(args.BirthDate); - ps.Add(p); - - } - public override void UpdateParameters(in global::Dapper.UnifiedCommand cmd, global::Foo.QueryModel args) - { - var ps = cmd.Parameters; - ps[0].Value = AsValue(args.BirthDate); - - } - - } - - - } -} -namespace System.Runtime.CompilerServices -{ - // this type is needed by the compiler to implement interceptors - it doesn't need to - // come from the runtime itself, though - - [global::System.Diagnostics.Conditional("DEBUG")] // not needed post-build, so: evaporate - [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] - sealed file class InterceptsLocationAttribute : global::System.Attribute - { - public InterceptsLocationAttribute(string path, int lineNumber, int columnNumber) - { - _ = path; - _ = lineNumber; - _ = columnNumber; - } - } -} \ No newline at end of file diff --git a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.txt b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.txt deleted file mode 100644 index 5ba1f0dd..00000000 --- a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.netfx.txt +++ /dev/null @@ -1,52 +0,0 @@ -Input code has 4 diagnostics from 'Interceptors/DateOnly.net6.input.cs': - -Error CS0103 Interceptors/DateOnly.net6.input.cs L14 C25 -The name 'DateOnly' does not exist in the current context - -Error CS0103 Interceptors/DateOnly.net6.input.cs L19 C25 -The name 'DateOnly' does not exist in the current context - -Error CS0246 Interceptors/DateOnly.net6.input.cs L25 C16 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) - -Error CS0246 Interceptors/DateOnly.net6.input.cs L32 C16 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) -Generator produced 1 diagnostics: - -Hidden DAP000 L1 C1 -Dapper.AOT handled 2 of 2 possible call-sites using 2 interceptors, 2 commands and 1 readers -Output code has 4 diagnostics from 'Interceptors/DateOnly.net6.input.cs': - -Error CS0103 Interceptors/DateOnly.net6.input.cs L14 C25 -The name 'DateOnly' does not exist in the current context - -Error CS0103 Interceptors/DateOnly.net6.input.cs L19 C25 -The name 'DateOnly' does not exist in the current context - -Error CS0246 Interceptors/DateOnly.net6.input.cs L25 C16 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) - -Error CS0246 Interceptors/DateOnly.net6.input.cs L32 C16 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) -Output code has 7 diagnostics from 'Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs': - -Error CS0246 Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs L77 C52 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) - -Error CS0246 Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs L107 C81 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) - -Error CS0246 Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs L107 C119 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) - -Error CS0246 Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs L110 C81 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) - -Error CS0246 Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs L110 C107 -The type or namespace name 'DateOnly' could not be found (are you missing a using directive or an assembly reference?) - -Error CS1031 Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs L128 C79 -Type expected - -Error CS1031 Dapper.AOT.Analyzers/Dapper.CodeAnalysis.DapperInterceptorGenerator/Test.generated.cs L140 C79 -Type expected diff --git a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs index 5309b48b..0c7c7518 100644 --- a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs @@ -91,13 +91,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Product result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -118,9 +120,6 @@ private RowFactory0() {} case 5: result.ProductNumber = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs index 5309b48b..0c7c7518 100644 --- a/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs @@ -91,13 +91,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Product result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -118,9 +120,6 @@ private RowFactory0() {} case 5: result.ProductNumber = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs index d30aff0f..2550bbc8 100644 --- a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs @@ -78,13 +78,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::HazNameId Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::HazNameId result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -99,9 +101,6 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs index d30aff0f..2550bbc8 100644 --- a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs @@ -78,13 +78,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::HazNameId Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::HazNameId result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -99,9 +101,6 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs index 2388e395..2b0655da 100644 --- a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs @@ -69,13 +69,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeApp.SomeQueryType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeApp.SomeQueryType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -84,9 +86,6 @@ private RowFactory0() {} case 1: result.Dummy = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs index 2388e395..2b0655da 100644 --- a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs @@ -69,13 +69,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeApp.SomeQueryType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeApp.SomeQueryType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -84,9 +86,6 @@ private RowFactory0() {} case 1: result.Dummy = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs index f126ca82..12defb8c 100644 --- a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs @@ -85,13 +85,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Entity1 Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Entity1 result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -106,9 +108,6 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs index f126ca82..12defb8c 100644 --- a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs @@ -85,13 +85,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Entity1 Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Entity1 result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -106,9 +108,6 @@ private RowFactory0() {} case 3: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs index 8d13c9a6..548a883c 100644 --- a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs @@ -158,13 +158,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeCode.InternalNesting.SomePublicType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeCode.InternalNesting.SomePublicType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -173,9 +175,6 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -216,13 +215,15 @@ private RowFactory1() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeCode.InternalNesting.SomeInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeCode.InternalNesting.SomeInternalType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -231,9 +232,6 @@ private RowFactory1() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -274,13 +272,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeCode.InternalNesting.SomeProtectedInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeCode.InternalNesting.SomeProtectedInternalType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -289,9 +289,6 @@ private RowFactory2() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs index 8d13c9a6..548a883c 100644 --- a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs @@ -158,13 +158,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeCode.InternalNesting.SomePublicType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeCode.InternalNesting.SomePublicType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -173,9 +175,6 @@ private RowFactory0() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -216,13 +215,15 @@ private RowFactory1() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeCode.InternalNesting.SomeInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeCode.InternalNesting.SomeInternalType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -231,9 +232,6 @@ private RowFactory1() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -274,13 +272,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeCode.InternalNesting.SomeProtectedInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::SomeCode.InternalNesting.SomeProtectedInternalType result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -289,9 +289,6 @@ private RowFactory2() {} case 1: result.Id = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs index da0f2aa9..934e3348 100644 --- a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs @@ -154,13 +154,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::UsageLinker.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::UsageLinker.Product result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -313,9 +315,6 @@ private RowFactory0() {} case 49: result.ModifiedDate = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs index da0f2aa9..934e3348 100644 --- a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs @@ -154,13 +154,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::UsageLinker.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::UsageLinker.Product result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -313,9 +315,6 @@ private RowFactory0() {} case 49: result.ModifiedDate = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Query.output.cs b/test/Dapper.AOT.Test/Interceptors/Query.output.cs index 726d37db..6e2dda16 100644 --- a/test/Dapper.AOT.Test/Interceptors/Query.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Query.output.cs @@ -192,13 +192,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -219,9 +221,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs index 5b64c540..b5dddcd8 100644 --- a/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs @@ -164,13 +164,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -191,9 +193,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs index 18fbca73..6ef78cab 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs @@ -227,13 +227,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.ParameterlessCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.ParameterlessCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -254,9 +256,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -303,15 +302,17 @@ private RowFactory1() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.GetOnlyPropertiesViaConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -332,9 +333,6 @@ private RowFactory1() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -379,13 +377,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordClass Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.RecordClass result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -406,9 +406,6 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -455,15 +452,17 @@ private RowFactory3() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordClassSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -484,9 +483,6 @@ private RowFactory3() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -531,13 +527,15 @@ private RowFactory4() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordStruct Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.RecordStruct result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -558,9 +556,6 @@ private RowFactory4() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -607,15 +602,17 @@ private RowFactory5() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordStructSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -636,9 +633,6 @@ private RowFactory5() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -683,15 +677,17 @@ private RowFactory6() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.InitPropsOnly Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -712,9 +708,6 @@ private RowFactory6() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -764,15 +757,17 @@ private RowFactory7() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.InitPropsAndDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -793,9 +788,6 @@ private RowFactory7() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -844,13 +836,15 @@ private RowFactory8() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.OnlyNonDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.OnlyNonDapperAotCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -871,9 +865,6 @@ private RowFactory8() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -920,13 +911,15 @@ private RowFactory9() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.SingleDefaultCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.SingleDefaultCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -947,9 +940,6 @@ private RowFactory9() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -996,13 +986,15 @@ private RowFactory10() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MultipleDapperAotCtors Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MultipleDapperAotCtors result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -1023,9 +1015,6 @@ private RowFactory10() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -1072,13 +1061,15 @@ private RowFactory11() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.SingleDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.SingleDapperAotCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -1099,9 +1090,6 @@ private RowFactory11() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs index 18fbca73..6ef78cab 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs @@ -227,13 +227,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.ParameterlessCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.ParameterlessCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -254,9 +256,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -303,15 +302,17 @@ private RowFactory1() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.GetOnlyPropertiesViaConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -332,9 +333,6 @@ private RowFactory1() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -379,13 +377,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordClass Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.RecordClass result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -406,9 +406,6 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -455,15 +452,17 @@ private RowFactory3() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordClassSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -484,9 +483,6 @@ private RowFactory3() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -531,13 +527,15 @@ private RowFactory4() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordStruct Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.RecordStruct result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -558,9 +556,6 @@ private RowFactory4() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -607,15 +602,17 @@ private RowFactory5() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.RecordStructSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -636,9 +633,6 @@ private RowFactory5() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -683,15 +677,17 @@ private RowFactory6() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.InitPropsOnly Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -712,9 +708,6 @@ private RowFactory6() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -764,15 +757,17 @@ private RowFactory7() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.InitPropsAndDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -793,9 +788,6 @@ private RowFactory7() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -844,13 +836,15 @@ private RowFactory8() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.OnlyNonDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.OnlyNonDapperAotCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -871,9 +865,6 @@ private RowFactory8() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -920,13 +911,15 @@ private RowFactory9() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.SingleDefaultCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.SingleDefaultCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -947,9 +940,6 @@ private RowFactory9() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -996,13 +986,15 @@ private RowFactory10() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MultipleDapperAotCtors Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MultipleDapperAotCtors result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -1023,9 +1015,6 @@ private RowFactory10() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -1072,13 +1061,15 @@ private RowFactory11() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.SingleDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.SingleDapperAotCtor result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -1099,9 +1090,6 @@ private RowFactory11() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs index 2c5826ef..9dece7a8 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs @@ -115,15 +115,17 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.PublicPropertiesNoConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -144,9 +146,6 @@ private RowFactory0() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -191,13 +190,15 @@ private RowFactory1() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MultipleDapperAotFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MultipleDapperAotFactoryMethods result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -218,9 +219,6 @@ private RowFactory1() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -267,13 +265,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.SingleFactoryNotMarkedWithDapperAot Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.SingleFactoryNotMarkedWithDapperAot result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -294,9 +294,6 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -343,13 +340,15 @@ private RowFactory3() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MultipleStandardFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MultipleStandardFactoryMethods result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -370,9 +369,6 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs index 2c5826ef..9dece7a8 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs @@ -115,15 +115,17 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.PublicPropertiesNoConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; double? value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -144,9 +146,6 @@ private RowFactory0() {} case 5: value2 = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -191,13 +190,15 @@ private RowFactory1() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MultipleDapperAotFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MultipleDapperAotFactoryMethods result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -218,9 +219,6 @@ private RowFactory1() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -267,13 +265,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.SingleFactoryNotMarkedWithDapperAot Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.SingleFactoryNotMarkedWithDapperAot result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -294,9 +294,6 @@ private RowFactory2() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -343,13 +340,15 @@ private RowFactory3() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.MultipleStandardFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.MultipleStandardFactoryMethods result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -370,9 +369,6 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs index e32c166a..8c16046b 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs @@ -155,13 +155,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -176,9 +178,6 @@ private RowFactory0() {} case 3: result.Name = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs index e32c166a..8c16046b 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs @@ -155,13 +155,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -176,9 +178,6 @@ private RowFactory0() {} case 3: result.Name = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs index 4a30871b..cbab64c9 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs @@ -33,8 +33,64 @@ file static class DapperGeneratedInterceptors } + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 14, 24)] + internal static global::System.Collections.Generic.IEnumerable Query2(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 15, 24)] + internal static global::System.Collections.Generic.IEnumerable Query3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 16, 24)] + internal static global::System.Collections.Generic.IEnumerable Query4(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 17, 24)] + internal static global::System.Collections.Generic.IEnumerable Query5(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 19, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync2(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + internal static global::System.Threading.Tasks.Task> QueryAsync6(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) { // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap // returns data: global::Foo.Customer @@ -49,7 +105,7 @@ file static class DapperGeneratedInterceptors } [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + internal static global::System.Threading.Tasks.Task> QueryAsync7(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) { // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap // returns data: global::Foo.Customer @@ -63,6 +119,66 @@ file static class DapperGeneratedInterceptors } + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 21, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync8(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 22, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync9(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 23, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync10(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 24, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync11(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + private class CommonCommandFactory : global::Dapper.CommandFactory { public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) @@ -117,13 +233,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -144,9 +262,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs index 4a30871b..cbab64c9 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs @@ -33,8 +33,64 @@ file static class DapperGeneratedInterceptors } + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 14, 24)] + internal static global::System.Collections.Generic.IEnumerable Query2(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 15, 24)] + internal static global::System.Collections.Generic.IEnumerable Query3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 16, 24)] + internal static global::System.Collections.Generic.IEnumerable Query4(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 17, 24)] + internal static global::System.Collections.Generic.IEnumerable Query5(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn); + + } + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 19, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync2(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + internal static global::System.Threading.Tasks.Task> QueryAsync6(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) { // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap // returns data: global::Foo.Customer @@ -49,7 +105,7 @@ file static class DapperGeneratedInterceptors } [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 20, 30)] - internal static global::System.Threading.Tasks.Task> QueryAsync3(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + internal static global::System.Threading.Tasks.Task> QueryAsync7(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) { // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap // returns data: global::Foo.Customer @@ -63,6 +119,66 @@ file static class DapperGeneratedInterceptors } + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 21, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync8(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 22, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync9(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 23, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync10(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\QueryMultiType.input.cs", 24, 30)] + internal static global::System.Threading.Tasks.Task> QueryAsync11(this global::System.Data.IDbConnection cnn, string sql, global::System.Func map, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, string splitOn, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, Async, TypedResult, Buffered, StoredProcedure, BindResultsByName, MultiMap + // returns data: global::Foo.Customer + global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(sql)); + global::System.Diagnostics.Debug.Assert((commandType ?? global::Dapper.DapperAotExtensions.GetCommandType(sql)) == global::System.Data.CommandType.StoredProcedure); + global::System.Diagnostics.Debug.Assert(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.AsEnumerableAsync( + global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.StoredProcedure, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBufferedAsync(param, map, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, RowFactory0.Instance, splitOn)); + + } + private class CommonCommandFactory : global::Dapper.CommandFactory { public override global::System.Data.Common.DbCommand GetCommand(global::System.Data.Common.DbConnection connection, string sql, global::System.Data.CommandType commandType, T args) @@ -117,13 +233,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -144,9 +262,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt new file mode 100644 index 00000000..4a9f05b6 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.txt @@ -0,0 +1,4 @@ +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 12 of 12 possible call-sites using 12 interceptors, 0 commands and 1 readers diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt new file mode 100644 index 00000000..4a9f05b6 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.txt @@ -0,0 +1,4 @@ +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 12 of 12 possible call-sites using 12 interceptors, 0 commands and 1 readers diff --git a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs index 6e8c8951..eff5ed61 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs @@ -108,13 +108,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -135,9 +137,6 @@ private RowFactory0() {} case 7: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -178,9 +177,6 @@ private RowFactory1() {} case 4: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -228,13 +224,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -246,9 +244,6 @@ private RowFactory2() {} case 2: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -295,13 +290,15 @@ private RowFactory3() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -322,9 +319,6 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs index 6e8c8951..eff5ed61 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs @@ -108,13 +108,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -135,9 +137,6 @@ private RowFactory0() {} case 7: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -178,9 +177,6 @@ private RowFactory1() {} case 4: result.Y = reader.IsDBNull(columnOffset) ? (string?)null : reader.GetString(columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -228,13 +224,15 @@ private RowFactory2() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -246,9 +244,6 @@ private RowFactory2() {} case 2: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : reader.GetDouble(columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; @@ -295,13 +290,15 @@ private RowFactory3() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -322,9 +319,6 @@ private RowFactory3() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs index 28f1c2d8..4eb8e647 100644 --- a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs @@ -74,15 +74,17 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; bool value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -103,9 +105,6 @@ private RowFactory0() {} case 5: value2 = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs index 28f1c2d8..4eb8e647 100644 --- a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs @@ -74,15 +74,17 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; bool value2 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -103,9 +105,6 @@ private RowFactory0() {} case 5: value2 = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs index 66536875..cc2c378e 100644 --- a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs @@ -105,13 +105,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -132,9 +134,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs index 66536875..cc2c378e 100644 --- a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs @@ -105,13 +105,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -132,9 +134,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Single.output.cs b/test/Dapper.AOT.Test/Interceptors/Single.output.cs index 32696980..625ab4de 100644 --- a/test/Dapper.AOT.Test/Interceptors/Single.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Single.output.cs @@ -175,13 +175,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -202,9 +204,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs index 32696980..625ab4de 100644 --- a/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs @@ -175,13 +175,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -202,9 +204,6 @@ private RowFactory0() {} case 5: result.Z = reader.IsDBNull(columnOffset) ? (double?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs index 891e48a5..749d568a 100644 --- a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs @@ -99,13 +99,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::World Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::World result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -120,9 +122,6 @@ private RowFactory0() {} case 3: result.RandomNumber = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs index 891e48a5..749d568a 100644 --- a/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs @@ -99,13 +99,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::World Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::World result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -120,9 +122,6 @@ private RowFactory0() {} case 3: result.RandomNumber = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs index c784baf4..b087bb80 100644 --- a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs @@ -70,14 +70,16 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeThing Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -92,9 +94,6 @@ private RowFactory0() {} case 3: value1 = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs index c784baf4..b087bb80 100644 --- a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs @@ -70,14 +70,16 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::SomeThing Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { int value0 = default; string? value1 = default; - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -92,9 +94,6 @@ private RowFactory0() {} case 3: value1 = reader.IsDBNull(columnOffset) ? (string?)null : GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs index bfbf1f3d..415bb512 100644 --- a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs @@ -226,13 +226,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -241,9 +243,6 @@ private RowFactory0() {} case 1: result.Balance = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; diff --git a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs index bfbf1f3d..415bb512 100644 --- a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs @@ -226,13 +226,15 @@ private RowFactory0() {} { tokens[i] = -1; } - return null; + return tokenCount; } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { global::Foo.Customer result = new(); - foreach (var token in tokens) + int tokenCount = state is int count ? count : tokens.Length; + for (int i = 0; i < tokenCount; i++) { + var token = tokens[i]; switch (token) { case 0: @@ -241,9 +243,6 @@ private RowFactory0() {} case 1: result.Balance = GetValue(reader, columnOffset); break; - case -1: - // unmapped column, skip - break; } columnOffset++; From 44ba3f47447ed0aec2d8d62446cf531be3b30781 Mon Sep 17 00:00:00 2001 From: Devedse Date: Sat, 13 Dec 2025 01:06:08 +0100 Subject: [PATCH 17/23] Add tests for multi-map query support with varying arities --- test/Dapper.AOT.Test/Verifiers/DAP001.cs | 99 ++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/test/Dapper.AOT.Test/Verifiers/DAP001.cs b/test/Dapper.AOT.Test/Verifiers/DAP001.cs index ec350165..d02ff944 100644 --- a/test/Dapper.AOT.Test/Verifiers/DAP001.cs +++ b/test/Dapper.AOT.Test/Verifiers/DAP001.cs @@ -8,7 +8,7 @@ namespace Dapper.AOT.Test.Verifiers; public class DAP001 : Verifier { [Fact] - public Task UnsupportedMethod() => CSVerifyAsync(""" + public Task SingleTypeQueryArity1Supported() => CSVerifyAsync(""" using Dapper; using System.Data.Common; @@ -17,11 +17,98 @@ class SomeCode { public void Foo(DbConnection conn) { - _ = conn.{|#0:Query|}("proc", null!); - _ = conn.Query("proc"); + _ = conn.Query("select 1"); } } - """, DefaultConfig, - [Diagnostic(Diagnostics.UnsupportedMethod).WithLocation(0) - .WithArguments("SqlMapper.Query(IDbConnection, string, Func, object?, IDbTransaction?, bool, string, int?, CommandType?)")]); + """, DefaultConfig, []); + + [Fact] + public Task MultiMapArity3Supported() => CSVerifyAsync(""" + using Dapper; + using System.Data.Common; + + [DapperAot(true)] + class SomeCode + { + public void Foo(DbConnection conn) + { + _ = conn.Query("select 1", (a,b) => a + b); + } + } + """, DefaultConfig, []); + + [Fact] + public Task MultiMapArity4Supported() => CSVerifyAsync(""" + using Dapper; + using System.Data.Common; + + [DapperAot(true)] + class SomeCode + { + public void Foo(DbConnection conn) + { + _ = conn.Query("select 1", (a,b,c) => a + b + c); + } + } + """, DefaultConfig, []); + + [Fact] + public Task MultiMapArity5Supported() => CSVerifyAsync(""" + using Dapper; + using System.Data.Common; + + [DapperAot(true)] + class SomeCode + { + public void Foo(DbConnection conn) + { + _ = conn.Query("select 1", (a,b,c,d) => a + b + c + d); + } + } + """, DefaultConfig, []); + + [Fact] + public Task MultiMapArity6Supported() => CSVerifyAsync(""" + using Dapper; + using System.Data.Common; + + [DapperAot(true)] + class SomeCode + { + public void Foo(DbConnection conn) + { + _ = conn.Query("select 1", (a,b,c,d,e) => a + b + c + d + e); + } + } + """, DefaultConfig, []); + + [Fact] + public Task MultiMapArity7Supported() => CSVerifyAsync(""" + using Dapper; + using System.Data.Common; + + [DapperAot(true)] + class SomeCode + { + public void Foo(DbConnection conn) + { + _ = conn.Query("select 1", (a,b,c,d,e,f) => a + b + c + d + e + f); + } + } + """, DefaultConfig, []); + + [Fact] + public Task MultiMapArity8Supported() => CSVerifyAsync(""" + using Dapper; + using System.Data.Common; + + [DapperAot(true)] + class SomeCode + { + public void Foo(DbConnection conn) + { + _ = conn.Query("select 1", (a,b,c,d,e,f,g) => a + b + c + d + e + f + g); + } + } + """, DefaultConfig, []); } \ No newline at end of file From a4756070a8e1f2d30b206e4a1730b232d3004649 Mon Sep 17 00:00:00 2001 From: Devedse Date: Sat, 13 Dec 2025 18:55:04 +0100 Subject: [PATCH 18/23] Implement feature X to enhance user experience and fix bug Y in module Z --- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 481 +++++++++++++---------- 1 file changed, 265 insertions(+), 216 deletions(-) diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 39ed4fe2..53fc286a 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -79,15 +79,17 @@ public List QueryBuffered( List results; if (state.Reader.Read()) { - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); var split = FindSplits(state.Reader, splitOn, 1)[0]; - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, split), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(split), split); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, split), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(split), split, tokenState2); results.Add(map(obj1, obj2)); } while (state.Reader.Read()); @@ -131,14 +133,16 @@ public async Task> QueryBufferedAsync( if (await state.Reader.ReadAsync(cancellationToken)) { var splits = FindSplits(state.Reader, splitOn, 1); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0]), splits[0]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0]), splits[0], tokenState2); results.Add(map(obj1, obj2)); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -181,16 +185,18 @@ public List QueryBuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 2); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1]), splits[1]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1]), splits[1], tokenState3); results.Add(map(obj1, obj2, obj3)); } while (state.Reader.Read()); @@ -234,16 +240,18 @@ public async Task> QueryBufferedAsync( if (await state.Reader.ReadAsync(cancellationToken)) { var splits = FindSplits(state.Reader, splitOn, 2); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1]), splits[1]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1]), splits[1], tokenState3); results.Add(map(obj1, obj2, obj3)); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -286,18 +294,20 @@ public List QueryBuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 3); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2]), splits[2]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2]), splits[2], tokenState4); results.Add(map(obj1, obj2, obj3, obj4)); } while (state.Reader.Read()); @@ -342,18 +352,20 @@ public async Task> QueryBufferedAsync( if (await state.Reader.ReadAsync(cancellationToken)) { var splits = FindSplits(state.Reader, splitOn, 3); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2]), splits[2]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2]), splits[2], tokenState4); results.Add(map(obj1, obj2, obj3, obj4)); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -397,20 +409,22 @@ public List QueryBuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 4); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3]), splits[3]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3]), splits[3], tokenState5); results.Add(map(obj1, obj2, obj3, obj4, obj5)); } while (state.Reader.Read()); @@ -456,20 +470,22 @@ public async Task> QueryBufferedAsync if (await state.Reader.ReadAsync(cancellationToken)) { var splits = FindSplits(state.Reader, splitOn, 4); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3]), splits[3]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3]), splits[3], tokenState5); results.Add(map(obj1, obj2, obj3, obj4, obj5)); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -514,22 +530,24 @@ public List QueryBuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 5); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4]), splits[4]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4]), splits[4], tokenState6); results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6)); } while (state.Reader.Read()); @@ -576,22 +594,24 @@ public async Task> QueryBufferedAsync.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4]), splits[4]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4]), splits[4], tokenState6); results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6)); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -637,24 +657,26 @@ public List QueryBuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 6); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); - var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4], splits[5] - splits[4]), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[5]), splits[5]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); - var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4], splits[5] - splits[4]), splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, allTokensReadOnly.Slice(splits[5]), splits[5], tokenState7); results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6, obj7)); } while (state.Reader.Read()); @@ -702,24 +724,26 @@ public async Task> QueryBufferedAsync.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); - var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4], splits[5] - splits[4]), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[5]), splits[5]); results = RowFactory.GetRowBuffer(rowCountHint); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); - var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4], splits[5] - splits[4]), splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, allTokensReadOnly.Slice(splits[5]), splits[5], tokenState7); results.Add(map(obj1, obj2, obj3, obj4, obj5, obj6, obj7)); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -757,14 +781,16 @@ public IEnumerable QueryUnbuffered( if (state.Reader.Read()) { - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); var split = FindSplits(state.Reader, splitOn, 1)[0]; - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, split), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(split), split); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, split), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(split), split, tokenState2); yield return map(obj1, obj2); } while (state.Reader.Read()); @@ -799,14 +825,16 @@ public async IAsyncEnumerable QueryUnbufferedAsync( if (await state.Reader.ReadAsync(cancellationToken)) { - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); var split = FindSplits(state.Reader, splitOn, 1)[0]; - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), split); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, split), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(split), split); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, split, tokenState2); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, split), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(split), split, tokenState2); yield return map(obj1, obj2); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -841,15 +869,17 @@ public IEnumerable QueryUnbuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 2); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1]), splits[1]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1]), splits[1], tokenState3); yield return map(obj1, obj2, obj3); } while (state.Reader.Read()); @@ -886,15 +916,17 @@ public async IAsyncEnumerable QueryUnbufferedAsync if (await state.Reader.ReadAsync(cancellationToken)) { var splits = FindSplits(state.Reader, splitOn, 2); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1]), splits[1]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1]), splits[1], tokenState3); yield return map(obj1, obj2, obj3); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -930,17 +962,19 @@ public IEnumerable QueryUnbuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 3); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2]), splits[2]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2]), splits[2], tokenState4); yield return map(obj1, obj2, obj3, obj4); } while (state.Reader.Read()); @@ -978,17 +1012,19 @@ public async IAsyncEnumerable QueryUnbufferedAsync.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2]), splits[2]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2]), splits[2], tokenState4); yield return map(obj1, obj2, obj3, obj4); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -1025,19 +1061,21 @@ public IEnumerable QueryUnbuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 4); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3]), splits[3]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3]), splits[3], tokenState5); yield return map(obj1, obj2, obj3, obj4, obj5); } while (state.Reader.Read()); @@ -1076,19 +1114,21 @@ public async IAsyncEnumerable QueryUnbufferedAsync.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3]), splits[3]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3]), splits[3], tokenState5); yield return map(obj1, obj2, obj3, obj4, obj5); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -1126,21 +1166,23 @@ public IEnumerable QueryUnbuffered( if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 5); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4]), splits[4]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4]), splits[4], tokenState6); yield return map(obj1, obj2, obj3, obj4, obj5, obj6); } while (state.Reader.Read()); @@ -1180,21 +1222,23 @@ public async IAsyncEnumerable QueryUnbufferedAsync.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4]), splits[4]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4]), splits[4], tokenState6); yield return map(obj1, obj2, obj3, obj4, obj5, obj6); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -1233,23 +1277,25 @@ public IEnumerable QueryUnbuffered if (state.Reader.Read()) { var splits = FindSplits(state.Reader, splitOn, 6); - var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); - var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4], splits[5] - splits[4]), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[5]), splits[5]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); - var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4], splits[5] - splits[4]), splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, allTokensReadOnly.Slice(splits[5]), splits[5], tokenState7); yield return map(obj1, obj2, obj3, obj4, obj5, obj6, obj7); } while (state.Reader.Read()); @@ -1290,23 +1336,25 @@ public async IAsyncEnumerable QueryUnbufferedAsync.Default).Tokenize(state.Reader, state.Lease(), 0); - var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[0]); - var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[1]); - var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[2]); - var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[3]); - var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[4]); - var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, state.Lease(), splits[5]); + var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); + var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); + var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1], splits[2] - splits[1]), splits[1]); + var tokenState4 = (factory4 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[2], splits[3] - splits[2]), splits[2]); + var tokenState5 = (factory5 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[3], splits[4] - splits[3]), splits[3]); + var tokenState6 = (factory6 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[4], splits[5] - splits[4]), splits[4]); + var tokenState7 = (factory7 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[5]), splits[5]); do { - var obj1 = factory1.Read(state.Reader, state.Tokens, 0, tokenState1); - var obj2 = factory2.Read(state.Reader, state.Tokens, splits[0], tokenState2); - var obj3 = factory3.Read(state.Reader, state.Tokens, splits[1], tokenState3); - var obj4 = factory4.Read(state.Reader, state.Tokens, splits[2], tokenState4); - var obj5 = factory5.Read(state.Reader, state.Tokens, splits[3], tokenState5); - var obj6 = factory6.Read(state.Reader, state.Tokens, splits[4], tokenState6); - var obj7 = factory7.Read(state.Reader, state.Tokens, splits[5], tokenState7); + var allTokensReadOnly = state.Tokens; + var obj1 = factory1.Read(state.Reader, allTokensReadOnly.Slice(0, splits[0]), 0, tokenState1); + var obj2 = factory2.Read(state.Reader, allTokensReadOnly.Slice(splits[0], splits[1] - splits[0]), splits[0], tokenState2); + var obj3 = factory3.Read(state.Reader, allTokensReadOnly.Slice(splits[1], splits[2] - splits[1]), splits[1], tokenState3); + var obj4 = factory4.Read(state.Reader, allTokensReadOnly.Slice(splits[2], splits[3] - splits[2]), splits[2], tokenState4); + var obj5 = factory5.Read(state.Reader, allTokensReadOnly.Slice(splits[3], splits[4] - splits[3]), splits[3], tokenState5); + var obj6 = factory6.Read(state.Reader, allTokensReadOnly.Slice(splits[4], splits[5] - splits[4]), splits[4], tokenState6); + var obj7 = factory7.Read(state.Reader, allTokensReadOnly.Slice(splits[5]), splits[5], tokenState7); yield return map(obj1, obj2, obj3, obj4, obj5, obj6, obj7); } while (await state.Reader.ReadAsync(cancellationToken)); @@ -1322,3 +1370,4 @@ public async IAsyncEnumerable QueryUnbufferedAsync Date: Sat, 13 Dec 2025 19:34:23 +0100 Subject: [PATCH 19/23] Normalize split column names for improved comparison in multi-map queries --- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 53fc286a..b73571c4 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -27,18 +27,27 @@ private static int[] FindSplits(System.Data.Common.DbDataReader reader, string s } #endif + // Normalize the split column names for comparison + for (int idx = 0; idx < splitColumns.Length; idx++) + { + splitColumns[idx] = StringHashing.Normalize(splitColumns[idx]); + } + int splitIndex = 0; int lastSplit = 0; for (int i = 0; i < fieldCount && splitIndex < count; i++) { var name = reader.GetName(i); + bool matched = false; foreach (var split in splitColumns) { - if (StringHashing.NormalizedEquals(name, split)) + bool equals = StringHashing.NormalizedEquals(name, split); + if (equals) { splits[splitIndex++] = i; lastSplit = i; + matched = true; break; } } @@ -241,6 +250,7 @@ public async Task> QueryBufferedAsync( { var splits = FindSplits(state.Reader, splitOn, 2); var allTokens = state.Lease(); + var tokenState1 = (factory1 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(0, splits[0]), 0); var tokenState2 = (factory2 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[0], splits[1] - splits[0]), splits[0]); var tokenState3 = (factory3 ??= RowFactory.Default).Tokenize(state.Reader, allTokens.Slice(splits[1]), splits[1]); From a3647240491609104ff18736a66f9989d2791bc2 Mon Sep 17 00:00:00 2001 From: Devedse Date: Sat, 13 Dec 2025 22:22:31 +0100 Subject: [PATCH 20/23] Refactor split column normalization for case-insensitive comparison in multi-map queries --- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index b73571c4..560303bb 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -27,27 +27,18 @@ private static int[] FindSplits(System.Data.Common.DbDataReader reader, string s } #endif - // Normalize the split column names for comparison - for (int idx = 0; idx < splitColumns.Length; idx++) - { - splitColumns[idx] = StringHashing.Normalize(splitColumns[idx]); - } - int splitIndex = 0; int lastSplit = 0; for (int i = 0; i < fieldCount && splitIndex < count; i++) { var name = reader.GetName(i); - bool matched = false; foreach (var split in splitColumns) { - bool equals = StringHashing.NormalizedEquals(name, split); - if (equals) + if (string.Equals(name, split, StringComparison.OrdinalIgnoreCase)) { splits[splitIndex++] = i; lastSplit = i; - matched = true; break; } } From ec8f0cdeefc1c946d5b3c5a081dd4e9fc80971f7 Mon Sep 17 00:00:00 2001 From: Devedse Date: Sun, 14 Dec 2025 01:07:12 +0100 Subject: [PATCH 21/23] If the first field of a multimap thing is null, then the whole variable should be null --- .../CodeAnalysis/DapperInterceptorGenerator.cs | 4 ++++ .../Interceptors/Cancellation.output.cs | 1 + .../Interceptors/Cancellation.output.netfx.cs | 1 + .../Interceptors/ColumnAttribute.output.cs | 1 + .../Interceptors/ColumnAttribute.output.netfx.cs | 1 + .../Interceptors/CommandProperties.output.cs | 1 + .../Interceptors/CommandProperties.output.netfx.cs | 1 + .../Interceptors/DateOnly.net6.output.cs | 1 + test/Dapper.AOT.Test/Interceptors/DbString.output.cs | 1 + .../Interceptors/DbString.output.netfx.cs | 1 + .../Interceptors/GetRowParser.output.cs | 1 + .../Interceptors/GetRowParser.output.netfx.cs | 1 + .../Interceptors/GlobalFetchSize.output.cs | 1 + .../Interceptors/GlobalFetchSize.output.netfx.cs | 1 + .../Interceptors/InheritedMembers.output.cs | 1 + .../Interceptors/InheritedMembers.output.netfx.cs | 1 + .../Interceptors/MiscDiagnostics.output.cs | 3 +++ .../Interceptors/MiscDiagnostics.output.netfx.cs | 3 +++ .../Interceptors/NonFactoryMethod.output.cs | 1 + .../Interceptors/NonFactoryMethod.output.netfx.cs | 1 + test/Dapper.AOT.Test/Interceptors/Query.output.cs | 1 + .../Interceptors/Query.output.netfx.cs | 1 + .../QueryCustomConstructionWithConstructor.output.cs | 12 ++++++++++++ ...CustomConstructionWithConstructor.output.netfx.cs | 12 ++++++++++++ ...ueryCustomConstructionWithFactoryMethod.output.cs | 4 ++++ ...stomConstructionWithFactoryMethod.output.netfx.cs | 4 ++++ .../Interceptors/QueryDetection.output.cs | 1 + .../Interceptors/QueryDetection.output.netfx.cs | 1 + .../Interceptors/QueryMultiType.output.cs | 1 + .../Interceptors/QueryMultiType.output.netfx.cs | 1 + .../Interceptors/QueryStrictBind.output.cs | 4 ++++ .../Interceptors/QueryStrictBind.output.netfx.cs | 4 ++++ .../Interceptors/RequiredProperties.output.cs | 1 + .../Interceptors/RequiredProperties.output.netfx.cs | 1 + .../Interceptors/RowCountHint.output.cs | 1 + .../Interceptors/RowCountHint.output.netfx.cs | 1 + test/Dapper.AOT.Test/Interceptors/Single.output.cs | 1 + .../Interceptors/Single.output.netfx.cs | 1 + .../Interceptors/Techempower.output.cs | 1 + .../Interceptors/Techempower.output.netfx.cs | 1 + .../Interceptors/TopLevelStatements.output.cs | 1 + .../Interceptors/TopLevelStatements.output.netfx.cs | 1 + test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs | 1 + .../Interceptors/TsqlTips.output.netfx.cs | 1 + 44 files changed, 85 insertions(+) diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index e8fca305..a3ae3157 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -903,6 +903,10 @@ void WriteReadMethod(in GenerateState context) sb.Append("public override ").Append(type).Append(" Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state)").Indent().NewLine(); + // For multi-map queries: if this is not the first entity (columnOffset > 0) and the first column is NULL, + // return default to handle LEFT JOINs where the related entity doesn't exist + sb.Append("if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!;").NewLine(); + int token = 0; var deferredMethodArgumentsOrdered = new SortedList(); diff --git a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs index 1dc0aa02..d6f99f0e 100644 --- a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.cs @@ -86,6 +86,7 @@ private RowFactory0() {} } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MyType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs index 1dc0aa02..d6f99f0e 100644 --- a/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Cancellation.output.netfx.cs @@ -86,6 +86,7 @@ private RowFactory0() {} } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MyType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs index 05d23a0f..1cd7fca5 100644 --- a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.cs @@ -83,6 +83,7 @@ private RowFactory0() {} } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MyType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs index 05d23a0f..1cd7fca5 100644 --- a/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/ColumnAttribute.output.netfx.cs @@ -83,6 +83,7 @@ private RowFactory0() {} } public override global::Foo.MyType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MyType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs index 0b81f09a..b585259f 100644 --- a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.cs @@ -144,6 +144,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs index 0b81f09a..b585259f 100644 --- a/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/CommandProperties.output.netfx.cs @@ -144,6 +144,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs index e844c648..52f520aa 100644 --- a/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DateOnly.net6.output.cs @@ -95,6 +95,7 @@ private RowFactory0() {} } public override global::Foo.User Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.User result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs index 0c7c7518..7eafd3df 100644 --- a/test/Dapper.AOT.Test/Interceptors/DbString.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/DbString.output.cs @@ -95,6 +95,7 @@ private RowFactory0() {} } public override global::Foo.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Product result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs index 0c7c7518..7eafd3df 100644 --- a/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/DbString.output.netfx.cs @@ -95,6 +95,7 @@ private RowFactory0() {} } public override global::Foo.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Product result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs index 2550bbc8..750ac227 100644 --- a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.cs @@ -82,6 +82,7 @@ private RowFactory0() {} } public override global::HazNameId Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::HazNameId result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs index 2550bbc8..750ac227 100644 --- a/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/GetRowParser.output.netfx.cs @@ -82,6 +82,7 @@ private RowFactory0() {} } public override global::HazNameId Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::HazNameId result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs index 2b0655da..c88c9029 100644 --- a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.cs @@ -73,6 +73,7 @@ private RowFactory0() {} } public override global::SomeApp.SomeQueryType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeApp.SomeQueryType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs index 2b0655da..c88c9029 100644 --- a/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/GlobalFetchSize.output.netfx.cs @@ -73,6 +73,7 @@ private RowFactory0() {} } public override global::SomeApp.SomeQueryType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeApp.SomeQueryType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs index 12defb8c..3db150ae 100644 --- a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.cs @@ -89,6 +89,7 @@ private RowFactory0() {} } public override global::Entity1 Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Entity1 result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs index 12defb8c..3db150ae 100644 --- a/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/InheritedMembers.output.netfx.cs @@ -89,6 +89,7 @@ private RowFactory0() {} } public override global::Entity1 Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Entity1 result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs index 548a883c..9f7e9902 100644 --- a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.cs @@ -162,6 +162,7 @@ private RowFactory0() {} } public override global::SomeCode.InternalNesting.SomePublicType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeCode.InternalNesting.SomePublicType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -219,6 +220,7 @@ private RowFactory1() {} } public override global::SomeCode.InternalNesting.SomeInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeCode.InternalNesting.SomeInternalType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -276,6 +278,7 @@ private RowFactory2() {} } public override global::SomeCode.InternalNesting.SomeProtectedInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeCode.InternalNesting.SomeProtectedInternalType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs index 548a883c..9f7e9902 100644 --- a/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/MiscDiagnostics.output.netfx.cs @@ -162,6 +162,7 @@ private RowFactory0() {} } public override global::SomeCode.InternalNesting.SomePublicType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeCode.InternalNesting.SomePublicType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -219,6 +220,7 @@ private RowFactory1() {} } public override global::SomeCode.InternalNesting.SomeInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeCode.InternalNesting.SomeInternalType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -276,6 +278,7 @@ private RowFactory2() {} } public override global::SomeCode.InternalNesting.SomeProtectedInternalType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::SomeCode.InternalNesting.SomeProtectedInternalType result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs index 934e3348..043a4a46 100644 --- a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.cs @@ -158,6 +158,7 @@ private RowFactory0() {} } public override global::UsageLinker.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::UsageLinker.Product result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs index 934e3348..043a4a46 100644 --- a/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/NonFactoryMethod.output.netfx.cs @@ -158,6 +158,7 @@ private RowFactory0() {} } public override global::UsageLinker.Product Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::UsageLinker.Product result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/Query.output.cs b/test/Dapper.AOT.Test/Interceptors/Query.output.cs index 6e2dda16..ed3f2e18 100644 --- a/test/Dapper.AOT.Test/Interceptors/Query.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Query.output.cs @@ -196,6 +196,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs index b5dddcd8..146e2333 100644 --- a/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Query.output.netfx.cs @@ -168,6 +168,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs index 6ef78cab..e61953be 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.cs @@ -231,6 +231,7 @@ private RowFactory0() {} } public override global::Foo.ParameterlessCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.ParameterlessCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -306,6 +307,7 @@ private RowFactory1() {} } public override global::Foo.GetOnlyPropertiesViaConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -381,6 +383,7 @@ private RowFactory2() {} } public override global::Foo.RecordClass Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.RecordClass result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -456,6 +459,7 @@ private RowFactory3() {} } public override global::Foo.RecordClassSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -531,6 +535,7 @@ private RowFactory4() {} } public override global::Foo.RecordStruct Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.RecordStruct result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -606,6 +611,7 @@ private RowFactory5() {} } public override global::Foo.RecordStructSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -681,6 +687,7 @@ private RowFactory6() {} } public override global::Foo.InitPropsOnly Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -761,6 +768,7 @@ private RowFactory7() {} } public override global::Foo.InitPropsAndDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -840,6 +848,7 @@ private RowFactory8() {} } public override global::Foo.OnlyNonDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.OnlyNonDapperAotCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -915,6 +924,7 @@ private RowFactory9() {} } public override global::Foo.SingleDefaultCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.SingleDefaultCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -990,6 +1000,7 @@ private RowFactory10() {} } public override global::Foo.MultipleDapperAotCtors Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MultipleDapperAotCtors result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -1065,6 +1076,7 @@ private RowFactory11() {} } public override global::Foo.SingleDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.SingleDapperAotCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs index 6ef78cab..e61953be 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithConstructor.output.netfx.cs @@ -231,6 +231,7 @@ private RowFactory0() {} } public override global::Foo.ParameterlessCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.ParameterlessCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -306,6 +307,7 @@ private RowFactory1() {} } public override global::Foo.GetOnlyPropertiesViaConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -381,6 +383,7 @@ private RowFactory2() {} } public override global::Foo.RecordClass Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.RecordClass result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -456,6 +459,7 @@ private RowFactory3() {} } public override global::Foo.RecordClassSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -531,6 +535,7 @@ private RowFactory4() {} } public override global::Foo.RecordStruct Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.RecordStruct result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -606,6 +611,7 @@ private RowFactory5() {} } public override global::Foo.RecordStructSimpleCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -681,6 +687,7 @@ private RowFactory6() {} } public override global::Foo.InitPropsOnly Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -761,6 +768,7 @@ private RowFactory7() {} } public override global::Foo.InitPropsAndDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -840,6 +848,7 @@ private RowFactory8() {} } public override global::Foo.OnlyNonDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.OnlyNonDapperAotCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -915,6 +924,7 @@ private RowFactory9() {} } public override global::Foo.SingleDefaultCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.SingleDefaultCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -990,6 +1000,7 @@ private RowFactory10() {} } public override global::Foo.MultipleDapperAotCtors Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MultipleDapperAotCtors result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -1065,6 +1076,7 @@ private RowFactory11() {} } public override global::Foo.SingleDapperAotCtor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.SingleDapperAotCtor result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs index 9dece7a8..65752530 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.cs @@ -119,6 +119,7 @@ private RowFactory0() {} } public override global::Foo.PublicPropertiesNoConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -194,6 +195,7 @@ private RowFactory1() {} } public override global::Foo.MultipleDapperAotFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MultipleDapperAotFactoryMethods result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -269,6 +271,7 @@ private RowFactory2() {} } public override global::Foo.SingleFactoryNotMarkedWithDapperAot Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.SingleFactoryNotMarkedWithDapperAot result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -344,6 +347,7 @@ private RowFactory3() {} } public override global::Foo.MultipleStandardFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MultipleStandardFactoryMethods result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs index 9dece7a8..65752530 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryCustomConstructionWithFactoryMethod.output.netfx.cs @@ -119,6 +119,7 @@ private RowFactory0() {} } public override global::Foo.PublicPropertiesNoConstructor Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; double? value2 = default; @@ -194,6 +195,7 @@ private RowFactory1() {} } public override global::Foo.MultipleDapperAotFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MultipleDapperAotFactoryMethods result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -269,6 +271,7 @@ private RowFactory2() {} } public override global::Foo.SingleFactoryNotMarkedWithDapperAot Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.SingleFactoryNotMarkedWithDapperAot result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -344,6 +347,7 @@ private RowFactory3() {} } public override global::Foo.MultipleStandardFactoryMethods Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.MultipleStandardFactoryMethods result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs index 8c16046b..25a9d44e 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.cs @@ -159,6 +159,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs index 8c16046b..25a9d44e 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryDetection.output.netfx.cs @@ -159,6 +159,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs index cbab64c9..5f07e915 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.cs @@ -237,6 +237,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs index cbab64c9..5f07e915 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryMultiType.output.netfx.cs @@ -237,6 +237,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs index eff5ed61..e39b0260 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.cs @@ -112,6 +112,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -162,6 +163,7 @@ private RowFactory1() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int lim = global::System.Math.Min(tokens.Length, 5); for (int token = 0; token < lim; token++) // query-columns predefined @@ -228,6 +230,7 @@ private RowFactory2() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -294,6 +297,7 @@ private RowFactory3() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs index eff5ed61..e39b0260 100644 --- a/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/QueryStrictBind.output.netfx.cs @@ -112,6 +112,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -162,6 +163,7 @@ private RowFactory1() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int lim = global::System.Math.Min(tokens.Length, 5); for (int token = 0; token < lim; token++) // query-columns predefined @@ -228,6 +230,7 @@ private RowFactory2() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) @@ -294,6 +297,7 @@ private RowFactory3() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs index 4eb8e647..2c284bca 100644 --- a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.cs @@ -78,6 +78,7 @@ private RowFactory0() {} } public override global::SomeType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; bool value2 = default; diff --git a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs index 4eb8e647..2c284bca 100644 --- a/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/RequiredProperties.output.netfx.cs @@ -78,6 +78,7 @@ private RowFactory0() {} } public override global::SomeType Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; bool value2 = default; diff --git a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs index cc2c378e..e93e89fc 100644 --- a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.cs @@ -109,6 +109,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs index cc2c378e..e93e89fc 100644 --- a/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/RowCountHint.output.netfx.cs @@ -109,6 +109,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/Single.output.cs b/test/Dapper.AOT.Test/Interceptors/Single.output.cs index 625ab4de..491f6b14 100644 --- a/test/Dapper.AOT.Test/Interceptors/Single.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Single.output.cs @@ -179,6 +179,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs index 625ab4de..491f6b14 100644 --- a/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Single.output.netfx.cs @@ -179,6 +179,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs index 749d568a..06893a1c 100644 --- a/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/Techempower.output.cs @@ -103,6 +103,7 @@ private RowFactory0() {} } public override global::World Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::World result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs index 749d568a..06893a1c 100644 --- a/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/Techempower.output.netfx.cs @@ -103,6 +103,7 @@ private RowFactory0() {} } public override global::World Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::World result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs index b087bb80..72a09a27 100644 --- a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.cs @@ -74,6 +74,7 @@ private RowFactory0() {} } public override global::SomeThing Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; int tokenCount = state is int count ? count : tokens.Length; diff --git a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs index b087bb80..72a09a27 100644 --- a/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/TopLevelStatements.output.netfx.cs @@ -74,6 +74,7 @@ private RowFactory0() {} } public override global::SomeThing Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; int value0 = default; string? value1 = default; int tokenCount = state is int count ? count : tokens.Length; diff --git a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs index 415bb512..8a3bdaec 100644 --- a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.cs @@ -230,6 +230,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) diff --git a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs index 415bb512..8a3bdaec 100644 --- a/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/TsqlTips.output.netfx.cs @@ -230,6 +230,7 @@ private RowFactory0() {} } public override global::Foo.Customer Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) { + if (columnOffset > 0 && reader.IsDBNull(columnOffset)) return default!; global::Foo.Customer result = new(); int tokenCount = state is int count ? count : tokens.Length; for (int i = 0; i < tokenCount; i++) From 8a52c2b58a3b0589bccbd8b862109592b2074065 Mon Sep 17 00:00:00 2001 From: Devedse Date: Sun, 14 Dec 2025 03:00:44 +0100 Subject: [PATCH 22/23] Enhance splitOn handling in multi-map queries to support wildcard and improve error messaging --- src/Dapper.AOT/CommandT.QueryMultiMap.cs | 64 +++++++++++++++++++----- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/Dapper.AOT/CommandT.QueryMultiMap.cs b/src/Dapper.AOT/CommandT.QueryMultiMap.cs index 560303bb..3ad105b7 100644 --- a/src/Dapper.AOT/CommandT.QueryMultiMap.cs +++ b/src/Dapper.AOT/CommandT.QueryMultiMap.cs @@ -17,6 +17,17 @@ private static int[] FindSplits(System.Data.Common.DbDataReader reader, string s { var splits = new int[count]; var fieldCount = reader.FieldCount; + + // Handle wildcard splitOn - split after every column + if (splitOn == "*") + { + for (int i = 0; i < count; i++) + { + splits[i] = i + 1; + } + return splits; + } + #if NET5_0_OR_GREATER var splitColumns = splitOn.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); #else @@ -27,32 +38,59 @@ private static int[] FindSplits(System.Data.Common.DbDataReader reader, string s } #endif - int splitIndex = 0; - int lastSplit = 0; + // Search RIGHT-TO-LEFT like original Dapper to handle duplicate column names correctly + // "in this we go right to left through the data reader in order to cope with properties that are + // named the same as a subsequent primary key that we split on" + int splitIndex = count - 1; + int currentPos = fieldCount; - for (int i = 0; i < fieldCount && splitIndex < count; i++) + // Process splits from last to first + for (int splitIdx = splitColumns.Length - 1; splitIdx >= 0; splitIdx--) { - var name = reader.GetName(i); - foreach (var split in splitColumns) + if (splitIndex < 0) break; + + var split = splitColumns[splitIdx]; + bool found = false; + + // Search backwards from currentPos + for (int i = currentPos - 1; i > 0; i--) { + var name = reader.GetName(i); if (string.Equals(name, split, StringComparison.OrdinalIgnoreCase)) { - splits[splitIndex++] = i; - lastSplit = i; + splits[splitIndex--] = i; + currentPos = i; + found = true; break; } } + + if (!found) + { + // Build helpful error message like original Dapper + var availableColumns = new System.Text.StringBuilder(); + for (int i = 0; i < fieldCount; i++) + { + if (i > 0) availableColumns.Append(", "); + availableColumns.Append(reader.GetName(i)); + } + throw new System.ArgumentException( + $"Multi-map error: splitOn column '{split}' was not found - please ensure your splitOn parameter is set and in the correct order (available columns: {availableColumns})", + nameof(splitOn)); + } } // Fill remaining splits evenly if not all found - if (splitIndex < count) + // Note: splitIndex will be >= 0 if we didn't find all splits (since we decrement on each find) + if (splitIndex >= 0) { - var remaining = count - splitIndex; - var step = (fieldCount - lastSplit) / (remaining + 1); - for (int i = 0; i < remaining; i++) + var remaining = splitIndex + 1; + var step = currentPos / (remaining + 1); + for (int i = 0; i <= splitIndex; i++) { - lastSplit += step; - splits[splitIndex++] = lastSplit; + currentPos -= step; + if (currentPos <= 0) currentPos = 1; // Ensure we don't go below column 1 + splits[i] = currentPos; } } From 58d215fbf9a6ee8859f852c39f2a5c04a5a46347 Mon Sep 17 00:00:00 2001 From: Devedse Date: Sun, 14 Dec 2025 21:30:51 +0100 Subject: [PATCH 23/23] Implement special handling for enum types in value conversion to align with Dapper's logic --- src/Dapper.AOT/Internal/CommandUtils.cs | 35 ++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Dapper.AOT/Internal/CommandUtils.cs b/src/Dapper.AOT/Internal/CommandUtils.cs index 00ac9414..7edf0319 100644 --- a/src/Dapper.AOT/Internal/CommandUtils.cs +++ b/src/Dapper.AOT/Internal/CommandUtils.cs @@ -303,7 +303,40 @@ internal static T As(object? value) } else { - return (T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T), CultureInfo.InvariantCulture); + // Handle enum types: match original Dapper's enum conversion logic + // Enums require special handling because: + // 1. Databases may return different integer types (e.g., SQLite returns Int64 for INTEGER columns) + // 2. The enum's underlying type might differ (e.g., enum with int underlying type vs Int64 from DB) + // 3. Floating point values need conversion to the enum's underlying integral type first + // Enum.ToObject handles the conversion from any integral type to the enum automatically + var targetType = typeof(T); + var underlyingType = Nullable.GetUnderlyingType(targetType); + + // Unwrap nullable first, like original Dapper does + var effectiveType = underlyingType ?? targetType; + + if (effectiveType.IsEnum) + { + // Special handling for float/double/decimal like original Dapper + if (value is float || value is double || value is decimal) + { + value = Convert.ChangeType(value, Enum.GetUnderlyingType(effectiveType), CultureInfo.InvariantCulture); + } + // Enum.ToObject returns the enum value boxed as object + // For nullable enums, the cast from object will fail, so we need to use Convert.ChangeType + // which properly handles boxing/unboxing for nullable types + return (T)Enum.ToObject(effectiveType, value); + } + else if (underlyingType is not null) + { + // Other nullable types + return (T)Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture); + } + else + { + // Non-nullable, non-enum fallback + return (T)Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture); + } } } }