From fd69bdcbd59202df98e99cb72f9c34593c06007c Mon Sep 17 00:00:00 2001 From: Pawel Gerr Date: Wed, 27 Apr 2022 21:02:06 +0200 Subject: [PATCH] Added test for collection handling in temp table creator. --- .../TempTables/SqlServerTempTableCreator.cs | 19 ++++++------ .../CreateTempTableAsync.cs | 29 +++++++++++++++++-- .../TestDatabaseContext/TestDbContext.cs | 1 + .../TestEntityWithCollation.cs | 20 +++++++++++++ 4 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 tests/Thinktecture.EntityFrameworkCore.TestHelpers/TestDatabaseContext/TestEntityWithCollation.cs diff --git a/src/Thinktecture.EntityFrameworkCore.SqlServer/EntityFrameworkCore/TempTables/SqlServerTempTableCreator.cs b/src/Thinktecture.EntityFrameworkCore.SqlServer/EntityFrameworkCore/TempTables/SqlServerTempTableCreator.cs index 7ba7a061..568e655a 100644 --- a/src/Thinktecture.EntityFrameworkCore.SqlServer/EntityFrameworkCore/TempTables/SqlServerTempTableCreator.cs +++ b/src/Thinktecture.EntityFrameworkCore.SqlServer/EntityFrameworkCore/TempTables/SqlServerTempTableCreator.cs @@ -236,17 +236,16 @@ private string GetColumnsDefinitions(SqlServerTempTableCreatorCacheKey options) if (_stringColumnTypes.Any(t => columnType.StartsWith(t, StringComparison.OrdinalIgnoreCase))) { // Collation information is not available from the runtime model, so we need to fetch it from the design time model - if (designTimeEntityType == null) - { - var designTimeModel = _ctx.GetService().Model; - designTimeEntityType = designTimeModel.FindEntityType(property.Property.DeclaringEntityType.Name) ?? - throw new InvalidOperationException($"Entity type {property.Property.DeclaringEntityType.Name} is missing from design time model."); - } - var designTimeEntityProperty = designTimeEntityType.GetProperty(property.Property.Name); - var collation = designTimeEntityProperty.GetCollation(storeObject.Value); - if (string.IsNullOrWhiteSpace(collation) && options.UseDefaultDatabaseCollation) + designTimeEntityType ??= _ctx.GetService().Model + .GetEntityType(property.Property.DeclaringEntityType.Name); + + var collation = designTimeEntityType.GetProperty(property.Property.Name) + .GetCollation(storeObject.Value); + + if (options.UseDefaultDatabaseCollation && String.IsNullOrWhiteSpace(collation)) collation = "database_default"; - if (!string.IsNullOrWhiteSpace(collation)) + + if (!String.IsNullOrWhiteSpace(collation)) sb.Append(" COLLATE ").Append(collation); } diff --git a/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/EntityFrameworkCore/TempTables/SqlServerTempTableCreatorTests/CreateTempTableAsync.cs b/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/EntityFrameworkCore/TempTables/SqlServerTempTableCreatorTests/CreateTempTableAsync.cs index 4e8c922b..4a4df9e2 100644 --- a/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/EntityFrameworkCore/TempTables/SqlServerTempTableCreatorTests/CreateTempTableAsync.cs +++ b/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/EntityFrameworkCore/TempTables/SqlServerTempTableCreatorTests/CreateTempTableAsync.cs @@ -2,6 +2,7 @@ using System.Data.Common; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; using Thinktecture.EntityFrameworkCore.BulkOperations; using Thinktecture.TestDatabaseContext; @@ -530,7 +531,7 @@ public async Task Should_create_temp_table_with_decimal() [Fact] public async Task Should_create_temp_table_with_decimal_with_explicit_precision() { - ConfigureModel = builder => builder.ConfigureTempTable(typeBuilder => typeBuilder.Property(t => t.Column1).HasPrecision(20,5)); + ConfigureModel = builder => builder.ConfigureTempTable(typeBuilder => typeBuilder.Property(t => t.Column1).HasPrecision(20, 5)); await using var tempTable = await SUT.CreateTempTableAsync(ActDbContext.GetTempTableEntityType>(), _optionsWithNonUniqueName); @@ -700,6 +701,26 @@ public async Task Should_create_temp_table_for_entity_with_many_owned_types() ValidateColumn(columns[3], nameof(OwnedEntity.StringColumn), "nvarchar", true); } + [Fact] + public async Task Should_honor_collation() + { + var entityType = ActDbContext.GetTempTableEntityType(); + await using var tempTable = await SUT.CreateTempTableAsync(entityType, _optionsWithNonUniqueName); + + var columns = AssertDbContext.GetTempTableColumns().ToList(); + columns.Should().HaveCount(3); + + var connection = AssertDbContext.Database.GetDbConnection(); + await using var command = connection.CreateCommand(); + command.Transaction = AssertDbContext.Database.CurrentTransaction?.GetDbTransaction(); + command.CommandText = "SELECT CONVERT (varchar(256), SERVERPROPERTY('collation'))"; + var databaseCollation = (string?)await command.ExecuteScalarAsync() ?? throw new Exception("Couldn't fetch database collection."); + + ValidateColumn(columns[0], nameof(TestEntityWithCollation.Id), "uniqueidentifier", false); + ValidateColumn(columns[1], nameof(TestEntityWithCollation.ColumnWithCollation), "nvarchar", false, collation: "Japanese_CI_AS"); + ValidateColumn(columns[2], nameof(TestEntityWithCollation.ColumnWithoutCollation), "nvarchar", false, collation: databaseCollation); + } + private static DbConnection CreateConnection() { return new SqlConnection(TestContext.Instance.ConnectionString); @@ -718,7 +739,8 @@ private static void ValidateColumn( byte? numericPrecision = null, int? numericScale = null, int? charMaxLength = null, - string? defaultValue = null) + string? defaultValue = null, + string? collation = null) { ArgumentNullException.ThrowIfNull(column); @@ -727,6 +749,9 @@ private static void ValidateColumn( column.IS_NULLABLE.Should().Be(isNullable ? "YES" : "NO"); column.COLUMN_DEFAULT.Should().Be(defaultValue); + if (collation is not null) + column.COLLATION_NAME.Should().Be(collation); + if (numericPrecision.HasValue) column.NUMERIC_PRECISION.Should().Be(numericPrecision.Value); diff --git a/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/TestDatabaseContext/TestDbContext.cs b/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/TestDatabaseContext/TestDbContext.cs index f74adfdf..1cb66ee2 100644 --- a/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/TestDatabaseContext/TestDbContext.cs +++ b/tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/TestDatabaseContext/TestDbContext.cs @@ -99,6 +99,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) TestEntity_Owns_SeparateMany_SeparateOne.Configure(modelBuilder); TestEntity_Owns_SeparateMany_Inline.Configure(modelBuilder); TestEntity_Owns_SeparateMany_SeparateMany.Configure(modelBuilder); + TestEntityWithCollation.Configure(modelBuilder); ConfigureModel?.Invoke(modelBuilder); diff --git a/tests/Thinktecture.EntityFrameworkCore.TestHelpers/TestDatabaseContext/TestEntityWithCollation.cs b/tests/Thinktecture.EntityFrameworkCore.TestHelpers/TestDatabaseContext/TestEntityWithCollation.cs new file mode 100644 index 00000000..ec89e6d7 --- /dev/null +++ b/tests/Thinktecture.EntityFrameworkCore.TestHelpers/TestDatabaseContext/TestEntityWithCollation.cs @@ -0,0 +1,20 @@ +namespace Thinktecture.TestDatabaseContext; + +public class TestEntityWithCollation +{ + public Guid Id { get; set; } + public string ColumnWithoutCollation { get; set; } + public string ColumnWithCollation { get; set; } + + public TestEntityWithCollation(Guid id, string columnWithoutCollation, string columnWithCollation) + { + Id = id; + ColumnWithoutCollation = columnWithoutCollation; + ColumnWithCollation = columnWithCollation; + } + + public static void Configure(ModelBuilder modelBuilder) + { + modelBuilder.Entity(builder => builder.Property(e => e.ColumnWithCollation).UseCollation("Japanese_CI_AS")); + } +}