Skip to content

Commit

Permalink
Merge pull request #7 from mkolumb/feature/auto-increment
Browse files Browse the repository at this point in the history
Add auto increment primary key support
  • Loading branch information
mkolumb authored Jul 15, 2022
2 parents 46e7cd1 + e9e90f4 commit 3656879
Show file tree
Hide file tree
Showing 71 changed files with 994 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory<EntitiesContext> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory<EntitiesContext> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory<EntitiesContext> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ public DbContextWrapper(IDbContextFactory<EntitiesContext> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="FluentAssertions.Json" Version="6.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="[6.0.0, 7.0.0)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="[6.0.0, 7.0.0)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Moq" Version="4.18.1" />
<PackageReference Include="SqlKata" Version="2.3.7" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore;

namespace EFCore.Extensions.SaveOptimizer.Internal.Tests.Extensions;

public static class DbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder<T> UseDynamicSqlLite<T>(this DbContextOptionsBuilder<T> 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}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class EntityTypeExtensionTests
public EntityTypeExtensionTests()
{
DbContextOptionsBuilder options =
new DbContextOptionsBuilder<TestDataContext>().UseInMemoryDatabase("in_memory_db");
new DbContextOptionsBuilder<TestDataContext>().UseDynamicSqlLite();
_sut = new TestDataContext(options.Options);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -21,8 +22,7 @@ public class QueryTranslatorServiceTests
public QueryTranslatorServiceTests()
{
DbContextOptionsBuilder<TestDataContext> options =
new DbContextOptionsBuilder<TestDataContext>().UseInMemoryDatabase("in_memory_db")
.UseSnakeCaseNamingConvention();
new DbContextOptionsBuilder<TestDataContext>().UseDynamicSqlLite();
_context = new TestDataContext(options.Options);

_wrapper = new DataContextModelWrapper(() => _context);
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand All @@ -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<NonInsertablePrimaryKeyEntity> 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<InsertablePrimaryKeyEntity> 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");
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
Original file line number Diff line number Diff line change
@@ -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; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public class TestDataContext : DbContext

public DbSet<AttributeEntityLog> AttributeEntityLogs { get; set; }

public DbSet<InsertablePrimaryKeyEntity> InsertablePrimaryKeyEntities { get; set; }
public DbSet<NonInsertablePrimaryKeyEntity> NonInsertablePrimaryKeyEntities { get; set; }

public TestDataContext(DbContextOptions options) : base(options)
{
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,8 +14,7 @@ public class DataContextModelWrapperTests
public DataContextModelWrapperTests()
{
DbContextOptionsBuilder<TestDataContext> options =
new DbContextOptionsBuilder<TestDataContext>().UseInMemoryDatabase("in_memory_db")
.UseSnakeCaseNamingConvention();
new DbContextOptionsBuilder<TestDataContext>().UseDynamicSqlLite();
TestDataContext context = new(options.Options);
_sut = new DataContextModelWrapper(() => context);
}
Expand Down Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -157,11 +152,6 @@ public async Task<int> 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;
}

Expand Down
Loading

0 comments on commit 3656879

Please sign in to comment.