From b8111fa01467d2c0e243641763554e42f248bb21 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Wed, 13 Dec 2023 16:32:13 +0000 Subject: [PATCH 1/2] respect designated DbType when explicitly specified --- .../Internal/Inspection.cs | 1 + .../Interceptors/DbValueUsage.input.cs | 28 ++++++ .../Interceptors/DbValueUsage.output.cs | 91 +++++++++++++++++++ .../Interceptors/DbValueUsage.output.netfx.cs | 91 +++++++++++++++++++ .../DbValueUsage.output.netfx.txt | 4 + .../Interceptors/DbValueUsage.output.txt | 4 + 6 files changed, 219 insertions(+) create mode 100644 test/Dapper.AOT.Test/Interceptors/DbValueUsage.input.cs create mode 100644 test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.cs create mode 100644 test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.netfx.cs create mode 100644 test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.netfx.txt create mode 100644 test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.txt diff --git a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs index 168528be..b7c37773 100644 --- a/src/Dapper.AOT.Analyzers/Internal/Inspection.cs +++ b/src/Dapper.AOT.Analyzers/Internal/Inspection.cs @@ -442,6 +442,7 @@ public readonly struct ElementMember if (preferredType != dbType) { // only preserve the reader method if this matches readerMethod = null; + dbType = preferredType; } } return dbType; diff --git a/test/Dapper.AOT.Test/Interceptors/DbValueUsage.input.cs b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.input.cs new file mode 100644 index 00000000..69825b01 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.input.cs @@ -0,0 +1,28 @@ +using Dapper; +using System; +using System.Data.Common; +using System.Threading.Tasks; + +[DapperAot(enabled: true)] +public static class UsersSqlQueries +{ + public sealed class UserIncrementParams + { + [DbValue(Name = "userId")] + public int UserId { get; set; } + + [DbValue(Name = "date", DbType = System.Data.DbType.Date)] + public DateTime Date { get; set; } + } + + public static async Task IncrementAsync(DbConnection connection, int userId) + { + var date = DateTime.Today; + + await connection.ExecuteAsync(""" + UPDATE [dbo].[table] + SET [column] = ([column] + 1) + WHERE [Id] = @userId and [Date] = @date + """, new UserIncrementParams() { UserId = userId, Date = date }); + } +} \ No newline at end of file diff --git a/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.cs b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.cs new file mode 100644 index 00000000..e70f5146 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.cs @@ -0,0 +1,91 @@ +#nullable enable +namespace Dapper.AOT // interceptors must be in a known namespace +{ + file static class DapperGeneratedInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\DbValueUsage.input.cs", 22, 26)] + internal static global::System.Threading.Tasks.Task ExecuteAsync0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Execute, Async, HasParameters, Text, KnownParameters + // takes parameter: global::UsersSqlQueries.UserIncrementParams + // parameter map: Date UserId + 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.Command(cnn, transaction, sql, global::System.Data.CommandType.Text, commandTimeout.GetValueOrDefault(), CommandFactory0.Instance).ExecuteAsync((global::UsersSqlQueries.UserIncrementParams)param!); + + } + + 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 CommandFactory0 : CommonCommandFactory + { + internal static readonly CommandFactory0 Instance = new(); + public override void AddParameters(in global::Dapper.UnifiedCommand cmd, global::UsersSqlQueries.UserIncrementParams args) + { + var ps = cmd.Parameters; + global::System.Data.Common.DbParameter p; + p = cmd.CreateParameter(); + p.ParameterName = "userId"; + p.DbType = global::System.Data.DbType.Int32; + p.Direction = global::System.Data.ParameterDirection.Input; + p.Value = AsValue(args.UserId); + ps.Add(p); + + p = cmd.CreateParameter(); + p.ParameterName = "date"; + p.DbType = global::System.Data.DbType.Date; + p.Direction = global::System.Data.ParameterDirection.Input; + p.Value = AsValue(args.Date); + ps.Add(p); + + } + public override void UpdateParameters(in global::Dapper.UnifiedCommand cmd, global::UsersSqlQueries.UserIncrementParams args) + { + var ps = cmd.Parameters; + ps[0].Value = AsValue(args.UserId); + ps[1].Value = AsValue(args.Date); + + } + public override bool CanPrepare => true; + + } + + + } +} +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/DbValueUsage.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.netfx.cs new file mode 100644 index 00000000..e70f5146 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.netfx.cs @@ -0,0 +1,91 @@ +#nullable enable +namespace Dapper.AOT // interceptors must be in a known namespace +{ + file static class DapperGeneratedInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\DbValueUsage.input.cs", 22, 26)] + internal static global::System.Threading.Tasks.Task ExecuteAsync0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Execute, Async, HasParameters, Text, KnownParameters + // takes parameter: global::UsersSqlQueries.UserIncrementParams + // parameter map: Date UserId + 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.Command(cnn, transaction, sql, global::System.Data.CommandType.Text, commandTimeout.GetValueOrDefault(), CommandFactory0.Instance).ExecuteAsync((global::UsersSqlQueries.UserIncrementParams)param!); + + } + + 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 CommandFactory0 : CommonCommandFactory + { + internal static readonly CommandFactory0 Instance = new(); + public override void AddParameters(in global::Dapper.UnifiedCommand cmd, global::UsersSqlQueries.UserIncrementParams args) + { + var ps = cmd.Parameters; + global::System.Data.Common.DbParameter p; + p = cmd.CreateParameter(); + p.ParameterName = "userId"; + p.DbType = global::System.Data.DbType.Int32; + p.Direction = global::System.Data.ParameterDirection.Input; + p.Value = AsValue(args.UserId); + ps.Add(p); + + p = cmd.CreateParameter(); + p.ParameterName = "date"; + p.DbType = global::System.Data.DbType.Date; + p.Direction = global::System.Data.ParameterDirection.Input; + p.Value = AsValue(args.Date); + ps.Add(p); + + } + public override void UpdateParameters(in global::Dapper.UnifiedCommand cmd, global::UsersSqlQueries.UserIncrementParams args) + { + var ps = cmd.Parameters; + ps[0].Value = AsValue(args.UserId); + ps[1].Value = AsValue(args.Date); + + } + public override bool CanPrepare => true; + + } + + + } +} +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/DbValueUsage.output.netfx.txt b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.netfx.txt new file mode 100644 index 00000000..6aee302d --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.netfx.txt @@ -0,0 +1,4 @@ +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 1 of 1 possible call-sites using 1 interceptors, 1 commands and 0 readers diff --git a/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.txt b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.txt new file mode 100644 index 00000000..6aee302d --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/DbValueUsage.output.txt @@ -0,0 +1,4 @@ +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 1 of 1 possible call-sites using 1 interceptors, 1 commands and 0 readers From 27e0ddf6e504a121bb763abb90cd44c41b028687 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Wed, 13 Dec 2023 16:38:29 +0000 Subject: [PATCH 2/2] observe additional impacted test --- test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.cs | 2 +- .../Interceptors/MappedSqlDetection.output.netfx.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.cs b/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.cs index 0ce075cc..0f7ec496 100644 --- a/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.cs +++ b/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.cs @@ -121,7 +121,7 @@ public override void AddParameters(in global::Dapper.UnifiedCommand cmd, global: global::System.Data.Common.DbParameter p; p = cmd.CreateParameter(); p.ParameterName = "f"; - p.DbType = global::System.Data.DbType.String; + p.DbType = global::System.Data.DbType.AnsiString; p.Size = 200; p.Direction = global::System.Data.ParameterDirection.Input; p.Value = AsValue(args.B); diff --git a/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.netfx.cs index 0ce075cc..0f7ec496 100644 --- a/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.netfx.cs +++ b/test/Dapper.AOT.Test/Interceptors/MappedSqlDetection.output.netfx.cs @@ -121,7 +121,7 @@ public override void AddParameters(in global::Dapper.UnifiedCommand cmd, global: global::System.Data.Common.DbParameter p; p = cmd.CreateParameter(); p.ParameterName = "f"; - p.DbType = global::System.Data.DbType.String; + p.DbType = global::System.Data.DbType.AnsiString; p.Size = 200; p.Direction = global::System.Data.ParameterDirection.Input; p.Value = AsValue(args.B);