From f702fbbfcace0d664c70e87d233d4991f8355c38 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Mon, 12 Sep 2022 14:14:58 +0100 Subject: [PATCH] Use different internal service provider when EnableNullChecks is changed Fixes #26840 --- .../Internal/InMemoryOptionsExtension.cs | 21 ++++++- .../Internal/InMemorySingletonOptions.cs | 9 +++ .../GlobalDatabaseTest.cs | 61 +++++++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/EFCore.InMemory/Infrastructure/Internal/InMemoryOptionsExtension.cs b/src/EFCore.InMemory/Infrastructure/Internal/InMemoryOptionsExtension.cs index a7d80cc4094..b2470752079 100644 --- a/src/EFCore.InMemory/Infrastructure/Internal/InMemoryOptionsExtension.cs +++ b/src/EFCore.InMemory/Infrastructure/Internal/InMemoryOptionsExtension.cs @@ -175,6 +175,11 @@ public override string LogFragment builder.Append("StoreName=").Append(Extension._storeName).Append(' '); + if (!Extension._nullabilityCheckEnabled) + { + builder.Append("NullabilityChecksEnabled "); + } + _logFragment = builder.ToString(); } @@ -183,14 +188,24 @@ public override string LogFragment } public override int GetServiceProviderHashCode() - => Extension._databaseRoot?.GetHashCode() ?? 0; + { + var hashCode = new HashCode(); + hashCode.Add(Extension._databaseRoot); + hashCode.Add(Extension._nullabilityCheckEnabled); + return hashCode.ToHashCode(); + } public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) => other is ExtensionInfo otherInfo - && Extension._databaseRoot == otherInfo.Extension._databaseRoot; + && Extension._databaseRoot == otherInfo.Extension._databaseRoot + && Extension._nullabilityCheckEnabled == otherInfo.Extension._nullabilityCheckEnabled; public override void PopulateDebugInfo(IDictionary debugInfo) - => debugInfo["InMemoryDatabase:DatabaseRoot"] + { + debugInfo["InMemoryDatabase:DatabaseRoot"] = (Extension._databaseRoot?.GetHashCode() ?? 0).ToString(CultureInfo.InvariantCulture); + debugInfo["InMemoryDatabase:NullabilityChecksEnabled"] + = (!Extension._nullabilityCheckEnabled).GetHashCode().ToString(CultureInfo.InvariantCulture); + } } } diff --git a/src/EFCore.InMemory/Infrastructure/Internal/InMemorySingletonOptions.cs b/src/EFCore.InMemory/Infrastructure/Internal/InMemorySingletonOptions.cs index 6955c27b2a2..a1b64414aea 100644 --- a/src/EFCore.InMemory/Infrastructure/Internal/InMemorySingletonOptions.cs +++ b/src/EFCore.InMemory/Infrastructure/Internal/InMemorySingletonOptions.cs @@ -46,6 +46,15 @@ public virtual void Validate(IDbContextOptions options) nameof(InMemoryDbContextOptionsExtensions.UseInMemoryDatabase), nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); } + + if (inMemoryOptions != null + && IsNullabilityCheckEnabled != inMemoryOptions.IsNullabilityCheckEnabled) + { + throw new InvalidOperationException( + CoreStrings.SingletonOptionChanged( + nameof(InMemoryDbContextOptionsBuilder.EnableNullChecks), + nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); + } } /// diff --git a/test/EFCore.InMemory.FunctionalTests/GlobalDatabaseTest.cs b/test/EFCore.InMemory.FunctionalTests/GlobalDatabaseTest.cs index a0f07434ee9..ab62f0045bc 100644 --- a/test/EFCore.InMemory.FunctionalTests/GlobalDatabaseTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/GlobalDatabaseTest.cs @@ -143,6 +143,29 @@ public void Global_store_can_be_used_when_AddDbContext_force_different_internal_ } } + [ConditionalFact] + public void EnableNullChecks_forces_different_internal_service_provider() + { + using var context1 = new ChangeNullabilityChecksContext(enableNullChecks: true); + using var context2 = new ChangeNullabilityChecksContext(enableNullChecks: false); + Assert.NotSame(((IInfrastructure)context1).Instance, ((IInfrastructure)context2).Instance); + } + + private class ChangeNullabilityChecksContext : DbContext + { + private readonly bool _enableNullChecks; + + public ChangeNullabilityChecksContext(bool enableNullChecks) + { + _enableNullChecks = enableNullChecks; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder + .UseInMemoryDatabase(nameof(ChangeNullabilityChecksCacheContext), + b => b.EnableNullChecks(_enableNullChecks)); + } + [ConditionalFact] public void Throws_changing_global_store_in_OnConfiguring_when_UseInternalServiceProvider() { @@ -181,6 +204,44 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) .UseInMemoryDatabase(nameof(ChangeSdlCacheContext), _on ? _databaseRoot : null); } + [ConditionalFact] + public void Throws_changing_nullability_checks_in_OnConfiguring_when_UseInternalServiceProvider() + { + using (var context = new ChangeNullabilityChecksCacheContext(false)) + { + Assert.NotNull(context.Model); + } + + using (var context = new ChangeNullabilityChecksCacheContext(true)) + { + Assert.Equal( + CoreStrings.SingletonOptionChanged( + nameof(InMemoryDbContextOptionsBuilder.EnableNullChecks), + nameof(DbContextOptionsBuilder.UseInternalServiceProvider)), + Assert.Throws(() => context.Model).Message); + } + } + + private class ChangeNullabilityChecksCacheContext : DbContext + { + private static readonly IServiceProvider _serviceProvider + = new ServiceCollection() + .AddEntityFrameworkInMemoryDatabase() + .BuildServiceProvider(validateScopes: true); + + private readonly bool _on; + + public ChangeNullabilityChecksCacheContext(bool on) + { + _on = on; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder + .UseInternalServiceProvider(_serviceProvider) + .UseInMemoryDatabase(nameof(ChangeSdlCacheContext), b => b.EnableNullChecks(_on)); + } + private class BooFooContext : DbContext { public BooFooContext(DbContextOptions options)