Skip to content

Commit

Permalink
Preserve unicodeness and fixed-lengthness in compiled model.
Browse files Browse the repository at this point in the history
Fixes #32617
  • Loading branch information
AndriySvyryd committed Dec 23, 2023
1 parent 2d7ed26 commit dbd6252
Show file tree
Hide file tree
Showing 25 changed files with 542 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ public virtual void Initialize(IDbContextOptions options)
Region = cosmosOptions.Region;
PreferredRegions = cosmosOptions.PreferredRegions;
LimitToEndpoint = cosmosOptions.LimitToEndpoint;
EnableContentResponseOnWrite = cosmosOptions.EnableContentResponseOnWrite;
ConnectionMode = cosmosOptions.ConnectionMode;
WebProxy = cosmosOptions.WebProxy;
RequestTimeout = cosmosOptions.RequestTimeout;
Expand Down Expand Up @@ -208,7 +207,6 @@ public virtual void Validate(IDbContextOptions options)
|| GatewayModeMaxConnectionLimit != cosmosOptions.GatewayModeMaxConnectionLimit
|| MaxTcpConnectionsPerEndpoint != cosmosOptions.MaxTcpConnectionsPerEndpoint
|| MaxRequestsPerTcpConnection != cosmosOptions.MaxRequestsPerTcpConnection
|| EnableContentResponseOnWrite != cosmosOptions.EnableContentResponseOnWrite
|| HttpClientFactory != cosmosOptions.HttpClientFactory
))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2154,7 +2154,15 @@ public override bool Create(
&& relationalTypeMapping.Scale != defaultInstance.Scale;
var dbTypeDifferent = relationalTypeMapping.DbType != null
&& relationalTypeMapping.DbType != defaultInstance.DbType;
if (storeTypeDifferent || sizeDifferent || precisionDifferent || scaleDifferent || dbTypeDifferent)
var isUnicodeDifferent = relationalTypeMapping.IsUnicode != defaultInstance.IsUnicode;
var isFixedLengthDifferent = relationalTypeMapping.IsFixedLength != defaultInstance.IsFixedLength;
if (storeTypeDifferent
|| sizeDifferent
|| precisionDifferent
|| scaleDifferent
|| dbTypeDifferent
|| isUnicodeDifferent
|| isFixedLengthDifferent)
{
AddNamespace(typeof(RelationalTypeMappingInfo), parameters.Namespaces);
mainBuilder.AppendLine(",")
Expand All @@ -2174,6 +2182,18 @@ public override bool Create(
"size", code.Literal(relationalTypeMapping.Size), mainBuilder, ref firstParameter);
}

if (isUnicodeDifferent)
{
GenerateArgument(
"unicode", code.Literal(relationalTypeMapping.IsUnicode), mainBuilder, ref firstParameter);
}

if (isFixedLengthDifferent)
{
GenerateArgument(
"fixedLength", code.Literal(relationalTypeMapping.IsFixedLength), mainBuilder, ref firstParameter);
}

if (precisionDifferent)
{
GenerateArgument(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ public SqlServerStringTypeMapping(
private static string GetDefaultStoreName(bool unicode, bool fixedLength)
=> unicode
? fixedLength ? "nchar" : "nvarchar"
: fixedLength
? "char"
: "varchar";
: fixedLength ? "char" : "varchar";

private static DbType? GetDbType(bool unicode, bool fixedLength)
=> unicode
Expand Down
97 changes: 11 additions & 86 deletions test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using static Microsoft.EntityFrameworkCore.WithConstructorsTestBase<TFixture>;

Check failure on line 4 in test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs

View check run for this annotation

Azure Pipelines / efcore-ci (Build macOS)

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs#L4

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs(4,69): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'TFixture' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 4 in test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs

View check run for this annotation

Azure Pipelines / efcore-ci (Build Linux)

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs#L4

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs(4,69): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'TFixture' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 4 in test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs

View check run for this annotation

Azure Pipelines / efcore-ci

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs#L4

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs(4,69): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'TFixture' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 4 in test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs

View check run for this annotation

Azure Pipelines / efcore-ci

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs#L4

test/EFCore.Cosmos.FunctionalTests/CosmosConcurrencyTest.cs(4,69): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'TFixture' could not be found (are you missing a using directive or an assembly reference?)

namespace Microsoft.EntityFrameworkCore;

public class CosmosConcurrencyTest : IClassFixture<CosmosConcurrencyTest.CosmosFixture>
Expand Down Expand Up @@ -45,91 +47,21 @@ public virtual Task Updating_then_updating_the_same_entity_results_in_DbUpdateCo
ctx => ctx.Customers.Single(c => c.Id == "3").Name = "Updated",
ctx => ctx.Customers.Single(c => c.Id == "3").Name = "Updated");

[ConditionalFact]
public async Task Etag_will_return_when_content_response_enabled_false()
{
await using var testDatabase = CosmosTestStore.CreateInitialized(
DatabaseName,
o => o.ContentResponseOnWriteEnabled(false));

var customer = new Customer
{
Id = "4", Name = "Theon",
};

await using (var context = new ConcurrencyContext(CreateOptions(testDatabase)))
{
await context.Database.EnsureCreatedAsync();

await context.AddAsync(customer);

await context.SaveChangesAsync();
}

await using (var context = new ConcurrencyContext(CreateOptions(testDatabase)))
{
var customerFromStore = await context.Set<Customer>().SingleAsync();

Assert.Equal(customer.Id, customerFromStore.Id);
Assert.Equal("Theon", customerFromStore.Name);
Assert.Equal(customer.ETag, customerFromStore.ETag);

context.Remove(customerFromStore);

await context.SaveChangesAsync();
}
}

[ConditionalFact]
public async Task Etag_will_return_when_content_response_enabled_true()
{
await using var testDatabase = CosmosTestStore.CreateInitialized(
DatabaseName,
o => o.ContentResponseOnWriteEnabled());

var customer = new Customer
{
Id = "3", Name = "Theon",
};

await using (var context = new ConcurrencyContext(CreateOptions(testDatabase)))
{
await context.Database.EnsureCreatedAsync();

await context.AddAsync(customer);

await context.SaveChangesAsync();
}

await using (var context = new ConcurrencyContext(CreateOptions(testDatabase)))
{
var customerFromStore = await context.Set<Customer>().SingleAsync();

Assert.Equal(customer.Id, customerFromStore.Id);
Assert.Equal("Theon", customerFromStore.Name);
Assert.Equal(customer.ETag, customerFromStore.ETag);

context.Remove(customerFromStore);

await context.SaveChangesAsync();
}
}

[ConditionalTheory]
[InlineData(null)]
[InlineData(true)]
[InlineData(false)]
public async Task Etag_is_updated_in_entity_after_SaveChanges(bool? contentResponseOnWriteEnabled)
{
await using var testDatabase = CosmosTestStore.CreateInitialized(
DatabaseName,
o =>
var options = new DbContextOptionsBuilder(Fixture.CreateOptions())
.UseCosmos(o =>
{
if (contentResponseOnWriteEnabled.HasValue)
if (contentResponseOnWriteEnabled != null)
{
o.ContentResponseOnWriteEnabled(contentResponseOnWriteEnabled.Value);
}
});
})
.Options;

var customer = new Customer
{
Expand All @@ -139,10 +71,9 @@ public async Task Etag_is_updated_in_entity_after_SaveChanges(bool? contentRespo
};

string etag = null;

await using (var context = new ConcurrencyContext(CreateOptions(testDatabase)))
await using (var context = new ConcurrencyContext(options))
{
await context.Database.EnsureCreatedAsync();
await Fixture.TestStore.CleanAsync(context);

await context.AddAsync(customer);

Expand All @@ -151,7 +82,7 @@ public async Task Etag_is_updated_in_entity_after_SaveChanges(bool? contentRespo
etag = customer.ETag;
}

await using (var context = new ConcurrencyContext(CreateOptions(testDatabase)))
await using (var context = new ConcurrencyContext(options))
{
var customerFromStore = await context.Set<Customer>().SingleAsync();

Expand Down Expand Up @@ -208,7 +139,7 @@ protected virtual async Task ConcurrencyTestAsync<TException>(
where TException : DbUpdateException
{
using var outerContext = CreateContext();
await outerContext.Database.EnsureCreatedAsync();
await Fixture.TestStore.CleanAsync(outerContext);
seedAction?.Invoke(outerContext);
await outerContext.SaveChangesAsync();

Expand Down Expand Up @@ -258,12 +189,6 @@ protected override void OnModelCreating(ModelBuilder builder)
});
}

private DbContextOptions CreateOptions(CosmosTestStore testDatabase)
=> testDatabase.AddProviderOptions(new DbContextOptionsBuilder())
.ConfigureWarnings(w => w.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning))
.EnableDetailedErrors()
.Options;

public class Customer
{
public string Id { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public DbContextOptions CreateOptions(TestStore testStore)
=> AddOptions(testStore.AddProviderOptions(new DbContextOptionsBuilder()))
.EnableDetailedErrors()
.UseInternalServiceProvider(ServiceProvider)
.EnableServiceProviderCaching(false)
.Options;

protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
Expand Down
2 changes: 1 addition & 1 deletion test/EFCore.Specification.Tests/SharedStoreFixtureBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public virtual Task InitializeAsync()

_serviceProvider = services.BuildServiceProvider(validateScopes: true);

TestStore.Initialize(ServiceProvider, CreateContext, c => Seed((TContext)c), c => Clean(c));
TestStore.Initialize(ServiceProvider, CreateContext, c => Seed((TContext)c), Clean);

return Task.CompletedTask;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public virtual DbContextOptions CreateOptions(IEnumerable<ISingletonInterceptor>
? new DbContextOptionsBuilder<DbContext>().UseInternalServiceProvider(
InjectInterceptors(new ServiceCollection(), interceptors)
.BuildServiceProvider(validateScopes: true))
: new DbContextOptionsBuilder<DbContext>().AddInterceptors(interceptors);
: new DbContextOptionsBuilder<DbContext>().AddInterceptors(interceptors)
.EnableServiceProviderCaching(false);

return AddOptions(TestStore.AddProviderOptions(optionsBuilder)).EnableDetailedErrors().Options;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ private IRelationalModel CreateRelationalModel()
microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase.Columns.Add("Char", charColumnBase);
var charArrayColumnBase = new ColumnBase<ColumnMappingBase>("CharArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase);
microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase.Columns.Add("CharArray", charArrayColumnBase);
var charToStringConverterPropertyColumnBase = new ColumnBase<ColumnMappingBase>("CharToStringConverterProperty", "nvarchar(1)", microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase);
var charToStringConverterPropertyColumnBase = new ColumnBase<ColumnMappingBase>("CharToStringConverterProperty", "nchar(1)", microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase);
microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase.Columns.Add("CharToStringConverterProperty", charToStringConverterPropertyColumnBase);
var dateOnlyColumnBase = new ColumnBase<ColumnMappingBase>("DateOnly", "date", microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase);
microsoftEntityFrameworkCoreScaffoldingCompiledModelTestBaseManyTypesTableBase.Columns.Add("DateOnly", dateOnlyColumnBase);
Expand Down Expand Up @@ -1044,7 +1044,7 @@ private IRelationalModel CreateRelationalModel()
manyTypesTable.Columns.Add("Char", charColumn);
var charArrayColumn = new Column("CharArray", "nvarchar(max)", manyTypesTable);
manyTypesTable.Columns.Add("CharArray", charArrayColumn);
var charToStringConverterPropertyColumn = new Column("CharToStringConverterProperty", "nvarchar(1)", manyTypesTable);
var charToStringConverterPropertyColumn = new Column("CharToStringConverterProperty", "nchar(1)", manyTypesTable);
manyTypesTable.Columns.Add("CharToStringConverterProperty", charToStringConverterPropertyColumn);
var dateOnlyColumn = new Column("DateOnly", "date", manyTypesTable);
manyTypesTable.Columns.Add("DateOnly", dateOnlyColumn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
mappingInfo: new RelationalTypeMappingInfo(
storeTypeName: "char(20)",
size: 20,
fixedLength: true,
dbType: System.Data.DbType.AnsiStringFixedLength));
data.AddAnnotation("Relational:IsFixedLength", true);
data.AddAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.None);
Expand Down
Loading

0 comments on commit dbd6252

Please sign in to comment.