Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes around scaffolding collations
Browse files Browse the repository at this point in the history
* Database-level collation is scaffolded to the Fluent API call,
  instead of to a raw annotation.
* Don't scaffold database collation when it is identical to the
  server collation (i.e. is the default).

Fixes #25418
Fixes #25419
roji committed Aug 4, 2021
1 parent 6c3fb03 commit 9f4015e
Showing 4 changed files with 68 additions and 6 deletions.
5 changes: 5 additions & 0 deletions src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -111,6 +111,11 @@ public virtual IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
RelationalAnnotationNames.DefaultSchema, nameof(RelationalModelBuilderExtensions.HasDefaultSchema),
methodCallCodeFragments);

GenerateSimpleFluentApiCall(
annotations,
RelationalAnnotationNames.Collation, nameof(RelationalModelBuilderExtensions.UseCollation),
methodCallCodeFragments);

methodCallCodeFragments.AddRange(GenerateFluentApiCallsHelper(model, annotations, GenerateFluentApi));
return methodCallCodeFragments;
}
Original file line number Diff line number Diff line change
@@ -131,7 +131,13 @@ public override DatabaseModel Create(DbConnection connection, DatabaseModelFacto

databaseModel.DatabaseName = connection.Database;
databaseModel.DefaultSchema = GetDefaultSchema(connection);
databaseModel.Collation = GetCollation(connection);

var serverCollation = GetServerCollation(connection);
var databaseCollation = GetDatabaseCollation(connection);
if (databaseCollation is not null && databaseCollation != serverCollation)
{
databaseModel.Collation = GetDatabaseCollation(connection);
}

var typeAliases = GetTypeAliases(connection);

@@ -145,7 +151,7 @@ public override DatabaseModel Create(DbConnection connection, DatabaseModelFacto
GetSequences(connection, databaseModel, schemaFilter, typeAliases);
}

GetTables(connection, databaseModel, tableFilter, typeAliases);
GetTables(connection, databaseModel, tableFilter, typeAliases, databaseCollation);

foreach (var schema in schemaList
.Except(
@@ -200,7 +206,17 @@ FROM sys.databases
return result != null ? Convert.ToByte(result) : (byte)0;
}

static string? GetCollation(DbConnection connection)
static string? GetServerCollation(DbConnection connection)
{
using var command = connection.CreateCommand();
command.CommandText = @"
SELECT SERVERPROPERTY('Collation');";
return command.ExecuteScalar() is string collation
? collation
: null;
}

static string? GetDatabaseCollation(DbConnection connection)
{
using var command = connection.CreateCommand();
command.CommandText = $@"
@@ -471,7 +487,8 @@ private void GetTables(
DbConnection connection,
DatabaseModel databaseModel,
Func<string, string, string>? tableFilter,
IReadOnlyDictionary<string, (string, string)> typeAliases)
IReadOnlyDictionary<string, (string, string)> typeAliases,
string? databaseCollation)
{
using var command = connection.CreateCommand();
var tables = new List<DatabaseTable>();
@@ -632,7 +649,7 @@ FROM [sys].[views] AS [v]
}

// This is done separately due to MARS property may be turned off
GetColumns(connection, tables, filter, viewFilter, typeAliases, databaseModel.Collation);
GetColumns(connection, tables, filter, viewFilter, typeAliases, databaseCollation);
GetIndexes(connection, tables, filter);
GetForeignKeys(connection, tables, filter);

26 changes: 26 additions & 0 deletions test/EFCore.Relational.Tests/Design/AnnotationCodeGeneratorTest.cs
Original file line number Diff line number Diff line change
@@ -24,6 +24,32 @@ public void IsTableExcludedFromMigrations_false_is_handled_by_convention()
Assert.DoesNotContain(RelationalAnnotationNames.IsTableExcludedFromMigrations, annotations.Keys);
}

[ConditionalFact]
public void GenerateFluentApi_IModel_works_with_collation()
{
var modelBuilder = CreateModelBuilder();
modelBuilder.UseCollation("foo");
var annotations = modelBuilder.Model.GetAnnotations().ToDictionary(a => a.Name, a => a);
var result = CreateGenerator().GenerateFluentApiCalls((IModel)modelBuilder.Model, annotations).Single();

Assert.Equal("UseCollation", result.Method);
Assert.Equal("foo", Assert.Single(result.Arguments));
}

[ConditionalFact]
public void GenerateFluentApi_IProperty_works_with_collation()
{
var modelBuilder = CreateModelBuilder();
modelBuilder.Entity("Blog", x => x.Property<string>("Name").UseCollation("foo"));
var property = modelBuilder.Model.FindEntityType("Blog").FindProperty("Name");

var annotations = property.GetAnnotations().ToDictionary(a => a.Name, a => a);
var result = CreateGenerator().GenerateFluentApiCalls((IProperty)property, annotations).Single();

Assert.Equal("UseCollation", result.Method);
Assert.Equal("foo", Assert.Single(result.Arguments));
}

private ModelBuilder CreateModelBuilder()
=> RelationalTestHelpers.Instance.CreateConventionBuilder();

Original file line number Diff line number Diff line change
@@ -303,6 +303,17 @@ public void Create_tables()
DROP TABLE [dbo].[Denali];");
}

[ConditionalFact]
public void Default_database_collation_is_not_scaffolded()
{
Test(
@"",
Enumerable.Empty<string>(),
Enumerable.Empty<string>(),
dbModel => Assert.Null(dbModel.Collation),
@"");
}

#endregion

#region FilteringSchemaTable
@@ -2368,7 +2379,10 @@ private void Test(
Action<DatabaseModel> asserter,
string cleanupSql)
{
Fixture.TestStore.ExecuteNonQuery(createSql);
if (!string.IsNullOrEmpty(createSql))
{
Fixture.TestStore.ExecuteNonQuery(createSql);
}

try
{

0 comments on commit 9f4015e

Please sign in to comment.