Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,8 @@ fabric.properties
# Vitepress
docs/.vitepress/dist
docs/.vitepress/cache

.direnv
.envrc
.launch
PhenX.EntityFrameworkCore.BulkInsert.sln.DotSettings.user
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,22 @@ internal static class PropertyAccessor
// instance => converter(body)
var invokeConverter = Expression.Invoke(converter, converterInput);

// If the property is a reference type, we need to check for null before calling the converter
if (body.Type.IsClass && !invokeConverter.Type.IsValueType)
// For nullable types, only call the converter if the value is not null
if (Nullable.GetUnderlyingType(body.Type) != null)
{
// Nullable<> as common result type for the converter and constant 'null'
var resultType = typeof(Nullable<>).MakeGenericType(invokeConverter.Type);

// instance => body == null ? null : (Nullable<T>)converter(body)
var nullCondition = Expression.Equal(body, Expression.Constant(null, body.Type));
var nullResult = Expression.Constant(null, resultType);
var nonNullResult = Expression.Convert(invokeConverter, resultType);

body = Expression.Condition(nullCondition, nullResult, nonNullResult);
}
// If the property is a reference type, we need to check for null before calling the converter. even if the
// property is not nullable
else if (body.Type.IsClass && !invokeConverter.Type.IsValueType)
{
// instance => body == null ? null : converter(body)
var nullCondition = Expression.Equal(body, Expression.Constant(null, body.Type));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public abstract class TestDbContainer<TBuilderEntity, TContainerEntity>(IMessage

protected abstract TBuilderEntity CreateBuilder();

protected virtual string DbmsName => typeof(TContainerEntity).Name.Replace("Container", "");
protected virtual string DbmsName => GetType().Name.Replace("TestDbContainer", "");

protected override TBuilderEntity Configure()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,177 +1,179 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

using SmartEnum.EFCore;

namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext;

public class TestDbContext : TestDbContextBase
{
public DbSet<TestEntity> TestEntities { get; set; } = null!;
public DbSet<TestEntityWithSimpleTypes> TestEntitiesWithSimpleTypes { get; set; } = null!;
public DbSet<TestEntityWithJson> TestEntitiesWithJson { get; set; } = null!;
public DbSet<TestEntityWithGuidId> TestEntitiesWithGuidId { get; set; } = null!;
public DbSet<TestEntityWithConverters> TestEntitiesWithConverter { get; set; } = null!;
public DbSet<TestEntityWithComplexType> TestEntitiesWithComplexType { get; set; } = null!;
public DbSet<TestEntityWithSmartEnum> TestEntitiesWithSmartEnum { get; set; } = null!;
public DbSet<TestEntityWithSpecialColumnNames> TestEntitiesWithSpecialColumnNames { get; set; } = null!;
public DbSet<Student> Students { get; set; } = null!;
public DbSet<Course> Courses { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.ConfigureSmartEnum();

modelBuilder.Entity<TestEntityWithConverters>(builder =>
{
builder.Property(e => e.CreatedAt)
.HasConversion(new DateTimeToBinaryConverter());
});

modelBuilder.Entity<TestEntityWithComplexType>(builder =>
{
builder
.ComplexProperty(e => e.OwnedComplexType)
.IsRequired();
});

// Many-to-many with shadow property
modelBuilder.Entity<Student>()
.HasMany(s => s.Courses)
.WithMany(c => c.Students)
.UsingEntity<Dictionary<string, object>>(
"StudentCourse",
j => j.HasOne<Course>().WithMany().HasForeignKey("CourseId"),
j => j.HasOne<Student>().WithMany().HasForeignKey("StudentId"),
j =>
{
j.Property<DateTime>("EnrolledAt");
j.HasKey("StudentId", "CourseId");
}
);

// Keyless entity type
modelBuilder.Entity<TestEntityKeyless>(builder =>
{
builder.HasNoKey();
// ToView will use the given table name read-only, it doesn't have to actually be a database view.
// We just reuse the table for the standard TestEntity.
builder.ToView("test_entity");
});
}
}

public class TestDbContextPostgreSql : TestDbContext
{
public DbSet<TestEntityWithArrays> TestEntitiesWithArrays { get; set; } = null!;
public DbSet<TestEntityWithEnumList> TestEntitiesWithEnumList { get; set; } = null!;
public DbSet<TestEntityWithEnumArray> TestEntitiesWithEnumArray { get; set; } = null!;
public DbSet<TestEntityWithIntList> TestEntitiesWithIntList { get; set; } = null!;
public DbSet<TestEntityWithEnumListExplicitType> TestEntitiesWithEnumListExplicitType { get; set; } = null!;
public DbSet<TestRecordWithEnumList> TestRecordsWithEnumList { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString("jsonb");
b.Property(x => x.JsonObject).AsJsonString("jsonb");
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});

modelBuilder.Entity<TestEntityWithEnumListExplicitType>(b =>
{
b.Property(x => x.EnumList).HasColumnType("integer[]");
});

modelBuilder.Entity<TestRecordWithEnumList>(b =>
{
b.Property(x => x.TestRun).HasColumnName("test_run");
b.Property(x => x.Values).HasColumnName("values");
});
}
}

public class TestDbContextMySql : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString("json");
b.Property(x => x.JsonObject).AsJsonString("json");
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});
}
}

public class TestDbContextSqlServer : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString(null);
b.Property(x => x.JsonObject).AsJsonString(null);
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});
}
}

public class TestDbContextSqlite : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString(null);
b.Property(x => x.JsonObject).AsJsonString(null);
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});
}
}

public class TestDbContextOracle : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString(null);
b.Property(x => x.JsonObject).AsJsonString(null);
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("nvarchar2(255)");
});
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

using SmartEnum.EFCore;

namespace PhenX.EntityFrameworkCore.BulkInsert.Tests.DbContext;

public class TestDbContext : TestDbContextBase
{
public DbSet<TestEntity> TestEntities { get; set; } = null!;
public DbSet<TestEntityWithSimpleTypes> TestEntitiesWithSimpleTypes { get; set; } = null!;
public DbSet<TestEntityWithJson> TestEntitiesWithJson { get; set; } = null!;
public DbSet<TestEntityWithGuidId> TestEntitiesWithGuidId { get; set; } = null!;
public DbSet<TestEntityWithConverters> TestEntitiesWithConverter { get; set; } = null!;
public DbSet<TestEntityWithComplexType> TestEntitiesWithComplexType { get; set; } = null!;
public DbSet<TestEntityWithSmartEnum> TestEntitiesWithSmartEnum { get; set; } = null!;
public DbSet<TestEntityWithSpecialColumnNames> TestEntitiesWithSpecialColumnNames { get; set; } = null!;
public DbSet<Student> Students { get; set; } = null!;
public DbSet<Course> Courses { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.ConfigureSmartEnum();

modelBuilder.Entity<TestEntityWithConverters>(builder =>
{
builder.Property(e => e.CreatedAt)
.HasConversion(new DateTimeToBinaryConverter());
});

modelBuilder.Entity<TestEntityWithComplexType>(builder =>
{
builder
.ComplexProperty(e => e.OwnedComplexType)
.IsRequired();
});

// Many-to-many with shadow property
modelBuilder.Entity<Student>()
.HasMany(s => s.Courses)
.WithMany(c => c.Students)
.UsingEntity<Dictionary<string, object>>(
"StudentCourse",
j => j.HasOne<Course>().WithMany().HasForeignKey("CourseId"),
j => j.HasOne<Student>().WithMany().HasForeignKey("StudentId"),
j =>
{
j.Property<DateTime>("EnrolledAt");
j.HasKey("StudentId", "CourseId");
}
);

// Keyless entity type
modelBuilder.Entity<TestEntityKeyless>(builder =>
{
builder.HasNoKey();
// ToView will use the given table name read-only, it doesn't have to actually be a database view.
// We just reuse the table for the standard TestEntity.
builder.ToView("test_entity");
});

modelBuilder.Entity<TestEntityWithNullableEnums>();
}
}

public class TestDbContextPostgreSql : TestDbContext
{
public DbSet<TestEntityWithArrays> TestEntitiesWithArrays { get; set; } = null!;
public DbSet<TestEntityWithEnumList> TestEntitiesWithEnumList { get; set; } = null!;
public DbSet<TestEntityWithEnumArray> TestEntitiesWithEnumArray { get; set; } = null!;
public DbSet<TestEntityWithIntList> TestEntitiesWithIntList { get; set; } = null!;
public DbSet<TestEntityWithEnumListExplicitType> TestEntitiesWithEnumListExplicitType { get; set; } = null!;
public DbSet<TestRecordWithEnumList> TestRecordsWithEnumList { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString("jsonb");
b.Property(x => x.JsonObject).AsJsonString("jsonb");
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});

modelBuilder.Entity<TestEntityWithEnumListExplicitType>(b =>
{
b.Property(x => x.EnumList).HasColumnType("integer[]");
});

modelBuilder.Entity<TestRecordWithEnumList>(b =>
{
b.Property(x => x.TestRun).HasColumnName("test_run");
b.Property(x => x.Values).HasColumnName("values");
});
}
}

public class TestDbContextMySql : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString("json");
b.Property(x => x.JsonObject).AsJsonString("json");
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});
}
}

public class TestDbContextSqlServer : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString(null);
b.Property(x => x.JsonObject).AsJsonString(null);
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});
}
}

public class TestDbContextSqlite : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString(null);
b.Property(x => x.JsonObject).AsJsonString(null);
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("text");
});
}
}

public class TestDbContextOracle : TestDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity<TestEntityWithJson>(b =>
{
b.Property(x => x.JsonArray).AsJsonString(null);
b.Property(x => x.JsonObject).AsJsonString(null);
});

modelBuilder.Entity<TestEntity>(b =>
{
b.Property(x => x.StringEnumValue).HasColumnType("nvarchar2(255)");
});
}
}
Loading
Loading