diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Benchmark/Specific/DbContextWrapper.cs index 7daf2f9f..7764f4bc 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "truncate \"NonRelatedEntities\";"; + foreach (var entity in EntitiesList) + { + var query = $"truncate \"{entity}\";"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Tests/WrapperResolver.cs index c0744c8e..63759d42 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Cockroach.Tests/WrapperResolver.cs @@ -15,9 +15,11 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "truncate \"NonRelatedEntities\";"; + const string truncateQuery = "truncate \"{0}\";"; - wrapper.Context.Database.ExecuteSqlRaw(query); + const string resetSequenceQuery = "select setval('\"{0}_{1}_seq\"', 1, false);"; + + wrapper.CleanDb(truncateQuery, resetSequenceQuery); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Benchmark/Specific/DbContextWrapper.cs index b83abadb..ffb5f49d 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "truncate \"NonRelatedEntities\";"; + foreach (var entity in EntitiesList) + { + var query = $"truncate \"{entity}\";"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Tests/WrapperResolver.cs index 0c4cf6ae..08d912ff 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.CockroachMulti.Tests/WrapperResolver.cs @@ -15,9 +15,11 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "truncate \"NonRelatedEntities\";"; + const string truncateQuery = "truncate \"{0}\";"; - wrapper.Context.Database.ExecuteSqlRaw(query); + const string resetSequenceQuery = "select setval('\"{0}_{1}_seq\"', 1, false);"; + + wrapper.CleanDb(truncateQuery, resetSequenceQuery); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Benchmark/Specific/DbContextWrapper.cs index f34c654d..1d642f64 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "DELETE FROM \"NonRelatedEntities\";"; + foreach (var entity in EntitiesList) + { + var query = $"DELETE FROM \"{entity}\";"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Tests/WrapperResolver.cs index f43dd6b8..0ce69fa1 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird3.Tests/WrapperResolver.cs @@ -15,9 +15,11 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "DELETE FROM \"NonRelatedEntities\";"; + const string truncateQuery = "DELETE FROM \"{0}\";"; - wrapper.Context.Database.ExecuteSqlRaw(query); + const string resetSequenceQuery = "ALTER TABLE \"{0}\" ALTER COLUMN \"{1}\" RESTART WITH 0;"; + + wrapper.CleanDb(truncateQuery, resetSequenceQuery); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Benchmark/Specific/DbContextWrapper.cs index 14212a4c..fda504f3 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "DELETE FROM \"NonRelatedEntities\";"; + foreach (var entity in EntitiesList) + { + var query = $"DELETE FROM \"{entity}\";"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Tests/WrapperResolver.cs index cd095196..c4bfc568 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Firebird4.Tests/WrapperResolver.cs @@ -15,9 +15,11 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "DELETE FROM \"NonRelatedEntities\";"; + const string truncateQuery = "DELETE FROM \"{0}\";"; - wrapper.Context.Database.ExecuteSqlRaw(query); + const string resetSequenceQuery = "ALTER TABLE \"{0}\" ALTER COLUMN \"{1}\" RESTART WITH 1;"; + + wrapper.CleanDb(truncateQuery, resetSequenceQuery); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/EFCore.Extensions.SaveOptimizer.Internal.Tests.csproj b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/EFCore.Extensions.SaveOptimizer.Internal.Tests.csproj index 9c53a71e..8302a2f9 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/EFCore.Extensions.SaveOptimizer.Internal.Tests.csproj +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/EFCore.Extensions.SaveOptimizer.Internal.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Extensions/DbContextOptionsBuilderExtensions.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Extensions/DbContextOptionsBuilderExtensions.cs new file mode 100644 index 00000000..2c3c0811 --- /dev/null +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Extensions/DbContextOptionsBuilderExtensions.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; + +namespace EFCore.Extensions.SaveOptimizer.Internal.Tests.Extensions; + +public static class DbContextOptionsBuilderExtensions +{ + public static DbContextOptionsBuilder UseDynamicSqlLite(this DbContextOptionsBuilder builder) + where T : DbContext => + builder + .UseSqlite(GetConnectionString()) + .UseSnakeCaseNamingConvention(); + + private static string GetConnectionString() + { + DirectoryInfo directory = new(Path.Combine(Path.GetTempPath(), "db")); + + if (!directory.Exists) + { + directory.Create(); + } + + var dbName = $"test_{DateTime.Now:yyyy_MM_dd_hh_mm_ss}_{Guid.NewGuid()}.db"; + + var path = Path.Join(directory.FullName, dbName); + + return $"Data Source={path}"; + } +} diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Extensions/EntityTypeExtensionTests.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Extensions/EntityTypeExtensionTests.cs index c0e93fb7..8d4e738c 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Extensions/EntityTypeExtensionTests.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Extensions/EntityTypeExtensionTests.cs @@ -14,7 +14,7 @@ public class EntityTypeExtensionTests public EntityTypeExtensionTests() { DbContextOptionsBuilder options = - new DbContextOptionsBuilder().UseInMemoryDatabase("in_memory_db"); + new DbContextOptionsBuilder().UseDynamicSqlLite(); _sut = new TestDataContext(options.Options); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Services/QueryTranslatorServiceTests.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Services/QueryTranslatorServiceTests.cs index c3b290d6..f9c07750 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Services/QueryTranslatorServiceTests.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Services/QueryTranslatorServiceTests.cs @@ -1,5 +1,6 @@ using EFCore.Extensions.SaveOptimizer.Internal.Models; using EFCore.Extensions.SaveOptimizer.Internal.Services; +using EFCore.Extensions.SaveOptimizer.Internal.Tests.Extensions; using EFCore.Extensions.SaveOptimizer.Internal.Tests.Helpers; using EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext; using EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext.Models; @@ -21,8 +22,7 @@ public class QueryTranslatorServiceTests public QueryTranslatorServiceTests() { DbContextOptionsBuilder options = - new DbContextOptionsBuilder().UseInMemoryDatabase("in_memory_db") - .UseSnakeCaseNamingConvention(); + new DbContextOptionsBuilder().UseDynamicSqlLite(); _context = new TestDataContext(options.Options); _wrapper = new DataContextModelWrapper(() => _context); @@ -62,7 +62,7 @@ public void GivenTranslate_WhenConcurrencyToken_ShouldPropertyTranslate() // Assert result.SchemaName.Should().BeNull(); - result.TableName.Should().Be("second_level_entity"); + result.TableName.Should().Be("second_level_entities"); result.EntityState.Should().Be(EntityState.Added); result.EntityType.Should().BeSameAs(typeof(SecondLevelEntity)); result.PrimaryKeyNames.First().Should().Be("second_level_entity_id"); @@ -110,7 +110,7 @@ public void GivenTranslate_WhenCreatedSecondLevelEntity_ShouldPropertyTranslate( // Assert result.SchemaName.Should().BeNull(); - result.TableName.Should().Be("second_level_entity"); + result.TableName.Should().Be("second_level_entities"); result.EntityState.Should().Be(EntityState.Added); result.EntityType.Should().BeSameAs(typeof(SecondLevelEntity)); result.PrimaryKeyNames.First().Should().Be("second_level_entity_id"); @@ -158,7 +158,7 @@ public void GivenTranslate_WhenDeletedSecondLevelEntity_ShouldPropertyTranslate( // Assert result.SchemaName.Should().BeNull(); - result.TableName.Should().Be("second_level_entity"); + result.TableName.Should().Be("second_level_entities"); result.EntityState.Should().Be(EntityState.Deleted); result.EntityType.Should().BeSameAs(typeof(SecondLevelEntity)); result.PrimaryKeyNames.First().Should().Be("second_level_entity_id"); @@ -264,7 +264,7 @@ public void GivenTranslate_WhenUpdatedSecondLevelEntity_ShouldPropertyTranslate( // Assert result.SchemaName.Should().BeNull(); - result.TableName.Should().Be("second_level_entity"); + result.TableName.Should().Be("second_level_entities"); result.EntityState.Should().Be(EntityState.Modified); result.EntityType.Should().BeSameAs(typeof(SecondLevelEntity)); result.PrimaryKeyNames.First().Should().Be("second_level_entity_id"); @@ -280,4 +280,60 @@ public void GivenTranslate_WhenUpdatedSecondLevelEntity_ShouldPropertyTranslate( { "second_level_entity_id", "aaa" } }); } + + [Fact] + public void GivenTranslate_WhenNonInsertablePrimaryKeyEntity_ShouldPropertyTranslate() + { + // Arrange + NonInsertablePrimaryKeyEntity entity = new() + { + SomeProperty = "Property" + }; + + EntityEntry entry = _context.Entry(entity); + entry.State = EntityState.Added; + entry.Property(x => x.SomeProperty).IsModified = true; + + // Act + QueryDataModel? result = _target.Translate(_wrapper, entry); + + // Assert + result.SchemaName.Should().BeNull(); + result.TableName.Should().Be("non_insertable_primary_key_entities"); + result.EntityState.Should().Be(EntityState.Added); + result.EntityType.Should().BeSameAs(typeof(NonInsertablePrimaryKeyEntity)); + result.PrimaryKeyNames.First().Should().Be("non_insertable_primary_key_entity_id"); + result.ConcurrencyTokens?.Map().Should().BeNullOrEmpty(); + result.Data.Keys + .Should() + .BeEquivalentTo("some_property"); + } + + [Fact] + public void GivenTranslate_WhenInsertablePrimaryKeyEntity_ShouldPropertyTranslate() + { + // Arrange + InsertablePrimaryKeyEntity entity = new() + { + SomeProperty = "Property" + }; + + EntityEntry entry = _context.Entry(entity); + entry.State = EntityState.Added; + entry.Property(x => x.SomeProperty).IsModified = true; + + // Act + QueryDataModel? result = _target.Translate(_wrapper, entry); + + // Assert + result.SchemaName.Should().BeNull(); + result.TableName.Should().Be("insertable_primary_key_entities"); + result.EntityState.Should().Be(EntityState.Added); + result.EntityType.Should().BeSameAs(typeof(InsertablePrimaryKeyEntity)); + result.PrimaryKeyNames.First().Should().Be("insertable_primary_key_entity_id"); + result.ConcurrencyTokens?.Map().Should().BeNullOrEmpty(); + result.Data.Keys + .Should() + .BeEquivalentTo("insertable_primary_key_entity_id", "some_property"); + } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/Models/InsertablePrimaryKeyEntity.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/Models/InsertablePrimaryKeyEntity.cs new file mode 100644 index 00000000..c6b4b8b6 --- /dev/null +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/Models/InsertablePrimaryKeyEntity.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +// ReSharper disable All + +namespace EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext.Models; + +public class InsertablePrimaryKeyEntity +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Key] + public Guid InsertablePrimaryKeyEntityId { get; set; } + + public string? SomeProperty { get; set; } +} diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/Models/NonInsertablePrimaryKeyEntity.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/Models/NonInsertablePrimaryKeyEntity.cs new file mode 100644 index 00000000..062aef0c --- /dev/null +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/Models/NonInsertablePrimaryKeyEntity.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +// ReSharper disable All + +namespace EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext.Models; + +public class NonInsertablePrimaryKeyEntity +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Key] + public int NonInsertablePrimaryKeyEntityId { get; set; } + + public string? SomeProperty { get; set; } +} diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/TestDataContext.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/TestDataContext.cs index f7db4da9..bf76179a 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/TestDataContext.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/TestContext/TestDataContext.cs @@ -17,6 +17,9 @@ public class TestDataContext : DbContext public DbSet AttributeEntityLogs { get; set; } + public DbSet InsertablePrimaryKeyEntities { get; set; } + public DbSet NonInsertablePrimaryKeyEntities { get; set; } + public TestDataContext(DbContextOptions options) : base(options) { } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Wrapper/DataContextModelWrapperTests.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Wrapper/DataContextModelWrapperTests.cs index 5cbc2173..b949e7b2 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Wrapper/DataContextModelWrapperTests.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal.Tests/Wrapper/DataContextModelWrapperTests.cs @@ -1,4 +1,5 @@ -using EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext; +using EFCore.Extensions.SaveOptimizer.Internal.Tests.Extensions; +using EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext; using EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext.LogEntities; using EFCore.Extensions.SaveOptimizer.Internal.Tests.TestContext.Models; using EFCore.Extensions.SaveOptimizer.Internal.Wrappers; @@ -13,8 +14,7 @@ public class DataContextModelWrapperTests public DataContextModelWrapperTests() { DbContextOptionsBuilder options = - new DbContextOptionsBuilder().UseInMemoryDatabase("in_memory_db") - .UseSnakeCaseNamingConvention(); + new DbContextOptionsBuilder().UseDynamicSqlLite(); TestDataContext context = new(options.Options); _sut = new DataContextModelWrapper(() => context); } @@ -90,11 +90,11 @@ public void GivenGetTableName_ShouldReturnsProperTableNameEveryTime() // Assert results.Should() .ContainInOrder( - "first_level_entity", - "first_level_entity", - "second_level_entity", - "first_level_entity", - "second_level_entity", - "third_level_entity"); + "first_level_entities", + "first_level_entities", + "second_level_entities", + "first_level_entities", + "second_level_entities", + "third_level_entities"); } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Factories/QueryBuilderFactory.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Factories/QueryBuilderFactory.cs index fb9cf4d5..ddc1134b 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Factories/QueryBuilderFactory.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Factories/QueryBuilderFactory.cs @@ -11,7 +11,7 @@ public IQueryBuilder Query(QueryBuilderConfiguration? configuration) => { QueryBuilderType.SqlServer => new SqlServerQueryBuilder(configuration), QueryBuilderType.SqLite => new SqliteQueryBuilder(configuration), - QueryBuilderType.Oracle => new OracleAllQueryBuilder(configuration), + QueryBuilderType.Oracle => new OracleQueryBuilder(configuration), QueryBuilderType.Firebird => new FirebirdQueryBuilder(configuration), QueryBuilderType.MySql => new MySqlQueryBuilder(configuration), QueryBuilderType.Postgres => new PostgresQueryBuilder(configuration), diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/DbContextExecutorService.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/DbContextExecutorService.cs index c2966f89..489191df 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/DbContextExecutorService.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/DbContextExecutorService.cs @@ -67,12 +67,7 @@ public int SaveChangesOptimized(DbContext context, QueryExecutionConfiguration? foreach (ISqlCommandModel sql in queries.Queries) { var affected = _queryExecutorService.Execute(context, configuration, transaction, sql, timeout); - - if (affected < 0 && sql.ExpectedRows.HasValue) // Oracle insert many fallback - { - affected = sql.ExpectedRows.Value; - } - + rows += affected; } @@ -157,11 +152,6 @@ public async Task SaveChangesOptimizedAsync(DbContext context, .ExecuteAsync(context, configuration, transaction, sql, timeout, cancellationToken) .ConfigureAwait(false); - if (affected < 0 && sql.ExpectedRows.HasValue) // Oracle insert many fallback - { - affected = sql.ExpectedRows.Value; - } - rows += affected; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/QueryTranslatorService.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/QueryTranslatorService.cs index 88150556..5c180967 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/QueryTranslatorService.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Internal/Services/QueryTranslatorService.cs @@ -45,19 +45,17 @@ public class QueryTranslatorService : IQueryTranslatorService { primaryKeyNames.Add(column.ColumnName); - data.Add(column.ColumnName, GetSqlValueModel(property, column)); + if (ShouldStoreValue(property, entry)) + { + data.Add(column.ColumnName, GetSqlValueModel(property, column)); + } propertiesCount++; continue; } - if (entry.State == EntityState.Modified && !property.IsModified) - { - continue; - } - - if (property.Metadata.ValueGenerated != ValueGenerated.Never) + if (!ShouldStoreValue(property, entry)) { continue; } @@ -74,6 +72,31 @@ public class QueryTranslatorService : IQueryTranslatorService propertiesCount); } + private static bool ShouldStoreValue(PropertyEntry property, EntityEntry entry) + { + if (property.Metadata.IsPrimaryKey() && entry.State != EntityState.Added) + { + return true; + } + + if (entry.State == EntityState.Modified && !property.IsModified) + { + return false; + } + + if (property.Metadata.ValueGenerated == ValueGenerated.Never) + { + return true; + } + + if (property.Metadata.GetBeforeSaveBehavior() != PropertySaveBehavior.Save) + { + return false; + } + + return !property.IsTemporary; + } + private static SqlValueModel GetSqlValueModel(MemberEntry property, PropertyTypeModel column) { var value = property.CurrentValue; diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220629141127_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220715101425_InitialCreate.Designer.cs similarity index 82% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220629141127_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220715101425_InitialCreate.Designer.cs index 278c220c..f93c03ab 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220629141127_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220715101425_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.Cockroach.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141127_InitialCreate")] + [Migration("20220715101425_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -24,6 +24,22 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Some") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220629141127_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220715101425_InitialCreate.cs similarity index 75% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220629141127_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220715101425_InitialCreate.cs index 4b609722..dd8a6b12 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220629141127_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/20220715101425_InitialCreate.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -9,6 +10,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Some = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +52,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/EntitiesContextModelSnapshot.cs index e4b80a8d..988375aa 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Cockroach/Migrations/EntitiesContextModelSnapshot.cs @@ -22,6 +22,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Some") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220629141134_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220715101439_InitialCreate.Designer.cs similarity index 82% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220629141134_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220715101439_InitialCreate.Designer.cs index a37a604f..9d84f38f 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220629141134_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220715101439_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.CockroachMulti.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141134_InitialCreate")] + [Migration("20220715101439_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -24,6 +24,22 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Some") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220629141134_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220715101439_InitialCreate.cs similarity index 75% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220629141134_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220715101439_InitialCreate.cs index ac44471a..761993ea 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220629141134_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/20220715101439_InitialCreate.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -9,6 +10,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Some = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +52,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/EntitiesContextModelSnapshot.cs index 6dd29f20..52e705aa 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.CockroachMulti/Migrations/EntitiesContextModelSnapshot.cs @@ -22,6 +22,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Some") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Firebird3DesignTimeFactory.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Firebird3DesignTimeFactory.cs index 573c0e32..549a7360 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Firebird3DesignTimeFactory.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Firebird3DesignTimeFactory.cs @@ -1,14 +1,45 @@ -using Microsoft.EntityFrameworkCore; +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata; +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata.Internal; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Logging; // ReSharper disable UnusedMember.Global +#pragma warning disable EF1001 + namespace EFCore.Extensions.SaveOptimizer.Model.Firebird3; public class Firebird3DesignTimeFactory : IDesignTimeDbContextFactory, ITestTimeDbContextFactory { + static Firebird3DesignTimeFactory() + { + static void Builder(ModelBuilder modelBuilder) + { + const string decimalColumnType = "DECIMAL(12,6)"; + + modelBuilder.Entity(eb => + { + eb.Property(b => b.SomeNullableDecimalProperty) + .HasPrecision(12, 6) + .HasColumnType(decimalColumnType); + + eb.Property(b => b.SomeNonNullableDecimalProperty) + .HasPrecision(12, 6) + .HasColumnType(decimalColumnType); + }); + + modelBuilder.Entity(eb => + { + eb.Property(b => b.Id) + .HasAnnotation(FbAnnotationNames.ValueGenerationStrategy, FbValueGenerationStrategy.IdentityColumn); + }); + } + + EntitiesContext.AdditionalBuilders.Add(Builder); + } + public EntitiesContext CreateDbContext(string[] args) => CreateDbContext(args, null); public EntitiesContext CreateDbContext(string[] args, ILoggerFactory? factory) diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220629141107_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220715101346_InitialCreate.Designer.cs similarity index 82% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220629141107_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220715101346_InitialCreate.Designer.cs index 95fb4cab..4f3aef59 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220629141107_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220715101346_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.Firebird3.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141107_InitialCreate")] + [Migration("20220715101346_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -23,6 +23,21 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 31); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasAnnotation("Fb:ValueGenerationStrategy", FbValueGenerationStrategy.IdentityColumn); + + b.Property("Some") + .HasColumnType("BLOB SUB_TYPE TEXT"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220629141107_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220715101346_InitialCreate.cs similarity index 74% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220629141107_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220715101346_InitialCreate.cs index cb9d2db1..a3a25403 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220629141107_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/20220715101346_InitialCreate.cs @@ -1,4 +1,5 @@ using System; +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -9,6 +10,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Fb:ValueGenerationStrategy", FbValueGenerationStrategy.IdentityColumn), + Some = table.Column(type: "BLOB SUB_TYPE TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEnti~", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +52,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/EntitiesContextModelSnapshot.cs index 76631cc6..3ba2a6d5 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird3/Migrations/EntitiesContextModelSnapshot.cs @@ -21,6 +21,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 31); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasAnnotation("Fb:ValueGenerationStrategy", FbValueGenerationStrategy.IdentityColumn); + + b.Property("Some") + .HasColumnType("BLOB SUB_TYPE TEXT"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Firebird4DesignTimeFactory.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Firebird4DesignTimeFactory.cs index 4a0a7c85..71d3a760 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Firebird4DesignTimeFactory.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Firebird4DesignTimeFactory.cs @@ -1,14 +1,45 @@ -using Microsoft.EntityFrameworkCore; +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata; +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata.Internal; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Logging; // ReSharper disable UnusedMember.Global +#pragma warning disable EF1001 + namespace EFCore.Extensions.SaveOptimizer.Model.Firebird4; public class Firebird4DesignTimeFactory : IDesignTimeDbContextFactory, ITestTimeDbContextFactory { + static Firebird4DesignTimeFactory() + { + static void Builder(ModelBuilder modelBuilder) + { + const string decimalColumnType = "DECIMAL(12,6)"; + + modelBuilder.Entity(eb => + { + eb.Property(b => b.SomeNullableDecimalProperty) + .HasPrecision(12, 6) + .HasColumnType(decimalColumnType); + + eb.Property(b => b.SomeNonNullableDecimalProperty) + .HasPrecision(12, 6) + .HasColumnType(decimalColumnType); + }); + + modelBuilder.Entity(eb => + { + eb.Property(b => b.Id) + .HasAnnotation(FbAnnotationNames.ValueGenerationStrategy, FbValueGenerationStrategy.IdentityColumn); + }); + } + + EntitiesContext.AdditionalBuilders.Add(Builder); + } + public EntitiesContext CreateDbContext(string[] args) => CreateDbContext(args, null); public EntitiesContext CreateDbContext(string[] args, ILoggerFactory? factory) diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220629141114_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220715101358_InitialCreate.Designer.cs similarity index 82% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220629141114_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220715101358_InitialCreate.Designer.cs index 221d41cc..c7b24ddd 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220629141114_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220715101358_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.Firebird4.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141114_InitialCreate")] + [Migration("20220715101358_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -23,6 +23,21 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 31); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasAnnotation("Fb:ValueGenerationStrategy", FbValueGenerationStrategy.IdentityColumn); + + b.Property("Some") + .HasColumnType("BLOB SUB_TYPE TEXT"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220629141114_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220715101358_InitialCreate.cs similarity index 74% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220629141114_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220715101358_InitialCreate.cs index d8e41bf6..350f28e7 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220629141114_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/20220715101358_InitialCreate.cs @@ -1,4 +1,5 @@ using System; +using FirebirdSql.EntityFrameworkCore.Firebird.Metadata; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -9,6 +10,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Fb:ValueGenerationStrategy", FbValueGenerationStrategy.IdentityColumn), + Some = table.Column(type: "BLOB SUB_TYPE TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEnti~", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +52,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/EntitiesContextModelSnapshot.cs index 5a50042a..6710c074 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Firebird4/Migrations/EntitiesContextModelSnapshot.cs @@ -21,6 +21,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 31); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasAnnotation("Fb:ValueGenerationStrategy", FbValueGenerationStrategy.IdentityColumn); + + b.Property("Some") + .HasColumnType("BLOB SUB_TYPE TEXT"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220629141040_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220715101248_InitialCreate.Designer.cs similarity index 82% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220629141040_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220715101248_InitialCreate.Designer.cs index ef8db08d..2e3ac9bf 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220629141040_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220715101248_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.Oracle.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141040_InitialCreate")] + [Migration("20220715101248_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -24,6 +24,22 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) OracleModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("NUMBER(10)"); + + OraclePropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Some") + .HasColumnType("NVARCHAR2(2000)"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220629141040_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220715101248_InitialCreate.cs similarity index 77% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220629141040_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220715101248_InitialCreate.cs index f4b6158f..b75f914f 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220629141040_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/20220715101248_InitialCreate.cs @@ -9,6 +9,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "NUMBER(10)", nullable: false) + .Annotation("Oracle:Identity", "START WITH 1 INCREMENT BY 1"), + Some = table.Column(type: "NVARCHAR2(2000)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +51,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/EntitiesContextModelSnapshot.cs index aca0843f..a5d0731d 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Oracle/Migrations/EntitiesContextModelSnapshot.cs @@ -22,6 +22,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) OracleModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("NUMBER(10)"); + + OraclePropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Some") + .HasColumnType("NVARCHAR2(2000)"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220629141121_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220715101413_InitialCreate.Designer.cs similarity index 84% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220629141121_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220715101413_InitialCreate.Designer.cs index 3980c6d0..78dcc93f 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220629141121_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220715101413_InitialCreate.Designer.cs @@ -11,7 +11,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141121_InitialCreate")] + [Migration("20220715101413_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -21,6 +21,20 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Some") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220629141121_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220715101413_InitialCreate.cs similarity index 75% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220629141121_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220715101413_InitialCreate.cs index dc745a05..78382f67 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220629141121_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/20220715101413_InitialCreate.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -12,6 +13,21 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.AlterDatabase() .Annotation("MySql:CharSet", "utf8mb4"); + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Some = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -44,6 +60,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/EntitiesContextModelSnapshot.cs index 31cdc45d..657a1050 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMariaDb/Migrations/EntitiesContextModelSnapshot.cs @@ -19,6 +19,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Some") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220629141101_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220715101331_InitialCreate.Designer.cs similarity index 84% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220629141101_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220715101331_InitialCreate.Designer.cs index 78b65eb6..74dcefae 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220629141101_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220715101331_InitialCreate.Designer.cs @@ -11,7 +11,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.PomeloMySql.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141101_InitialCreate")] + [Migration("20220715101331_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -21,6 +21,20 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Some") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220629141101_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220715101331_InitialCreate.cs similarity index 75% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220629141101_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220715101331_InitialCreate.cs index ae34e0a8..d7f38ef4 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220629141101_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/20220715101331_InitialCreate.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -12,6 +13,21 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.AlterDatabase() .Annotation("MySql:CharSet", "utf8mb4"); + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Some = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -44,6 +60,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/EntitiesContextModelSnapshot.cs index 52979e76..a48c82c5 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.PomeloMySql/Migrations/EntitiesContextModelSnapshot.cs @@ -19,6 +19,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Some") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220629141054_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220715101313_InitialCreate.Designer.cs similarity index 82% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220629141054_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220715101313_InitialCreate.Designer.cs index 08da0b62..47079fe5 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220629141054_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220715101313_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.Postgres.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141054_InitialCreate")] + [Migration("20220715101313_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -24,6 +24,22 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Some") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220629141054_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220715101313_InitialCreate.cs similarity index 75% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220629141054_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220715101313_InitialCreate.cs index 813a130b..76effcd6 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220629141054_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/20220715101313_InitialCreate.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -9,6 +10,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Some = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +52,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/EntitiesContextModelSnapshot.cs index 49231241..fc1b8e00 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Postgres/Migrations/EntitiesContextModelSnapshot.cs @@ -22,6 +22,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Some") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220629141033_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220715101235_InitialCreate.Designer.cs similarity index 82% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220629141033_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220715101235_InitialCreate.Designer.cs index 7f0efdd7..9adb5287 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220629141033_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220715101235_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.SqlServer.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141033_InitialCreate")] + [Migration("20220715101235_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -24,6 +24,22 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Some") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220629141033_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220715101235_InitialCreate.cs similarity index 77% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220629141033_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220715101235_InitialCreate.cs index 7a39817a..62a6926c 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220629141033_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/20220715101235_InitialCreate.cs @@ -9,6 +9,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Some = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +51,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/EntitiesContextModelSnapshot.cs index 8cc252ce..ccd9fe21 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.SqlServer/Migrations/EntitiesContextModelSnapshot.cs @@ -22,6 +22,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Some") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220629141047_InitialCreate.Designer.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220715101259_InitialCreate.Designer.cs similarity index 83% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220629141047_InitialCreate.Designer.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220715101259_InitialCreate.Designer.cs index bcfc3f69..5966defb 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220629141047_InitialCreate.Designer.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220715101259_InitialCreate.Designer.cs @@ -11,7 +11,7 @@ namespace EFCore.Extensions.SaveOptimizer.Model.Sqlite.Migrations { [DbContext(typeof(EntitiesContext))] - [Migration("20220629141047_InitialCreate")] + [Migration("20220715101259_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -19,6 +19,20 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "6.0.0"); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Some") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220629141047_InitialCreate.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220715101259_InitialCreate.cs similarity index 77% rename from EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220629141047_InitialCreate.cs rename to EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220715101259_InitialCreate.cs index 62dd9b9d..dca46071 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220629141047_InitialCreate.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/20220715101259_InitialCreate.cs @@ -9,6 +9,19 @@ public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "AutoIncrementPrimaryKeyEntities", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Some = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AutoIncrementPrimaryKeyEntities", x => x.Id); + }); + migrationBuilder.CreateTable( name: "NonRelatedEntities", columns: table => new @@ -38,6 +51,9 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AutoIncrementPrimaryKeyEntities"); + migrationBuilder.DropTable( name: "NonRelatedEntities"); } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/EntitiesContextModelSnapshot.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/EntitiesContextModelSnapshot.cs index 2f18cfd8..f317dab9 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/EntitiesContextModelSnapshot.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/Migrations/EntitiesContextModelSnapshot.cs @@ -17,6 +17,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "6.0.0"); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.AutoIncrementPrimaryKeyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Some") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AutoIncrementPrimaryKeyEntities"); + }); + modelBuilder.Entity("EFCore.Extensions.SaveOptimizer.Model.NonRelatedEntity", b => { b.Property("NonRelatedEntityId") diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/SqliteDesignTimeFactory.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/SqliteDesignTimeFactory.cs index 3f1e4e05..69878321 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/SqliteDesignTimeFactory.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model.Sqlite/SqliteDesignTimeFactory.cs @@ -29,7 +29,7 @@ public EntitiesContext CreateDbContext(string[] args, ILoggerFactory? factory) private static string GetConnectionString() { - DirectoryInfo directory = new("db"); + DirectoryInfo directory = new(Path.Combine(Path.GetTempPath(), "db")); if (!directory.Exists) { diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model/AutoIncrementPrimaryKeyEntity.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model/AutoIncrementPrimaryKeyEntity.cs new file mode 100644 index 00000000..708db9a0 --- /dev/null +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model/AutoIncrementPrimaryKeyEntity.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace EFCore.Extensions.SaveOptimizer.Model; + +public class AutoIncrementPrimaryKeyEntity +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Key] + public int Id { get; set; } + + public string? Some { get; set; } +} diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model/EntitiesContext.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model/EntitiesContext.cs index e0c5a3eb..5bcf36e8 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model/EntitiesContext.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Model/EntitiesContext.cs @@ -6,8 +6,12 @@ namespace EFCore.Extensions.SaveOptimizer.Model; public class EntitiesContext : DbContext { + public static List> AdditionalBuilders = new(); + public DbSet NonRelatedEntities { get; set; } + public DbSet AutoIncrementPrimaryKeyEntities { get; set; } + public EntitiesContext(DbContextOptions options) : base(options) { @@ -15,37 +19,13 @@ public EntitiesContext(DbContextOptions options) protected override void OnModelCreating(ModelBuilder modelBuilder) { - if (Database.ProviderName != null && Database.ProviderName.Contains("Firebird")) - { - const string columnType = "DECIMAL(12,6)"; - - modelBuilder.Entity( - eb => - { - eb.Property(b => b.SomeNullableDecimalProperty) - .HasPrecision(12, 6) - .HasColumnType(columnType); - - eb.Property(b => b.SomeNonNullableDecimalProperty) - .HasPrecision(12, 6) - .HasColumnType(columnType); - }); - } - else - { - modelBuilder.Entity( - eb => - { - eb.Property(b => b.SomeNullableDecimalProperty) - .HasPrecision(12, 6); - - eb.Property(b => b.SomeNonNullableDecimalProperty) - .HasPrecision(12, 6); - }); - } - modelBuilder.Entity() .HasIndex(x => new { x.ConcurrencyToken, x.NonRelatedEntityId }) .IsUnique(false); + + foreach (Action builder in AdditionalBuilders) + { + builder(modelBuilder); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Benchmark/Specific/DbContextWrapper.cs index bcef80c4..96224d30 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "TRUNCATE TABLE \"NonRelatedEntities\";"; + foreach (var entity in EntitiesList) + { + var query = $"TRUNCATE TABLE \"{entity}\";"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Tests/WrapperResolver.cs index 544f0258..0a10494b 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Oracle.Tests/WrapperResolver.cs @@ -15,9 +15,11 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "TRUNCATE TABLE \"NonRelatedEntities\";"; + const string truncateQuery = "TRUNCATE TABLE \"{0}\";"; - wrapper.Context.Database.ExecuteSqlRaw(query); + const string resetSequenceQuery = "alter table \"{0}\" modify \"{1}\" generated always as identity restart start with 1;"; + + wrapper.CleanDb(truncateQuery, resetSequenceQuery); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Benchmark/Specific/DbContextWrapper.cs index 7d69e28b..a04a4e19 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "truncate `NonRelatedEntities`;"; + foreach (var entity in EntitiesList) + { + var query = $"truncate `{entity}`;"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Tests/WrapperResolver.cs index 2d464d54..783f3a17 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMariaDb.Tests/WrapperResolver.cs @@ -15,9 +15,9 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "truncate `NonRelatedEntities`;"; + const string query = "truncate `{0}`;"; - wrapper.Context.Database.ExecuteSqlRaw(query); + wrapper.CleanDb(query); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Benchmark/Specific/DbContextWrapper.cs index 34d9cc5d..7a815908 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "truncate `NonRelatedEntities`;"; + foreach (var entity in EntitiesList) + { + var query = $"truncate `{entity}`;"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Tests/WrapperResolver.cs index 39645cc6..736d32ae 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.PomeloMySql.Tests/WrapperResolver.cs @@ -15,9 +15,9 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "truncate `NonRelatedEntities`;"; + const string query = "truncate `{0}`;"; - wrapper.Context.Database.ExecuteSqlRaw(query); + wrapper.CleanDb(query); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Benchmark/Specific/DbContextWrapper.cs index 4ed0cbbd..a87ff0f0 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "truncate \"NonRelatedEntities\";"; + foreach (var entity in EntitiesList) + { + var query = $"truncate \"{entity}\";"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Tests/WrapperResolver.cs index 087d43ed..0a1a5caf 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Postgres.Tests/WrapperResolver.cs @@ -15,9 +15,9 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "truncate \"NonRelatedEntities\";"; + const string query = "truncate \"{0}\" RESTART IDENTITY;"; - wrapper.Context.Database.ExecuteSqlRaw(query); + wrapper.CleanDb(query); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Benchmark/DbContextWrapperBase.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Benchmark/DbContextWrapperBase.cs index af3329fb..e659cf6e 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Benchmark/DbContextWrapperBase.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Benchmark/DbContextWrapperBase.cs @@ -16,6 +16,8 @@ public abstract class DbContextWrapperBase : IDbContextWrapper private readonly IDbContextFactory _factory; private int _failures; + protected string[] EntitiesList { get; } = { "NonRelatedEntities", "AutoIncrementPrimaryKeyEntities" }; + protected DbContextWrapperBase(IDbContextFactory factory) { _failures = 0; diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/BaseMiscTests.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/BaseMiscTests.cs index 99794935..4475087d 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/BaseMiscTests.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/BaseMiscTests.cs @@ -6,7 +6,7 @@ namespace EFCore.Extensions.SaveOptimizer.Shared.Tests; -public abstract class BaseMiscTests : BaseTests +public abstract partial class BaseMiscTests : BaseTests { public static IEnumerable> BaseWriteTheoryData => TheoryData.BaseWriteTheoryData; diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/BasePrimaryKeyTests.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/BasePrimaryKeyTests.cs new file mode 100644 index 00000000..90eb5c72 --- /dev/null +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/BasePrimaryKeyTests.cs @@ -0,0 +1,109 @@ +using EFCore.Extensions.SaveOptimizer.Model; +using EFCore.Extensions.SaveOptimizer.Shared.Tests.Extensions; + +namespace EFCore.Extensions.SaveOptimizer.Shared.Tests; + +public abstract partial class BaseMiscTests +{ + [Theory] + [MemberData(nameof(BaseWriteTheoryData))] + public async Task GivenSaveChangesAsync_WhenInsertAutoIncrementPrimaryKey_ShouldStoreData(SaveVariant variant) + { + // Arrange + using DbContextWrapper db = ContextWrapperResolver(); + + AutoIncrementPrimaryKeyEntity[] data = { new() { Some = "x1" }, new() { Some = "x2" }, new() { Some = "x3" } }; + + await db.Context.AddRangeAsync(data as IEnumerable); + + // Act + await db.SaveAsync(variant, null); + + AutoIncrementPrimaryKeyEntity[] result = + await db.Context.AutoIncrementPrimaryKeyEntities.OrderBy(x => x.Some).ToArrayWithRetryAsync(); + + var keys = result.Select(x => x.Id).ToArray(); + + var properties = result.Select(x => x.Some).ToArray(); + + // Assert + result.Should().HaveCount(3); + + keys.Should().ContainInOrder(1, 2, 3); + + properties.Should().ContainInOrder("x1", "x2", "x3"); + } + + [Theory] + [MemberData(nameof(BaseWriteTheoryData))] + public async Task GivenSaveChangesAsync_WhenUpdateAutoIncrementPrimaryKey_ShouldStoreData(SaveVariant variant) + { + // Arrange + using DbContextWrapper db = ContextWrapperResolver(); + + AutoIncrementPrimaryKeyEntity[] data = { new() { Some = "x1" }, new() { Some = "x2" }, new() { Some = "x3" } }; + + await db.Context.AddRangeAsync(data as IEnumerable); + + await db.SaveAsync(variant, null); + + data = await db.Context.AutoIncrementPrimaryKeyEntities.OrderBy(x => x.Some).ToArrayWithRetryAsync(); + + foreach (AutoIncrementPrimaryKeyEntity item in data) + { + item.Some = $"a_{item.Some}_{item.Id}"; + } + + // Act + await db.SaveAsync(variant, null); + + AutoIncrementPrimaryKeyEntity[] result = + await db.Context.AutoIncrementPrimaryKeyEntities.OrderBy(x => x.Some).ToArrayWithRetryAsync(); + + var keys = result.Select(x => x.Id).ToArray(); + + var properties = result.Select(x => x.Some).ToArray(); + + // Assert + result.Should().HaveCount(3); + + keys.Should().ContainInOrder(1, 2, 3); + + properties.Should().ContainInOrder("a_x1_1", "a_x2_2", "a_x3_3"); + } + + [Theory] + [MemberData(nameof(BaseWriteTheoryData))] + public async Task GivenSaveChangesAsync_WhenDeleteAutoIncrementPrimaryKey_ShouldStoreData(SaveVariant variant) + { + // Arrange + using DbContextWrapper db = ContextWrapperResolver(); + + AutoIncrementPrimaryKeyEntity[] data = { new() { Some = "x1" }, new() { Some = "x2" }, new() { Some = "x3" } }; + + await db.Context.AddRangeAsync(data as IEnumerable); + + await db.SaveAsync(variant, null); + + data = await db.Context.AutoIncrementPrimaryKeyEntities.OrderBy(x => x.Some).ToArrayWithRetryAsync(); + + db.Context.RemoveRange(data.Take(2)); + + // Act + await db.SaveAsync(variant, null); + + AutoIncrementPrimaryKeyEntity[] result = + await db.Context.AutoIncrementPrimaryKeyEntities.OrderBy(x => x.Some).ToArrayWithRetryAsync(); + + var keys = result.Select(x => x.Id).ToArray(); + + var properties = result.Select(x => x.Some).ToArray(); + + // Assert + result.Should().HaveCount(1); + + keys.Should().ContainInOrder(3); + + properties.Should().ContainInOrder("x3"); + } +} diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/DbContextWrapper.cs index 05cc3609..d2ef8092 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Shared.Tests/DbContextWrapper.cs @@ -21,6 +21,10 @@ public sealed class DbContextWrapper : IDisposable public EntitiesContext Context { get; private set; } + public string[] EntitiesList { get; } = { "NonRelatedEntities", "AutoIncrementPrimaryKeyEntities" }; + + public Dictionary SequencesList { get; } = new() { { "AutoIncrementPrimaryKeyEntities", "Id" } }; + public DbContextWrapper(ITestTimeDbContextFactory factory, ITestOutputHelper testOutputHelper) { _factory = factory; @@ -56,6 +60,28 @@ public async Task SaveAsync(SaveVariant variant, int? batchSize, int retries = R } } + public void CleanDb(string truncateFormat, string? resetSequenceFormat = null) + { + foreach (var entity in EntitiesList) + { + var query = string.Format(truncateFormat, entity); + + Run(RunTry, () => Context.Database.ExecuteSqlRaw(query)); + } + + if (resetSequenceFormat == null) + { + return; + } + + foreach (var (table, column) in SequencesList) + { + var query = string.Format(resetSequenceFormat, table, column); + + Run(RunTry, () => Context.Database.ExecuteSqlRaw(query)); + } + } + private async Task TrySaveAsync(SaveVariant variant, int? batchSize) { QueryExecutionConfiguration? configuration = diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Benchmark/Specific/DbContextWrapper.cs index f2ee31ec..7a257e28 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "truncate table \"NonRelatedEntities\";"; + foreach (var entity in EntitiesList) + { + var query = $"truncate table \"{entity}\";"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Tests/WrapperResolver.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Tests/WrapperResolver.cs index 9aed7490..1e9b5e49 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Tests/WrapperResolver.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.SqlServer.Tests/WrapperResolver.cs @@ -15,9 +15,9 @@ public static DbContextWrapper ContextWrapperResolver(ITestOutputHelper testOutp wrapper.Context.Database.Migrate(); - const string query = "truncate table \"NonRelatedEntities\";"; + const string query = "truncate table \"{0}\";"; - wrapper.Context.Database.ExecuteSqlRaw(query); + wrapper.CleanDb(query); return wrapper; } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Sqlite.Benchmark/Specific/DbContextWrapper.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Sqlite.Benchmark/Specific/DbContextWrapper.cs index 16086bd7..5b9bed29 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Sqlite.Benchmark/Specific/DbContextWrapper.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer.Sqlite.Benchmark/Specific/DbContextWrapper.cs @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory factory) : base(facto protected override async Task TruncateBaseAsync() { - const string query = "DELETE FROM NonRelatedEntities"; + foreach (var entity in EntitiesList) + { + var query = $"DELETE FROM {entity}"; - await Context.Database.ExecuteSqlRawAsync(query); + await Context.Database.ExecuteSqlRawAsync(query); + } } } diff --git a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer/Services/QueryExecutorService.cs b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer/Services/QueryExecutorService.cs index a447750e..431f39c0 100644 --- a/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer/Services/QueryExecutorService.cs +++ b/EFCore.Extensions.SaveOptimizer/EFCore.Extensions.SaveOptimizer/Services/QueryExecutorService.cs @@ -1,4 +1,5 @@ using System.Data.Common; +using System.Reflection; using EFCore.Extensions.SaveOptimizer.Internal.Configuration; using EFCore.Extensions.SaveOptimizer.Internal.Models; using EFCore.Extensions.SaveOptimizer.Internal.Services; @@ -103,7 +104,28 @@ private static DbCommand GetCommand(IDbContextTransaction transaction, logger.LogDebug("Executing command: {Sql}", sql.Sql); - return command; + return SetDbSpecificProperties(command); + } + + private static DbCommand SetDbSpecificProperties(DbCommand command) + { + Type type = command.GetType(); + + switch (type.Name) + { + case "OracleCommand": + PropertyInfo? property = type.GetProperty("BindByName"); + + if (property == null) + { + return command; + } + + property.SetValue(command, true); + + return command; + default: return command; + } } private static void AddParameter(DbCommand command, SqlParamModel param) diff --git a/README.md b/README.md index e0b4c6ff..be81ffcc 100644 --- a/README.md +++ b/README.md @@ -78,11 +78,8 @@ Please note it is not working exactly as SaveChanges, so you should verify it wo - Insert - Update - Delete -- Configuration - - Batch size - - Parameters optimization behavior -- Other - - Concurrency token +- [Configuration](#configuration) +- Concurrency token support ## What to do next @@ -102,12 +99,19 @@ Please note it is not working exactly as SaveChanges, so you should verify it wo - Tests - Value generated on add - Auto increment primary key + - Explicitly set primary key + - No primary key + - Various type primary keys + - Sequences - Value converter - - Mixed statements - Hierarchical operations - Data types precision (date, decimal etc.) - Update concurrency tokens - Different configuration types + - Owned entities + - Keyless entities + - Value comparers + - Table splitting ## Limitations @@ -124,9 +128,14 @@ Also DatabaseValues for entry will not be retrieved from db when ConcurrencyToke It looks like serializable transaction produces many errors during execution, especially during insert (e.g. ORA-08177 & ORA-06512). This is something to investigate, maybe this is dockerized Oracle Express issue. I don't recommend using this library with Oracle in production environment without strong testing. Sometimes decrease batch size for insert could help. -### Oracle concurrency token behavior +### Firebird provider -Oracle returns -1 as affected rows when using INSERT ALL, likely due to using BEGIN / END statements in command. In that case affected rows would be assumed as equal to expected for the statement. +This is not a SaveOptimizer issue, however I experienced some problems with Firebird provider. It looks model builder sometimes build different model than other providers. + +| Issue | Workaround | +|---|---| +| Precision lost for decimal column | Use `HasColumnType("DECIMAL(PRECISION,SCALE)")` | +| Auto increment column not created | Use `HasAnnotation(FbAnnotationNames.ValueGenerationStrategy, FbValueGenerationStrategy.IdentityColumn)` | ## Q&A