From 3d116f0723a3636e75e168ccf14e8e586971e8c5 Mon Sep 17 00:00:00 2001 From: "fabien.menager" Date: Wed, 15 Oct 2025 21:27:53 +0200 Subject: [PATCH 1/2] Update dependencies --- src/Directory.Build.props | 2 +- ...ntityFrameworkCore.BulkInsert.MySql.csproj | 2 +- ...tityFrameworkCore.BulkInsert.Oracle.csproj | 4 ++-- ...yFrameworkCore.BulkInsert.Benchmark.csproj | 8 ++++---- ...ntityFrameworkCore.BulkInsert.Tests.csproj | 20 +++++++++---------- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 53fbcdf..8ce036d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -47,7 +47,7 @@ - + diff --git a/src/PhenX.EntityFrameworkCore.BulkInsert.MySql/PhenX.EntityFrameworkCore.BulkInsert.MySql.csproj b/src/PhenX.EntityFrameworkCore.BulkInsert.MySql/PhenX.EntityFrameworkCore.BulkInsert.MySql.csproj index 1ec986b..59cd250 100644 --- a/src/PhenX.EntityFrameworkCore.BulkInsert.MySql/PhenX.EntityFrameworkCore.BulkInsert.MySql.csproj +++ b/src/PhenX.EntityFrameworkCore.BulkInsert.MySql/PhenX.EntityFrameworkCore.BulkInsert.MySql.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/PhenX.EntityFrameworkCore.BulkInsert.Oracle/PhenX.EntityFrameworkCore.BulkInsert.Oracle.csproj b/src/PhenX.EntityFrameworkCore.BulkInsert.Oracle/PhenX.EntityFrameworkCore.BulkInsert.Oracle.csproj index 5056ecc..0fe7376 100644 --- a/src/PhenX.EntityFrameworkCore.BulkInsert.Oracle/PhenX.EntityFrameworkCore.BulkInsert.Oracle.csproj +++ b/src/PhenX.EntityFrameworkCore.BulkInsert.Oracle/PhenX.EntityFrameworkCore.BulkInsert.Oracle.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj index ff1122a..927cf43 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark/PhenX.EntityFrameworkCore.BulkInsert.Benchmark.csproj @@ -7,7 +7,7 @@ - + @@ -16,13 +16,13 @@ - + - - + + diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj index 37ff290..553af61 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj @@ -14,10 +14,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -25,23 +25,23 @@ - - - - - + + + + + - + - + - + From 19d6ef158ce439ee721c83a30b3f7b9a705aaac3 Mon Sep 17 00:00:00 2001 From: "fabien.menager" Date: Wed, 15 Oct 2025 22:32:31 +0200 Subject: [PATCH 2/2] Handle converters returning value types (like with SmartEnum) - Fixes #74 --- .../Metadata/PropertyAccessor.cs | 3 +- .../DbContext/TestDbContext.cs | 5 +++ .../DbContext/TestEntityWithSmartEnum.cs | 11 +++++ .../DbContext/TestSmartEnum.cs | 12 +++++ ...ntityFrameworkCore.BulkInsert.Tests.csproj | 4 ++ .../Tests/Various/VariousTestsBase.cs | 45 +++++++++++++++++++ .../Tests/Various/VariousTestsMySql.cs | 12 +++++ .../Tests/Various/VariousTestsOracle.cs | 12 +++++ .../Tests/Various/VariousTestsPostgreSql.cs | 12 +++++ .../Tests/Various/VariousTestsSqlServer.cs | 12 +++++ .../Tests/Various/VariousTestsSqlite.cs | 12 +++++ 11 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestEntityWithSmartEnum.cs create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestSmartEnum.cs create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsMySql.cs create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsOracle.cs create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsPostgreSql.cs create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlServer.cs create mode 100644 tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlite.cs diff --git a/src/PhenX.EntityFrameworkCore.BulkInsert/Metadata/PropertyAccessor.cs b/src/PhenX.EntityFrameworkCore.BulkInsert/Metadata/PropertyAccessor.cs index beee387..f66a813 100644 --- a/src/PhenX.EntityFrameworkCore.BulkInsert/Metadata/PropertyAccessor.cs +++ b/src/PhenX.EntityFrameworkCore.BulkInsert/Metadata/PropertyAccessor.cs @@ -64,7 +64,8 @@ internal static class PropertyAccessor // instance => converter(body) var invokeConverter = Expression.Invoke(converter, converterInput); - if (body.Type.IsClass) + // If the property is a reference type, we need to check for null before calling the converter + if (body.Type.IsClass && !invokeConverter.Type.IsValueType) { // instance => body == null ? null : converter(body) var nullCondition = Expression.Equal(body, Expression.Constant(null, body.Type)); diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContext.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContext.cs index f40911b..c4835a5 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContext.cs +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestDbContext.cs @@ -1,6 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SmartEnum.EFCore; + namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; public class TestDbContext : TestDbContextBase @@ -11,6 +13,7 @@ public class TestDbContext : TestDbContextBase public DbSet TestEntitiesWithGuidId { get; set; } = null!; public DbSet TestEntitiesWithConverter { get; set; } = null!; public DbSet TestEntitiesWithComplexType { get; set; } = null!; + public DbSet TestEntitiesWithSmartEnum { get; set; } = null!; public DbSet Students { get; set; } = null!; public DbSet Courses { get; set; } = null!; @@ -18,6 +21,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + modelBuilder.ConfigureSmartEnum(); + modelBuilder.Entity(builder => { builder.Property(e => e.CreatedAt) diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestEntityWithSmartEnum.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestEntityWithSmartEnum.cs new file mode 100644 index 0000000..11a8eef --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestEntityWithSmartEnum.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +[PrimaryKey(nameof(Id))] +public class TestEntityWithSmartEnum : TestEntityBase +{ + public int Id { get; set; } + + public TestSmartEnum Enum { get; set; } = null!; +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestSmartEnum.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestSmartEnum.cs new file mode 100644 index 0000000..02aad51 --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContext/TestSmartEnum.cs @@ -0,0 +1,12 @@ +using Ardalis.SmartEnum; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +public class TestSmartEnum : SmartEnum +{ + private TestSmartEnum(string name, int value) : base(name, value) + { + } + + public static readonly TestSmartEnum Value = new TestSmartEnum("test", 1); +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj index 553af61..76dc45b 100644 --- a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/PhenX.EntityFrameworkCore.BulkInsert.Tests.csproj @@ -43,6 +43,10 @@ + + + + diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs new file mode 100644 index 0000000..e3769f5 --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsBase.cs @@ -0,0 +1,45 @@ +using FluentAssertions; + +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +using Xunit; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; + +public abstract class VariousTestsBase(TestDbContainer dbContainer) : IAsyncLifetime + where TDbContext : TestDbContext, new() +{ + private readonly Guid _run = Guid.NewGuid(); + private TDbContext _context = null!; + + public async Task InitializeAsync() + { + _context = await dbContainer.CreateContextAsync("various"); + } + + public Task DisposeAsync() + { + _context.Dispose(); + return Task.CompletedTask; + } + + [SkippableTheory] + [CombinatorialData] + public async Task InsertSmartEnumEntities(InsertStrategy strategy) + { + // Arrange + var entities = new List + { + new TestEntityWithSmartEnum { TestRun = _run, Enum = TestSmartEnum.Value}, + new TestEntityWithSmartEnum { TestRun = _run, Enum = TestSmartEnum.Value} + }; + + // Act + var insertedEntities = await _context.InsertWithStrategyAsync(strategy, entities); + + // Assert + insertedEntities.Should().BeEquivalentTo(entities, + o => o.RespectingRuntimeTypes().Excluding(e => e.Id)); + } +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsMySql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsMySql.cs new file mode 100644 index 0000000..30e2a8d --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsMySql.cs @@ -0,0 +1,12 @@ +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +using Xunit; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; + +[Trait("Category", "MySql")] +[Collection(TestDbContainerMySqlCollection.Name)] +public class VariousTestsMySql(TestDbContainerMySql dbContainer) : VariousTestsBase(dbContainer) +{ +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsOracle.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsOracle.cs new file mode 100644 index 0000000..8a0361d --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsOracle.cs @@ -0,0 +1,12 @@ +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +using Xunit; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; + +[Trait("Category", "Oracle")] +[Collection(TestDbContainerOracleCollection.Name)] +public class VariousTestsOracle(TestDbContainerOracle dbContainer) : VariousTestsBase(dbContainer) +{ +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsPostgreSql.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsPostgreSql.cs new file mode 100644 index 0000000..099c3f6 --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsPostgreSql.cs @@ -0,0 +1,12 @@ +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +using Xunit; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; + +[Trait("Category", "PostgreSql")] +[Collection(TestDbContainerPostgreSqlCollection.Name)] +public class VariousTestsPostgreSql(TestDbContainerPostgreSql dbContainer) : VariousTestsBase(dbContainer) +{ +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlServer.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlServer.cs new file mode 100644 index 0000000..b9ae700 --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlServer.cs @@ -0,0 +1,12 @@ +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +using Xunit; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; + +[Trait("Category", "SqlServer")] +[Collection(TestDbContainerSqlServerCollection.Name)] +public class VariousTestsSqlServer(TestDbContainerSqlServer dbContainer) : VariousTestsBase(dbContainer) +{ +} diff --git a/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlite.cs b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlite.cs new file mode 100644 index 0000000..21e0055 --- /dev/null +++ b/tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/Tests/Various/VariousTestsSqlite.cs @@ -0,0 +1,12 @@ +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContainer; +using PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext; + +using Xunit; + +namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.Tests.Various; + +[Trait("Category", "Sqlite")] +[Collection(TestDbContainerSqliteCollection.Name)] +public class VariousTestsSqlite(TestDbContainerSqlite dbContainer) : VariousTestsBase(dbContainer) +{ +}