DapperAOT icon indicating copy to clipboard operation
DapperAOT copied to clipboard

feat: support Dapper overloads with CommandDefinition

Open DeagleGross opened this issue 9 months ago • 0 comments

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

DeagleGross avatar May 04 '25 17:05 DeagleGross