diff --git a/Directory.Packages.props b/Directory.Packages.props index 081d78e..adc4550 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,6 +6,7 @@ + diff --git a/src/Dapper.AOT/Internal/CommandUtils.cs b/src/Dapper.AOT/Internal/CommandUtils.cs index 00ac941..997274b 100644 --- a/src/Dapper.AOT/Internal/CommandUtils.cs +++ b/src/Dapper.AOT/Internal/CommandUtils.cs @@ -303,7 +303,14 @@ internal static T As(object? value) } else { - return (T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T), CultureInfo.InvariantCulture); + var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); + if (targetType.IsEnum) + { + return (s = value as string) is not null + ? (T)Enum.Parse(targetType, s, true) + : (T)Enum.ToObject(targetType, value); + } + return (T)Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture); } } } diff --git a/test/Dapper.AOT.Test/CommandUtilsEnumTests.cs b/test/Dapper.AOT.Test/CommandUtilsEnumTests.cs new file mode 100644 index 0000000..c013c7e --- /dev/null +++ b/test/Dapper.AOT.Test/CommandUtilsEnumTests.cs @@ -0,0 +1,23 @@ +using Dapper.AOT.Test.Integration; +using Dapper.Internal; +using Xunit; + +namespace Dapper.AOT.Test +{ + public class CommandUtilsEnumTests + { + [Theory] + [InlineData(1L, SomeEnum.A)] // Int64 (SQLite) + [InlineData(2, SomeEnum.B)] // Int32 (SQL Server) + public void As_NumericToEnum(object value, SomeEnum expected) + { + Assert.Equal(expected, CommandUtils.As(value)); + } + + [Fact] + public void As_StringToEnum() + { + Assert.Equal(SomeEnum.B, CommandUtils.As("B")); + } + } +} diff --git a/test/Dapper.AOT.Test/Dapper.AOT.Test.csproj b/test/Dapper.AOT.Test/Dapper.AOT.Test.csproj index bd4ee08..955d5a0 100644 --- a/test/Dapper.AOT.Test/Dapper.AOT.Test.csproj +++ b/test/Dapper.AOT.Test/Dapper.AOT.Test.csproj @@ -1,15 +1,17 @@ - + net8.0;net48;net9.0 $(NoWarn);IDE0042;CS8002;CA1816 Dapper.AOT.Test $(DefineConstants);DAPPERAOT_INTERNAL + $(InterceptorsPreviewNamespaces);Dapper.AOT + true + true - @@ -17,6 +19,13 @@ + + + + + Interceptors\SqliteUsage.input.cs + + $([System.String]::Copy(%(Filename)).Replace('.output', '.input.cs')) @@ -26,11 +35,11 @@ $([System.String]::Copy(%(Filename)).Replace('.VB.cs', '.cs')) - + diff --git a/test/Dapper.AOT.Test/Integration/SqliteTests.cs b/test/Dapper.AOT.Test/Integration/SqliteTests.cs new file mode 100644 index 0000000..1087530 --- /dev/null +++ b/test/Dapper.AOT.Test/Integration/SqliteTests.cs @@ -0,0 +1,35 @@ +#if !NETFRAMEWORK +using SqliteConnection = Microsoft.Data.Sqlite.SqliteConnection; +using System; +using Xunit; + +namespace Dapper.AOT.Test.Integration +{ + public sealed class SqliteTests : IDisposable + { + private readonly SqliteConnection connection; + + public void Dispose() => connection.Dispose(); + + [DapperAot(false)] + public SqliteTests() + { + connection = new("Data Source=:memory:"); + connection.Open(); + connection.Execute("CREATE TABLE Foo(Id INTEGER PRIMARY KEY ASC, Name TEXT, Value INTEGER)"); // vanilla Dapper + } + + [Fact] + public void InsertAndSelect() + { + // use the generated output from SqliteUsage.Output.cs + foreach (SomeEnum e in Enum.GetValues(typeof(SomeEnum))) + { + SqliteUsage.Insert(connection, e.ToString(), e); + } + + var rows = SqliteUsage.GetAll(connection); + } + } +} +#endif \ No newline at end of file diff --git a/test/Dapper.AOT.Test/Interceptors/SqliteUsage.input.cs b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.input.cs new file mode 100644 index 0000000..104439b --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.input.cs @@ -0,0 +1,27 @@ +using Dapper; +using System.Collections.Generic; +using System.Data.Common; + +namespace Dapper.AOT.Test.Integration; + +[DapperAot] +static class SqliteUsage +{ + public static void Insert(DbConnection connection, string name, SomeEnum value) + => connection.Execute("INSERT INTO Foo(Name, Value) VALUES ($name, $value)", new { name, value }); + + public static List GetAll(DbConnection connection) + => connection.Query("SELECT Id, Name, Value FROM Foo").AsList(); +} + +public class TypeWithSomeEnum +{ + public int Id { get; set; } + public string Name { get; set; } + public SomeEnum Value { get; set; } +} + +public enum SomeEnum +{ + A = 1, B = 2, C = 3, +} \ No newline at end of file diff --git a/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.cs b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.cs new file mode 100644 index 0000000..6d007db --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.cs @@ -0,0 +1,171 @@ +#nullable enable +namespace Dapper.AOT // interceptors must be in a known namespace +{ + file static class DapperGeneratedInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\SqliteUsage.input.cs", 11, 23)] + internal static int Execute0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Execute, HasParameters, Text, KnownParameters + // takes parameter: + // parameter map: name value + 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).Execute(param); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\SqliteUsage.input.cs", 14, 23)] + internal static global::System.Collections.Generic.IEnumerable Query1(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, Text, BindResultsByName + // returns data: global::Dapper.AOT.Test.Integration.TypeWithSomeEnum + 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(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.Text, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(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 1113510858U when NormalizedEquals(name, "value"): + token = type == typeof(global::Dapper.AOT.Test.Integration.SomeEnum) ? 2 : 5; + break; + + } + tokens[i] = token; + columnOffset++; + + } + return null; + } + public override global::Dapper.AOT.Test.Integration.TypeWithSomeEnum Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + { + global::Dapper.AOT.Test.Integration.TypeWithSomeEnum 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.Value = reader.GetFieldValue(columnOffset); + break; + case 5: + result.Value = 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 { name = default(string)!, value = default(global::Dapper.AOT.Test.Integration.SomeEnum) }); // expected shape + var ps = cmd.Parameters; + global::System.Data.Common.DbParameter p; + p = cmd.CreateParameter(); + p.ParameterName = "name"; + p.DbType = global::System.Data.DbType.String; + p.Direction = global::System.Data.ParameterDirection.Input; + SetValueWithDefaultSize(p, typed.name); + ps.Add(p); + + p = cmd.CreateParameter(); + p.ParameterName = "value"; + p.Direction = global::System.Data.ParameterDirection.Input; + p.Value = AsValue(typed.value); + ps.Add(p); + + } + public override void UpdateParameters(in global::Dapper.UnifiedCommand cmd, object? args) + { + var typed = Cast(args, static () => new { name = default(string)!, value = default(global::Dapper.AOT.Test.Integration.SomeEnum) }); // expected shape + var ps = cmd.Parameters; + ps[0].Value = AsValue(typed.name); + ps[1].Value = AsValue(typed.value); + + } + + } + + + } +} +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/SqliteUsage.output.netfx.cs b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.netfx.cs new file mode 100644 index 0000000..6d007db --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.netfx.cs @@ -0,0 +1,171 @@ +#nullable enable +namespace Dapper.AOT // interceptors must be in a known namespace +{ + file static class DapperGeneratedInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\SqliteUsage.input.cs", 11, 23)] + internal static int Execute0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Execute, HasParameters, Text, KnownParameters + // takes parameter: + // parameter map: name value + 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).Execute(param); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute("Interceptors\\SqliteUsage.input.cs", 14, 23)] + internal static global::System.Collections.Generic.IEnumerable Query1(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, Text, BindResultsByName + // returns data: global::Dapper.AOT.Test.Integration.TypeWithSomeEnum + 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(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.Text, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(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 1113510858U when NormalizedEquals(name, "value"): + token = type == typeof(global::Dapper.AOT.Test.Integration.SomeEnum) ? 2 : 5; + break; + + } + tokens[i] = token; + columnOffset++; + + } + return null; + } + public override global::Dapper.AOT.Test.Integration.TypeWithSomeEnum Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + { + global::Dapper.AOT.Test.Integration.TypeWithSomeEnum 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.Value = reader.GetFieldValue(columnOffset); + break; + case 5: + result.Value = 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 { name = default(string)!, value = default(global::Dapper.AOT.Test.Integration.SomeEnum) }); // expected shape + var ps = cmd.Parameters; + global::System.Data.Common.DbParameter p; + p = cmd.CreateParameter(); + p.ParameterName = "name"; + p.DbType = global::System.Data.DbType.String; + p.Direction = global::System.Data.ParameterDirection.Input; + SetValueWithDefaultSize(p, typed.name); + ps.Add(p); + + p = cmd.CreateParameter(); + p.ParameterName = "value"; + p.Direction = global::System.Data.ParameterDirection.Input; + p.Value = AsValue(typed.value); + ps.Add(p); + + } + public override void UpdateParameters(in global::Dapper.UnifiedCommand cmd, object? args) + { + var typed = Cast(args, static () => new { name = default(string)!, value = default(global::Dapper.AOT.Test.Integration.SomeEnum) }); // expected shape + var ps = cmd.Parameters; + ps[0].Value = AsValue(typed.name); + ps[1].Value = AsValue(typed.value); + + } + + } + + + } +} +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/SqliteUsage.output.netfx.txt b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.netfx.txt new file mode 100644 index 0000000..47c3de5 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.netfx.txt @@ -0,0 +1,12 @@ +Input code has 1 diagnostics from 'Interceptors/SqliteUsage.input.cs': + +Hidden CS8019 Interceptors/SqliteUsage.input.cs L1 C1 +Unnecessary using directive. +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 2 of 2 possible call-sites using 2 interceptors, 1 commands and 1 readers +Output code has 1 diagnostics from 'Interceptors/SqliteUsage.input.cs': + +Hidden CS8019 Interceptors/SqliteUsage.input.cs L1 C1 +Unnecessary using directive. diff --git a/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.txt b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.txt new file mode 100644 index 0000000..47c3de5 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.output.txt @@ -0,0 +1,12 @@ +Input code has 1 diagnostics from 'Interceptors/SqliteUsage.input.cs': + +Hidden CS8019 Interceptors/SqliteUsage.input.cs L1 C1 +Unnecessary using directive. +Generator produced 1 diagnostics: + +Hidden DAP000 L1 C1 +Dapper.AOT handled 2 of 2 possible call-sites using 2 interceptors, 1 commands and 1 readers +Output code has 1 diagnostics from 'Interceptors/SqliteUsage.input.cs': + +Hidden CS8019 Interceptors/SqliteUsage.input.cs L1 C1 +Unnecessary using directive. diff --git a/test/Dapper.AOT.Test/Interceptors/SqliteUsage.snapshot.cs b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.snapshot.cs new file mode 100644 index 0000000..28fe1c9 --- /dev/null +++ b/test/Dapper.AOT.Test/Interceptors/SqliteUsage.snapshot.cs @@ -0,0 +1,171 @@ +#nullable enable +namespace Dapper.AOT // interceptors must be in a known namespace +{ + file static class DapperGeneratedInterceptors + { + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(@"/_/test/Dapper.AOT.Test/Interceptors/SqliteUsage.input.cs", 11, 23)] + internal static int Execute0(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Execute, HasParameters, Text, KnownParameters + // takes parameter: + // parameter map: name value + 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).Execute(param); + + } + + [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(@"/_/test/Dapper.AOT.Test/Interceptors/SqliteUsage.input.cs", 14, 23)] + internal static global::System.Collections.Generic.IEnumerable Query1(this global::System.Data.IDbConnection cnn, string sql, object? param, global::System.Data.IDbTransaction? transaction, bool buffered, int? commandTimeout, global::System.Data.CommandType? commandType) + { + // Query, TypedResult, Buffered, Text, BindResultsByName + // returns data: global::Dapper.AOT.Test.Integration.TypeWithSomeEnum + 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(buffered is true); + global::System.Diagnostics.Debug.Assert(param is null); + + return global::Dapper.DapperAotExtensions.Command(cnn, transaction, sql, global::System.Data.CommandType.Text, commandTimeout.GetValueOrDefault(), DefaultCommandFactory).QueryBuffered(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 1113510858U when NormalizedEquals(name, "value"): + token = type == typeof(global::Dapper.AOT.Test.Integration.SomeEnum) ? 2 : 5; + break; + + } + tokens[i] = token; + columnOffset++; + + } + return null; + } + public override global::Dapper.AOT.Test.Integration.TypeWithSomeEnum Read(global::System.Data.Common.DbDataReader reader, global::System.ReadOnlySpan tokens, int columnOffset, object? state) + { + global::Dapper.AOT.Test.Integration.TypeWithSomeEnum 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.Value = reader.GetFieldValue(columnOffset); + break; + case 5: + result.Value = 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 { name = default(string)!, value = default(global::Dapper.AOT.Test.Integration.SomeEnum) }); // expected shape + var ps = cmd.Parameters; + global::System.Data.Common.DbParameter p; + p = cmd.CreateParameter(); + p.ParameterName = "name"; + p.DbType = global::System.Data.DbType.String; + p.Direction = global::System.Data.ParameterDirection.Input; + SetValueWithDefaultSize(p, typed.name); + ps.Add(p); + + p = cmd.CreateParameter(); + p.ParameterName = "value"; + p.Direction = global::System.Data.ParameterDirection.Input; + p.Value = AsValue(typed.value); + ps.Add(p); + + } + public override void UpdateParameters(in global::Dapper.UnifiedCommand cmd, object? args) + { + var typed = Cast(args, static () => new { name = default(string)!, value = default(global::Dapper.AOT.Test.Integration.SomeEnum) }); // expected shape + var ps = cmd.Parameters; + ps[0].Value = AsValue(typed.name); + ps[1].Value = AsValue(typed.value); + + } + + } + + + } +} +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