feat: support Dapper overloads with CommandDefinition
Current PR adds a support to parse use cases with CommandDefinition.
Usage check
If an user invocation has 2 or more parameters which are 1) cnn; 2) command where it is of type CommandDefinition, then I consider it to be a proper Dapper usage with CommandDefinition.
DapperAnalyzer has built-in argument processing to fix sql / detect buffering etc. https://github.com/DapperLib/DapperAOT/blob/4f8cd50639a4b1d489cdff0583d351578246c78e/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs#L514-L517
Unfortunately, it is too complicated to cover many other usages of CommandDefinition, so I only added a case for in-place object creation:
_ = connection.Query<string>(new CommandDefinition(...));
Other cases will work (i.e. CommandDefinition as local variable), but will not have argument processing done.
Generator
Since the Dapper.AOT has its own API, it is very easy to just pass in the same parameters who are the CommandDefinition members. So I just do this for example:
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\CommandDefinition.input.cs", 32, 24)]
internal static global::System.Threading.Tasks.Task<string?> ExecuteScalarAsync2(this global::System.Data.IDbConnection cnn, global::Dapper.CommandDefinition command)
{
// Execute, Async, TypedResult, HasParameters, Text, Scalar, KnownParameters
// takes parameter: <anonymous type: int X>
// parameter map: X
// returns data: string
global::System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(command.CommandText));
global::System.Diagnostics.Debug.Assert((command.CommandType ?? global::Dapper.DapperAotExtensions.GetCommandType(command.CommandText)) == global::System.Data.CommandType.Text);
global::System.Diagnostics.Debug.Assert(command.Buffered is false);
global::System.Diagnostics.Debug.Assert(command.Parameters is not null);
return global::Dapper.DapperAotExtensions.Command(cnn, command.Transaction, command.CommandText, command.CommandType ?? default, command.CommandTimeout ?? default, CommandFactory0.Instance).ExecuteScalarAsync<string>(command.Parameters, cancellationToken: command.CancellationToken);
}
CommandDefinition implementation is very convenient for such usage - for example I dont care what CancellationToken to pass in, because if it is not passed into CommandDefinition, it will be simply default. So I just map all members into the underlying SQL API
Tests
Added code-generation + integration tests.
Fixes #112