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