From f4e549ead524b1fcb3514ad49ce85fbef328b564 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Sun, 5 Feb 2023 21:20:22 +0000 Subject: [PATCH 1/8] [release/7.0] Use correct provider type for nullable value type with converter Port of #29746 Fixes #29985 --- .../RelationalModelValidator.cs | 5 +- .../Metadata/Internal/ColumnBase.cs | 7 +- .../Update/Internal/ColumnAccessorsFactory.cs | 7 +- .../RowForeignKeyValueFactoryFactory.cs | 75 +++++++++++++------ ...lablePrincipalRowForeignKeyValueFactory.cs | 1 - .../Internal/SimpleRowKeyValueFactory.cs | 35 ++++++++- .../Internal/CurrentValueComparerFactory.cs | 28 +++++-- src/EFCore/ChangeTracking/ValueComparer`.cs | 3 +- src/EFCore/Metadata/Internal/Property.cs | 21 +++++- ...Core.Relational.Specification.Tests.csproj | 1 + .../RelationalModelValidatorTest.cs | 10 ++- .../Internal/MigrationsModelDifferTest.cs | 30 ++++++++ .../UpdatesTestBase.cs | 11 ++- .../Update/MismatchedKeyTypesSqlServerTest.cs | 2 +- test/ef.Tests/ef.Tests.csproj | 1 + 15 files changed, 190 insertions(+), 47 deletions(-) diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index d5c8102b7a0..62b468ea5fe 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -1393,8 +1393,6 @@ protected virtual void ValidateCompatible( storeObject.DisplayName())); } - var typeMapping = property.GetRelationalTypeMapping(); - var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping(); var currentTypeString = property.GetColumnType(storeObject); var previousTypeString = duplicateProperty.GetColumnType(storeObject); if (!string.Equals(currentTypeString, previousTypeString, StringComparison.OrdinalIgnoreCase)) @@ -1411,6 +1409,9 @@ protected virtual void ValidateCompatible( currentTypeString)); } + var typeMapping = property.GetRelationalTypeMapping(); + var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping(); + Type currentProviderType, previousProviderType; if (QuirkEnabled29531) { diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs b/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs index cd7fdaff3b6..29ab84f6112 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs @@ -12,6 +12,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal; public class ColumnBase : Annotatable, IColumnBase where TColumnMappingBase : class, IColumnMappingBase { + private static readonly bool QuirkEnabled29985 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled; + private Type? _providerClrType; /// @@ -84,7 +87,9 @@ public virtual Type ProviderClrType } var typeMapping = StoreTypeMapping; - var providerType = typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType; + var providerType = QuirkEnabled29985 + ? typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType + : typeMapping.Converter?.ProviderClrType.UnwrapNullableType() ?? typeMapping.ClrType; return _providerClrType = providerType; } diff --git a/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs b/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs index 8e7f9d1ce1a..6a0a75a38ad 100644 --- a/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs +++ b/src/EFCore.Relational/Update/Internal/ColumnAccessorsFactory.cs @@ -13,6 +13,9 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal; /// public static class ColumnAccessorsFactory { + private static readonly bool QuirkEnabled29985 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -51,7 +54,7 @@ private static ColumnAccessors CreateGeneric(IColumn column) var providerValue = entry.GetCurrentProviderValue(property); if (providerValue == null - && !typeof(TColumn).IsNullableType()) + && (!QuirkEnabled29985 || !typeof(TColumn).IsNullableType())) { return (value!, valueFound); } @@ -94,7 +97,7 @@ private static ColumnAccessors CreateGeneric(IColumn column) var providerValue = entry.GetOriginalProviderValue(property); if (providerValue == null - && !typeof(TColumn).IsNullableType()) + && (!QuirkEnabled29985 || !typeof(TColumn).IsNullableType())) { return (value!, valueFound); } diff --git a/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs b/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs index bf16ff5289f..f8692e4c73b 100644 --- a/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs +++ b/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs @@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal; /// public class RowForeignKeyValueFactoryFactory : IRowForeignKeyValueFactoryFactory { + private static readonly bool QuirkEnabled29985 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled; + private readonly IValueConverterSelector _valueConverterSelector; /// @@ -53,30 +56,60 @@ private static IRowForeignKeyValueFactory CreateSimple( IValueConverterSelector valueConverterSelector) where TKey : notnull { - var dependentColumn = foreignKey.Columns.Single(); - var dependentType = dependentColumn.ProviderClrType; - var principalType = foreignKey.PrincipalColumns.Single().ProviderClrType; - var columnAccessors = ((Column)dependentColumn).Accessors; - - if (dependentType.IsNullableType() - && principalType.IsNullableType()) + if (QuirkEnabled29985) { - return new SimpleFullyNullableRowForeignKeyValueFactory( - foreignKey, dependentColumn, columnAccessors, valueConverterSelector); - } + var dependentColumn = foreignKey.Columns.Single(); + var dependentType = dependentColumn.ProviderClrType; + var principalType = foreignKey.PrincipalColumns.Single().ProviderClrType; + var columnAccessors = ((Column)dependentColumn).Accessors; + + if (dependentType.IsNullableType() + && principalType.IsNullableType()) + { + return new SimpleFullyNullableRowForeignKeyValueFactory( + foreignKey, dependentColumn, columnAccessors, valueConverterSelector); + } + + if (dependentType.IsNullableType()) + { + return (IRowForeignKeyValueFactory)Activator.CreateInstance( + typeof(SimpleNullableRowForeignKeyValueFactory<,>).MakeGenericType( + typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors, valueConverterSelector)!; + } - if (dependentType.IsNullableType()) + return principalType.IsNullableType() + ? (IRowForeignKeyValueFactory)Activator.CreateInstance( + typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType( + typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)! + : new SimpleNonNullableRowForeignKeyValueFactory( + foreignKey, dependentColumn, columnAccessors, valueConverterSelector); } + else { - return (IRowForeignKeyValueFactory)Activator.CreateInstance( - typeof(SimpleNullableRowForeignKeyValueFactory<,>).MakeGenericType( - typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors, valueConverterSelector)!; - } + var dependentColumn = foreignKey.Columns.First(); + var principalColumn = foreignKey.PrincipalColumns.First(); + var columnAccessors = ((Column)dependentColumn).Accessors; + + if (principalColumn.ProviderClrType.IsNullableType() + || (dependentColumn.IsNullable + && principalColumn.IsNullable)) + { + return new SimpleFullyNullableRowForeignKeyValueFactory( + foreignKey, dependentColumn, columnAccessors, valueConverterSelector); + } - return principalType.IsNullableType() - ? (IRowForeignKeyValueFactory)Activator.CreateInstance( - typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType( - typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)! - : new SimpleNonNullableRowForeignKeyValueFactory( - foreignKey, dependentColumn, columnAccessors, valueConverterSelector); + if (dependentColumn.IsNullable) + { + return (IRowForeignKeyValueFactory)Activator.CreateInstance( + typeof(SimpleNullableRowForeignKeyValueFactory<,>).MakeGenericType( + typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors, valueConverterSelector)!; + } + + return principalColumn.IsNullable + ? (IRowForeignKeyValueFactory)Activator.CreateInstance( + typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType( + typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)! + : new SimpleNonNullableRowForeignKeyValueFactory( + foreignKey, dependentColumn, columnAccessors, valueConverterSelector); + } } } diff --git a/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs b/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs index 5ad7e24a1fe..107624fc772 100644 --- a/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs +++ b/src/EFCore.Relational/Update/Internal/SimpleNullablePrincipalRowForeignKeyValueFactory.cs @@ -13,7 +13,6 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal; /// public class SimpleNullablePrincipalRowForeignKeyValueFactory : RowForeignKeyValueFactory - where TNonNullableKey : struct { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs b/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs index 5b2af2d9142..95368026c99 100644 --- a/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs +++ b/src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs @@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal; /// public class SimpleRowKeyValueFactory : IRowKeyValueFactory { + private static readonly bool QuirkEnabled29985 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled; + private readonly IUniqueConstraint _constraint; private readonly IColumn _column; private readonly ColumnAccessors _columnAccessors; @@ -138,8 +141,36 @@ private sealed class NoNullsCustomEqualityComparer : IEqualityComparer public NoNullsCustomEqualityComparer(ValueComparer comparer) { - _equals = (Func)comparer.EqualsExpression.Compile(); - _hashCode = (Func)comparer.HashCodeExpression.Compile(); + if (QuirkEnabled29985) + { + _equals = (Func)comparer.EqualsExpression.Compile(); + _hashCode = (Func)comparer.HashCodeExpression.Compile(); + } + else + { + var equals = comparer.EqualsExpression; + var getHashCode = comparer.HashCodeExpression; + var type = typeof(TKey); + if (type != comparer.Type) + { + var newEqualsParam1 = Expression.Parameter(type, "v1"); + var newEqualsParam2 = Expression.Parameter(type, "v2"); + equals = Expression.Lambda( + comparer.ExtractEqualsBody( + Expression.Convert(newEqualsParam1, comparer.Type), + Expression.Convert(newEqualsParam2, comparer.Type)), + newEqualsParam1, newEqualsParam2); + + + var newHashCodeParam = Expression.Parameter(type, "v"); + getHashCode = Expression.Lambda( + comparer.ExtractHashCodeBody( + Expression.Convert(newHashCodeParam, comparer.Type)), + newHashCodeParam); + } + + _equals = (Func)equals.Compile(); + _hashCode = (Func)getHashCode.Compile(); } } public bool Equals(TKey? x, TKey? y) diff --git a/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs b/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs index 3afa347deaa..cdcbc6f397d 100644 --- a/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs @@ -13,6 +13,9 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; /// public class CurrentValueComparerFactory { + private static readonly bool QuirkEnabled29985 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -49,12 +52,25 @@ public virtual IComparer Create(IPropertyBase propertyBase) var nonNullableProviderType = providerType.UnwrapNullableType(); if (IsGenericComparable(providerType, nonNullableProviderType)) { - var comparerType = modelType.IsClass - ? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType) - : modelType == converter.ModelClrType - ? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType) - : typeof(NullableStructCurrentProviderValueComparer<,>).MakeGenericType( - nonNullableModelType, converter.ProviderClrType); + Type comparerType; + if (QuirkEnabled29985) + { + comparerType = modelType.IsClass + ? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType) + : modelType == converter.ModelClrType + ? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType) + : typeof(NullableStructCurrentProviderValueComparer<,>).MakeGenericType( + nonNullableModelType, converter.ProviderClrType); + } + else + { + comparerType = modelType.IsClass + ? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, providerType) + : modelType == converter.ModelClrType + ? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, providerType) + : typeof(NullableStructCurrentProviderValueComparer<,>).MakeGenericType( + nonNullableModelType, providerType); + } return (IComparer)Activator.CreateInstance(comparerType, propertyBase, converter)!; } diff --git a/src/EFCore/ChangeTracking/ValueComparer`.cs b/src/EFCore/ChangeTracking/ValueComparer`.cs index 4712356813b..99e6fffca78 100644 --- a/src/EFCore/ChangeTracking/ValueComparer`.cs +++ b/src/EFCore/ChangeTracking/ValueComparer`.cs @@ -117,8 +117,7 @@ public ValueComparer( || unwrappedType == typeof(Guid) || unwrappedType == typeof(bool) || unwrappedType == typeof(decimal) - || unwrappedType == typeof(object) - ) + || unwrappedType == typeof(object)) { return Expression.Lambda>( Expression.Equal(param1, param2), diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index 7287b94bb63..ce0b14cf47f 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -30,6 +30,9 @@ public class Property : PropertyBase, IMutableProperty, IConventionProperty, IPr private static readonly bool QuirkEnabled29642 = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29642", out var enabled) && enabled; + private static readonly bool QuirkEnabled29985 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -803,8 +806,8 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior() => FindAnnotation(CoreAnnotationNames.ProviderClrType)?.GetConfigurationSource(); private Type GetEffectiveProviderClrType() - => TypeMapping?.Converter?.ProviderClrType - ?? ClrType.UnwrapNullableType(); + => (TypeMapping?.Converter?.ProviderClrType + ?? ClrType).UnwrapNullableType(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1012,10 +1015,20 @@ public virtual CoreTypeMapping? TypeMapping /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual ValueComparer? GetProviderValueComparer() - => GetProviderValueComparer(null) - ?? (GetEffectiveProviderClrType() == ClrType + { + if (QuirkEnabled29985) + { + return GetProviderValueComparer(null) + ?? (GetEffectiveProviderClrType() == ClrType + ? GetKeyValueComparer() + : TypeMapping?.ProviderValueComparer); + } + + return GetProviderValueComparer(null) + ?? (GetEffectiveProviderClrType() == ClrType.UnwrapNullableType() ? GetKeyValueComparer() : TypeMapping?.ProviderValueComparer); + } private ValueComparer? GetProviderValueComparer(HashSet? checkedProperties) { diff --git a/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj b/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj index cba1f1ace6a..09df0e4b1a7 100644 --- a/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj +++ b/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj @@ -53,6 +53,7 @@ + diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index c1646e0bfe8..e7d19705146 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -421,22 +421,26 @@ public virtual void Detects_incompatible_primary_key_columns_with_shared_table() } [ConditionalFact] - public virtual void Passes_on_not_configured_shared_columns_with_shared_table() + public virtual void Passes_on_shared_columns_with_shared_table() { var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().Property(a => a.P0).HasColumnName(nameof(A.P0)); + modelBuilder.Entity().Property(a => a.P3).HasColumnName(nameof(A.P3)) + .HasConversion(e => (long?)e, e => (int?)e); modelBuilder.Entity().Property(a => a.P1).IsRequired(); modelBuilder.Entity().ToTable("Table"); modelBuilder.Entity().Property(b => b.P0).HasColumnName(nameof(A.P0)).HasColumnType("someInt"); + modelBuilder.Entity().Property(b => b.P3).HasColumnName(nameof(A.P3)) + .HasConversion(e => (long)e, e => (int?)e); modelBuilder.Entity().ToTable("Table"); Validate(modelBuilder); } [ConditionalFact] - public virtual void Throws_on_not_configured_shared_columns_with_shared_table_with_dependents() + public virtual void Throws_on_nullable_shared_columns_with_shared_table_with_dependents() { var modelBuilder = CreateConventionModelBuilder(); @@ -450,7 +454,7 @@ public virtual void Throws_on_not_configured_shared_columns_with_shared_table_wi } [ConditionalFact] - public virtual void Warns_on_not_configured_shared_columns_with_shared_table() + public virtual void Warns_on_no_required_columns_with_shared_table() { var modelBuilder = CreateConventionModelBuilder(); diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs index fa8a36cd51d..4f2c8f0bf29 100644 --- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs +++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs @@ -10281,6 +10281,36 @@ public void SeedData_key_refactoring_value_conversion() Assert.Empty, Assert.Empty); + [ConditionalFact] // Issue #29985 + public void SeedData_value_conversion_nullable_datetime() + => Execute( + common => common.Entity( + "EntityWithOneProperty", + x => + { + x.Property("Id"); + x.HasData(new { Id = 42 }); + }), + source => source.Entity( + "EntityWithOneProperty", + x => + { + x.Property("Value1") + .HasColumnType("datetime2") + .HasConversion( + p => p, + p => p != null ? DateTime.SpecifyKind(p.Value, DateTimeKind.Utc) : null); + }), + target => target.Entity( + "EntityWithOneProperty", + x => + { + x.Property("Value1") + .HasColumnType("datetime2"); + }), + Assert.Empty, + Assert.Empty); + [ConditionalFact] public void SeedData_change_enum_conversion() => Execute( diff --git a/test/EFCore.Specification.Tests/UpdatesTestBase.cs b/test/EFCore.Specification.Tests/UpdatesTestBase.cs index 09663a1c312..c02dc2c1197 100644 --- a/test/EFCore.Specification.Tests/UpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/UpdatesTestBase.cs @@ -430,7 +430,11 @@ public virtual void Can_change_enums_with_conversion() => ExecuteWithStrategyInTransaction( context => { - var person = new Person("1", null) { Address = new Address { Country = Country.Eswatini, City = "Bulembu" }, Country = "Eswatini" }; + var person = new Person("1", null) + { + Address = new Address { Country = Country.Eswatini, City = "Bulembu" }, + Country = "Eswatini" + }; context.Add(person); @@ -439,8 +443,9 @@ public virtual void Can_change_enums_with_conversion() context => { var person = context.Set().Single(); - person.Address = new Address { Country = Country.Türkiye, City = "Konya" }; + person.Address = new Address { Country = Country.Türkiye, City = "Konya", ZipCode = 42100 }; person.Country = "Türkiye"; + person.ZipCode = "42100"; context.SaveChanges(); }, @@ -450,7 +455,9 @@ public virtual void Can_change_enums_with_conversion() Assert.Equal(Country.Türkiye, person.Address!.Country); Assert.Equal("Konya", person.Address.City); + Assert.Equal(42100, person.Address.ZipCode); Assert.Equal("Türkiye", person.Country); + Assert.Equal("42100", person.ZipCode); }); [ConditionalFact] diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs index 6f34150ae67..6ca8415795f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/MismatchedKeyTypesSqlServerTest.cs @@ -361,7 +361,7 @@ public virtual void Queries_work_but_SaveChanges_fails_when_keys_incompatible_in Assert.Equal( RelationalStrings.StoredKeyTypesNotConvertable( nameof(OptionalSingleBad.PrincipalId), "uniqueidentifier", "bigint", nameof(PrincipalBad.Id)), - Assert.Throws(() => context.SaveChanges()).InnerException!.Message); + Assert.Throws(() => context.SaveChanges()).InnerException!.InnerException!.Message); } protected class MismatchedKeyTypesContextNoFks : MismatchedKeyTypesContext diff --git a/test/ef.Tests/ef.Tests.csproj b/test/ef.Tests/ef.Tests.csproj index 98acdff6030..20abc2fb11b 100644 --- a/test/ef.Tests/ef.Tests.csproj +++ b/test/ef.Tests/ef.Tests.csproj @@ -39,6 +39,7 @@ + From eb28fd34e64e796748127621a34af31a27d646d8 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Tue, 7 Feb 2023 21:29:26 +0000 Subject: [PATCH 2/8] Review updates --- .../Infrastructure/RelationalModelValidator.cs | 5 ++--- .../Update/Internal/RowForeignKeyValueFactoryFactory.cs | 9 ++++++--- .../ChangeTracking/Internal/InternalEntityEntry.cs | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index 62b468ea5fe..d5c8102b7a0 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -1393,6 +1393,8 @@ protected virtual void ValidateCompatible( storeObject.DisplayName())); } + var typeMapping = property.GetRelationalTypeMapping(); + var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping(); var currentTypeString = property.GetColumnType(storeObject); var previousTypeString = duplicateProperty.GetColumnType(storeObject); if (!string.Equals(currentTypeString, previousTypeString, StringComparison.OrdinalIgnoreCase)) @@ -1409,9 +1411,6 @@ protected virtual void ValidateCompatible( currentTypeString)); } - var typeMapping = property.GetRelationalTypeMapping(); - var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping(); - Type currentProviderType, previousProviderType; if (QuirkEnabled29531) { diff --git a/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs b/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs index f8692e4c73b..d4f532f284c 100644 --- a/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs +++ b/src/EFCore.Relational/Update/Internal/RowForeignKeyValueFactoryFactory.cs @@ -80,9 +80,11 @@ private static IRowForeignKeyValueFactory CreateSimple( return principalType.IsNullableType() ? (IRowForeignKeyValueFactory)Activator.CreateInstance( typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType( - typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)! + typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TForeignKey)), foreignKey, dependentColumn, + columnAccessors)! : new SimpleNonNullableRowForeignKeyValueFactory( - foreignKey, dependentColumn, columnAccessors, valueConverterSelector); } + foreignKey, dependentColumn, columnAccessors, valueConverterSelector); + } else { var dependentColumn = foreignKey.Columns.First(); @@ -107,7 +109,8 @@ private static IRowForeignKeyValueFactory CreateSimple( return principalColumn.IsNullable ? (IRowForeignKeyValueFactory)Activator.CreateInstance( typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType( - typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)! + typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, + columnAccessors)! : new SimpleNonNullableRowForeignKeyValueFactory( foreignKey, dependentColumn, columnAccessors, valueConverterSelector); } diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs index 7e235b36e0d..9e835b37d15 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs @@ -186,7 +186,7 @@ public async Task SetEntityStateAsync( CancellationToken cancellationToken = default) { var oldState = _stateData.EntityState; - bool adding; + var adding = false; await SetupAsync().ConfigureAwait(false); if ((adding || oldState is EntityState.Detached) From b4968ff48a8b0f72536ea73cd1e0b3f732e70741 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Tue, 31 Jan 2023 19:58:53 +0000 Subject: [PATCH 3/8] Prevent re-entrance into DetectChanges Fixes #30122 Fixes #30135 EF7 contained some new calls to local `DetectChanges`. This resulted in re-entrance into `DetectChanges` for some types of graphs. This change prevents that. --- .../ChangeTracking/Internal/ChangeDetector.cs | 80 ++-- ...phUpdatesIdentityResolutionInMemoryTest.cs | 153 +++++++ .../GraphUpdates/GraphUpdatesInMemoryTest.cs | 177 +------- .../GraphUpdatesInMemoryTestBase.cs | 188 +++++++++ .../GraphUpdates/GraphUpdatesTestBase.cs | 389 +++++++++++++++--- .../GraphUpdatesTestBaseMiscellaneous.cs | 363 ++++++++++++---- .../GraphUpdatesSqlServerOwnedTest.cs | 28 ++ ...hUpdatesSqliteSnapshotNotificationsTest.cs | 2 +- .../GraphUpdatesSqliteTestBase.cs | 2 +- .../StoreValueGenerationLegacySqliteTest.cs | 16 + 10 files changed, 1066 insertions(+), 332 deletions(-) create mode 100644 test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesIdentityResolutionInMemoryTest.cs create mode 100644 test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTestBase.cs diff --git a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs index 34add1c5dac..78898e3f024 100644 --- a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs +++ b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs @@ -17,6 +17,7 @@ public class ChangeDetector : IChangeDetector { private readonly IDiagnosticsLogger _logger; private readonly ILoggingOptions _loggingOptions; + private bool _inCascadeDelete; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -112,37 +113,51 @@ public virtual void PropertyChanging(InternalEntityEntry entry, IPropertyBase pr /// public virtual void DetectChanges(IStateManager stateManager) { - OnDetectingAllChanges(stateManager); - var changesFound = false; - - _logger.DetectChangesStarting(stateManager.Context); + if (_inCascadeDelete) + { + return; + } - foreach (var entry in stateManager.ToList()) // Might be too big, but usually _all_ entities are using Snapshot tracking + try { - switch (entry.EntityState) - { - case EntityState.Detached: - break; - case EntityState.Deleted: - if (entry.SharedIdentityEntry != null) - { - continue; - } + _inCascadeDelete = true; - goto default; - default: - if (LocalDetectChanges(entry)) - { - changesFound = true; - } + OnDetectingAllChanges(stateManager); + var changesFound = false; + + _logger.DetectChangesStarting(stateManager.Context); - break; + foreach (var entry in stateManager.ToList()) // Might be too big, but usually _all_ entities are using Snapshot tracking + { + switch (entry.EntityState) + { + case EntityState.Detached: + break; + case EntityState.Deleted: + if (entry.SharedIdentityEntry != null) + { + continue; + } + + goto default; + default: + if (LocalDetectChanges(entry)) + { + changesFound = true; + } + + break; + } } - } - _logger.DetectChangesCompleted(stateManager.Context); + _logger.DetectChangesCompleted(stateManager.Context); - OnDetectedAllChanges(stateManager, changesFound); + OnDetectedAllChanges(stateManager, changesFound); + } + finally + { + _inCascadeDelete = false; + } } /// @@ -152,7 +167,22 @@ public virtual void DetectChanges(IStateManager stateManager) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual void DetectChanges(InternalEntityEntry entry) - => DetectChanges(entry, new HashSet { entry }); + { + if (_inCascadeDelete) + { + return; + } + + try + { + _inCascadeDelete = true; + DetectChanges(entry, new HashSet { entry }); + } + finally + { + _inCascadeDelete = false; + } + } private bool DetectChanges(InternalEntityEntry entry, HashSet visited) { diff --git a/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesIdentityResolutionInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesIdentityResolutionInMemoryTest.cs new file mode 100644 index 00000000000..3c096d372f6 --- /dev/null +++ b/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesIdentityResolutionInMemoryTest.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore; + +public class GraphUpdatesIdentityResolutionInMemoryTest + : GraphUpdatesInMemoryTestBase +{ + public GraphUpdatesIdentityResolutionInMemoryTest(InMemoryIdentityResolutionFixture fixture) + : base(fixture) + { + } + + [ConditionalFact] + public void Can_attach_full_required_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadRequiredGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryRequiredGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + [ConditionalFact] + public void Can_attach_full_optional_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadOptionalGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryOptionalGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + [ConditionalFact] + public void Can_attach_full_required_non_PK_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadRequiredNonPkGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryRequiredNonPkGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + [ConditionalFact] + public void Can_attach_full_required_AK_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadRequiredAkGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryRequiredAkGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + [ConditionalFact] + public void Can_attach_full_optional_AK_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadOptionalAkGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryOptionalAkGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + [ConditionalFact] + public void Can_attach_full_required_non_PK_AK_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadRequiredNonPkAkGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryRequiredNonPkAkGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + [ConditionalFact] + public void Can_attach_full_required_one_to_many_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadOptionalOneToManyGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryOptionalOneToManyGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + [ConditionalFact] + public void Can_attach_full_required_composite_graph_of_duplicates() + => ExecuteWithStrategyInTransaction( + context => + { + var trackedRoot = LoadRequiredCompositeGraph(context); + var entries = context.ChangeTracker.Entries().ToList(); + + context.Attach(QueryRequiredCompositeGraph(context).AsNoTracking().Single(IsTheRoot)); + + AssertEntries(entries, context.ChangeTracker.Entries().ToList()); + AssertNavigations(trackedRoot); + + Assert.Equal(0, context.SaveChanges()); + }); + + public class InMemoryIdentityResolutionFixture : GraphUpdatesInMemoryFixtureBase + { + protected override string StoreName + => "GraphUpdatesIdentityResolutionTest"; + + public override bool HasIdentityResolution + => true; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder).AddInterceptors(new UpdatingIdentityResolutionInterceptor()); + } +} diff --git a/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTest.cs index b23e13f5865..57ad5484c02 100644 --- a/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTest.cs @@ -3,188 +3,17 @@ namespace Microsoft.EntityFrameworkCore; -public class GraphUpdatesInMemoryTest : GraphUpdatesTestBase +public class GraphUpdatesInMemoryTest + : GraphUpdatesInMemoryTestBase { public GraphUpdatesInMemoryTest(InMemoryFixture fixture) : base(fixture) { } - // In-memory database does not have database default values - public override Task Can_insert_when_composite_FK_has_default_value_for_one_part(bool async) - => Task.CompletedTask; - - // In-memory database does not have database default values - public override Task Can_insert_when_FK_has_default_value(bool async) - => Task.CompletedTask; - - public override void Required_many_to_one_dependents_are_cascade_deleted_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Optional_many_to_one_dependents_are_orphaned_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_many_to_one_dependents_with_alternate_key_are_cascade_deleted_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Optional_many_to_one_dependents_with_alternate_key_are_orphaned_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Optional_one_to_one_relationships_are_one_to_one( - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_one_to_one_relationships_are_one_to_one( - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Save_required_one_to_one_changed_by_reference( - ChangeMechanism changeMechanism, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Sever_required_one_to_one( - ChangeMechanism changeMechanism, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_one_to_one_are_cascade_deleted_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_non_PK_one_to_one_are_cascade_deleted_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Optional_one_to_one_are_orphaned_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_one_to_one_are_cascade_detached_when_Added( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_non_PK_one_to_one_are_cascade_detached_when_Added( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Optional_one_to_one_with_AK_relationships_are_one_to_one( - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_one_to_one_with_AK_relationships_are_one_to_one( - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_one_to_one_with_alternate_key_are_cascade_deleted_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_non_PK_one_to_one_with_alternate_key_are_cascade_deleted_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Optional_one_to_one_with_alternate_key_are_orphaned_in_store( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_non_PK_one_to_one_with_alternate_key_are_cascade_detached_when_Added( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - public override void Required_one_to_one_with_alternate_key_are_cascade_detached_when_Added( - CascadeTiming? cascadeDeleteTiming, - CascadeTiming? deleteOrphansTiming) - { - // FK uniqueness not enforced in in-memory database - } - - protected override void ExecuteWithStrategyInTransaction( - Action testOperation, - Action nestedTestOperation1 = null, - Action nestedTestOperation2 = null, - Action nestedTestOperation3 = null) - { - base.ExecuteWithStrategyInTransaction(testOperation, nestedTestOperation1, nestedTestOperation2, nestedTestOperation3); - Fixture.Reseed(); - } - - protected override async Task ExecuteWithStrategyInTransactionAsync( - Func testOperation, - Func nestedTestOperation1 = null, - Func nestedTestOperation2 = null, - Func nestedTestOperation3 = null) - { - await base.ExecuteWithStrategyInTransactionAsync( - testOperation, nestedTestOperation1, nestedTestOperation2, nestedTestOperation3); - - Fixture.Reseed(); - } - - public class InMemoryFixture : GraphUpdatesFixtureBase + public class InMemoryFixture : GraphUpdatesInMemoryFixtureBase { protected override string StoreName => "GraphUpdatesTest"; - - protected override ITestStoreFactory TestStoreFactory - => InMemoryTestStoreFactory.Instance; - - public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).ConfigureWarnings(w => w.Log(InMemoryEventId.TransactionIgnoredWarning)); } } diff --git a/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTestBase.cs b/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTestBase.cs new file mode 100644 index 00000000000..20d3d07362c --- /dev/null +++ b/test/EFCore.InMemory.FunctionalTests/GraphUpdates/GraphUpdatesInMemoryTestBase.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore; + +public abstract class GraphUpdatesInMemoryTestBase : GraphUpdatesTestBase + where TFixture : GraphUpdatesInMemoryTestBase.GraphUpdatesInMemoryFixtureBase, new() +{ + protected GraphUpdatesInMemoryTestBase(TFixture fixture) + : base(fixture) + { + } + + // In-memory database does not have database default values + public override Task Can_insert_when_composite_FK_has_default_value_for_one_part(bool async) + => Task.CompletedTask; + + // In-memory database does not have database default values + public override Task Can_insert_when_FK_has_default_value(bool async) + => Task.CompletedTask; + + public override void Required_many_to_one_dependents_are_cascade_deleted_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Optional_many_to_one_dependents_are_orphaned_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_many_to_one_dependents_with_alternate_key_are_cascade_deleted_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Optional_many_to_one_dependents_with_alternate_key_are_orphaned_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Optional_one_to_one_relationships_are_one_to_one( + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_one_to_one_relationships_are_one_to_one( + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Save_required_one_to_one_changed_by_reference( + ChangeMechanism changeMechanism, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Sever_required_one_to_one( + ChangeMechanism changeMechanism, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_one_to_one_are_cascade_deleted_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_non_PK_one_to_one_are_cascade_deleted_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Optional_one_to_one_are_orphaned_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_one_to_one_are_cascade_detached_when_Added( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_non_PK_one_to_one_are_cascade_detached_when_Added( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Optional_one_to_one_with_AK_relationships_are_one_to_one( + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_one_to_one_with_AK_relationships_are_one_to_one( + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_one_to_one_with_alternate_key_are_cascade_deleted_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_non_PK_one_to_one_with_alternate_key_are_cascade_deleted_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Optional_one_to_one_with_alternate_key_are_orphaned_in_store( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_non_PK_one_to_one_with_alternate_key_are_cascade_detached_when_Added( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + public override void Required_one_to_one_with_alternate_key_are_cascade_detached_when_Added( + CascadeTiming? cascadeDeleteTiming, + CascadeTiming? deleteOrphansTiming) + { + // FK uniqueness not enforced in in-memory database + } + + protected override void ExecuteWithStrategyInTransaction( + Action testOperation, + Action nestedTestOperation1 = null, + Action nestedTestOperation2 = null, + Action nestedTestOperation3 = null) + { + base.ExecuteWithStrategyInTransaction(testOperation, nestedTestOperation1, nestedTestOperation2, nestedTestOperation3); + Fixture.Reseed(); + } + + protected override async Task ExecuteWithStrategyInTransactionAsync( + Func testOperation, + Func nestedTestOperation1 = null, + Func nestedTestOperation2 = null, + Func nestedTestOperation3 = null) + { + await base.ExecuteWithStrategyInTransactionAsync( + testOperation, nestedTestOperation1, nestedTestOperation2, nestedTestOperation3); + + Fixture.Reseed(); + } + + public abstract class GraphUpdatesInMemoryFixtureBase : GraphUpdatesFixtureBase + { + protected override ITestStoreFactory TestStoreFactory + => InMemoryTestStoreFactory.Instance; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder).ConfigureWarnings(w => w.Log(InMemoryEventId.TransactionIgnoredWarning)); + } +} diff --git a/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs b/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs index 8519cfe7955..a822b506d99 100644 --- a/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.CompilerServices; -using Microsoft.EntityFrameworkCore.Internal; // ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local // ReSharper disable ArrangeAccessorOwnerBody @@ -31,8 +31,11 @@ public virtual bool ForceClientNoAction public virtual bool NoStoreCascades => false; - public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).AddInterceptors(new UpdatingIdentityResolutionInterceptor()); + public virtual bool HasIdentityResolution + => false; + + public virtual bool AutoDetectChanges + => true; protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { @@ -481,62 +484,70 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity(); modelBuilder.Entity().HasData( - new SomethingCategory - { - Id = 1, - Name = "A" - }, - new SomethingCategory - { - Id = 2, - Name = "B" - }, - new SomethingCategory - { - Id = 3, - Name = "C" - }); + new SomethingCategory { Id = 1, Name = "A" }, + new SomethingCategory { Id = 2, Name = "B" }, + new SomethingCategory { Id = 3, Name = "C" }); modelBuilder.Entity().HasOne(s => s.SomethingCategory) .WithMany() .HasForeignKey(s => s.CategoryId) .OnDelete(DeleteBehavior.ClientSetNull); - modelBuilder.Entity(builder => - { - builder.Property("CategoryId").IsRequired(); + modelBuilder.Entity( + builder => + { + builder.Property("CategoryId").IsRequired(); - builder.HasKey(nameof(SomethingOfCategoryA.SomethingId), "CategoryId"); + builder.HasKey(nameof(SomethingOfCategoryA.SomethingId), "CategoryId"); - builder.HasOne(d => d.Something) - .WithOne(p => p.SomethingOfCategoryA) - .HasPrincipalKey(p => new {p.Id, p.CategoryId}) - .HasForeignKey(nameof(SomethingOfCategoryA.SomethingId), "CategoryId") - .OnDelete(DeleteBehavior.ClientSetNull); + builder.HasOne(d => d.Something) + .WithOne(p => p.SomethingOfCategoryA) + .HasPrincipalKey(p => new { p.Id, p.CategoryId }) + .HasForeignKey(nameof(SomethingOfCategoryA.SomethingId), "CategoryId") + .OnDelete(DeleteBehavior.ClientSetNull); - builder.HasOne() - .WithMany() - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.ClientSetNull); - }); + builder.HasOne() + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.ClientSetNull); + }); - modelBuilder.Entity(builder => - { - builder.Property(e => e.CategoryId).IsRequired(); + modelBuilder.Entity( + builder => + { + builder.Property(e => e.CategoryId).IsRequired(); + + builder.HasKey(e => new { e.SomethingId, e.CategoryId }); - builder.HasKey(e => new {e.SomethingId, e.CategoryId}); + builder.HasOne(d => d.Something) + .WithOne(p => p.SomethingOfCategoryB) + .HasPrincipalKey(p => new { p.Id, p.CategoryId }) + .HasForeignKey(socb => new { socb.SomethingId, socb.CategoryId }) + .OnDelete(DeleteBehavior.ClientSetNull); + + builder.HasOne(e => e.SomethingCategory) + .WithMany() + .HasForeignKey(e => e.CategoryId) + .OnDelete(DeleteBehavior.ClientSetNull); + }); - builder.HasOne(d => d.Something) - .WithOne(p => p.SomethingOfCategoryB) - .HasPrincipalKey(p => new {p.Id, p.CategoryId}) - .HasForeignKey(socb => new {socb.SomethingId, socb.CategoryId}) - .OnDelete(DeleteBehavior.ClientSetNull); + modelBuilder.Entity().HasMany(e => e.TurnipSwedes).WithOne(e => e.Swede).OnDelete(DeleteBehavior.Restrict); + modelBuilder.Entity().HasData(new Parsnip { Id = 1 }); + modelBuilder.Entity().HasData(new Carrot { Id = 1, ParsnipId = 1 }); + modelBuilder.Entity().HasData(new Turnip { Id = 1, CarrotsId = 1 }); + modelBuilder.Entity().HasData(new Swede { Id = 1, ParsnipId = 1 }); + modelBuilder.Entity().HasData( + new TurnipSwede + { + Id = 1, + SwedesId = 1, + TurnipId = 1 + }); - builder.HasOne(e => e.SomethingCategory) - .WithMany() - .HasForeignKey(e => e.CategoryId) - .OnDelete(DeleteBehavior.ClientSetNull); - }); + modelBuilder.Entity(); + modelBuilder.Entity(); + modelBuilder.Entity(); + modelBuilder.Entity(); } protected virtual object CreateFullGraph() @@ -3686,6 +3697,292 @@ public virtual Something Something } } + protected class Parsnip : NotifyingEntity + { + private int _id; + private Carrot _carrot; + private Swede _swede; + + public int Id + { + get => _id; + set => SetWithNotify(value, ref _id); + } + + public Carrot Carrot + { + get => _carrot; + set => SetWithNotify(value, ref _carrot); + } + + public Swede Swede + { + get => _swede; + set => SetWithNotify(value, ref _swede); + } + } + + protected class Carrot : NotifyingEntity + { + private int _id; + private int _parsnipId; + private Parsnip _parsnip; + private ICollection _turnips = new ObservableHashSet(); + + public int Id + { + get => _id; + set => SetWithNotify(value, ref _id); + } + + public int ParsnipId + { + get => _parsnipId; + set => SetWithNotify(value, ref _parsnipId); + } + + public Parsnip Parsnip + { + get => _parsnip; + set => SetWithNotify(value, ref _parsnip); + } + + public ICollection Turnips + { + get => _turnips; + set => SetWithNotify(value, ref _turnips); + } + } + + protected class Turnip : NotifyingEntity + { + private int _id; + private int _carrotsId; + private Carrot _carrot; + + public int Id + { + get => _id; + set => SetWithNotify(value, ref _id); + } + + public int CarrotsId + { + get => _carrotsId; + set => SetWithNotify(value, ref _carrotsId); + } + + public Carrot Carrot + { + get => _carrot; + set => SetWithNotify(value, ref _carrot); + } + } + + protected class Swede : NotifyingEntity + { + private int _id; + private int _parsnipId; + private Parsnip _parsnip; + private ICollection _turnipSwede = new ObservableHashSet(); + + public int Id + { + get => _id; + set => SetWithNotify(value, ref _id); + } + + public int ParsnipId + { + get => _parsnipId; + set => SetWithNotify(value, ref _parsnipId); + } + + public Parsnip Parsnip + { + get => _parsnip; + set => SetWithNotify(value, ref _parsnip); + } + + public ICollection TurnipSwedes + { + get => _turnipSwede; + set => SetWithNotify(value, ref _turnipSwede); + } + } + + protected class TurnipSwede : NotifyingEntity + { + private int _id; + private int _swedesId; + private Swede _swede; + private int _turnipId; + private Turnip _turnip; + + public int Id + { + get => _id; + set => SetWithNotify(value, ref _id); + } + + public int SwedesId + { + get => _swedesId; + set => SetWithNotify(value, ref _swedesId); + } + + public Swede Swede + { + get => _swede; + set => SetWithNotify(value, ref _swede); + } + + public int TurnipId + { + get => _turnipId; + set => SetWithNotify(value, ref _turnipId); + } + + public Turnip Turnip + { + get => _turnip; + set => SetWithNotify(value, ref _turnip); + } + } + + protected class Bayaz : NotifyingEntity + { + private int _bayazId; + private string _bayazName; + private ICollection _firstLaw = new ObservableHashSet(); + + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int BayazId + { + get => _bayazId; + set => SetWithNotify(value, ref _bayazId); + } + + public string BayazName + { + get => _bayazName; + set => SetWithNotify(value, ref _bayazName); + } + + public virtual ICollection FirstLaw + { + get => _firstLaw; + set => SetWithNotify(value, ref _firstLaw); + } + } + + protected class FirstLaw : NotifyingEntity + { + private int _firstLawId; + private string _firstLawName; + private int _bayazId; + private Bayaz _bayaz = null!; + private readonly ICollection _secondLaw = new ObservableHashSet(); + + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int FirstLawId + { + get => _firstLawId; + set => SetWithNotify(value, ref _firstLawId); + } + + public string FirstLawName + { + get => _firstLawName; + set => SetWithNotify(value, ref _firstLawName); + } + + public int BayazId + { + get => _bayazId; + set => SetWithNotify(value, ref _bayazId); + } + + public virtual Bayaz Bayaz + { + get => _bayaz; + set => SetWithNotify(value, ref _bayaz); + } + + public virtual ICollection SecondLaw + => _secondLaw; + } + + protected class SecondLaw : NotifyingEntity + { + private int _secondLawId; + private string _secondLawName; + private int _firstLawId; + private FirstLaw _firstLaw = null!; + private readonly ICollection _thirdLaw = new ObservableHashSet(); + + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int SecondLawId + { + get => _secondLawId; + set => SetWithNotify(value, ref _secondLawId); + } + + public string SecondLawName + { + get => _secondLawName; + set => SetWithNotify(value, ref _secondLawName); + } + + public int FirstLawId + { + get => _firstLawId; + set => SetWithNotify(value, ref _firstLawId); + } + + public virtual FirstLaw FirstLaw + { + get => _firstLaw; + set => SetWithNotify(value, ref _firstLaw); + } + + public virtual ICollection ThirdLaw + => _thirdLaw; + } + + protected class ThirdLaw : NotifyingEntity + { + private int _thirdLawId; + private string _thirdLawName; + private int _secondLawId; + private SecondLaw _secondLaw = null!; + + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int ThirdLawId + { + get => _thirdLawId; + set => SetWithNotify(value, ref _thirdLawId); + } + + public string ThirdLawName + { + get => _thirdLawName; + set => SetWithNotify(value, ref _thirdLawName); + } + + public int SecondLawId + { + get => _secondLawId; + set => SetWithNotify(value, ref _secondLawId); + } + + public virtual SecondLaw SecondLaw + { + get => _secondLaw; + set => SetWithNotify(value, ref _secondLaw); + } + } + protected class NotifyingEntity : INotifyPropertyChanging, INotifyPropertyChanged { protected void SetWithNotify(T value, ref T field, [CallerMemberName] string propertyName = "") diff --git a/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBaseMiscellaneous.cs b/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBaseMiscellaneous.cs index 621b7bde6d0..2959e19cbcb 100644 --- a/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBaseMiscellaneous.cs +++ b/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBaseMiscellaneous.cs @@ -115,7 +115,7 @@ public virtual async Task Saving_multiple_modified_entities_with_the_same_key_do } else { - Assert.Equal(2, context.ChangeTracker.Entries().Count()); + Assert.Equal(Fixture.HasIdentityResolution ? 2 : 3, context.ChangeTracker.Entries().Count()); Assert.Equal(EntityState.Deleted, context.Entry(college).State); Assert.Equal(EntityState.Unchanged, context.Entry(city).State); } @@ -1612,131 +1612,324 @@ public virtual void Can_add_multiple_dependents_when_multiple_possible_principal } }); - [ConditionalFact] - public void Can_attach_full_required_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + [ConditionalTheory] // Issue #30122 + [InlineData(false)] + [InlineData(true)] + public virtual Task Sever_relationship_that_will_later_be_deleted(bool async) + => ExecuteWithStrategyInTransactionAsync( + async context => { - var trackedRoot = LoadRequiredGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); + var swedes = context.Set() + .Include(x => x.Carrot) + .ThenInclude(x => x.Turnips) + .Include(x => x.Swede) + .ThenInclude(x => x.TurnipSwedes) + .Single(x => x.Id == 1); + + swedes.Carrot.Turnips.Clear(); + swedes.Swede.TurnipSwedes.Clear(); + + _ = async + ? await context.SaveChangesAsync() + : context.SaveChanges(); + + var entries = context.ChangeTracker.Entries(); + Assert.Equal(3, entries.Count()); + Assert.All(entries, e => Assert.Equal(EntityState.Unchanged, e.State)); + Assert.Contains(entries, e => e.Entity.GetType() == typeof(Carrot)); + Assert.Contains(entries, e => e.Entity.GetType() == typeof(Parsnip)); + Assert.Contains(entries, e => e.Entity.GetType() == typeof(Swede)); + }); - context.Attach(QueryRequiredGraph(context).AsNoTracking().Single(IsTheRoot)); + [ConditionalTheory] // Issue #30135 + [InlineData(false)] + [InlineData(true)] + public virtual Task Update_root_by_collection_replacement_of_inserted_first_level(bool async) + => ExecuteWithStrategyInTransactionAsync( + async context => + { + PopulateGraph(context); + var newRoot = BuildNewRoot(firstLevel1: true, secondLevel1: true, thirdLevel1: true, firstLevel2: true); - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); - Assert.Equal(0, context.SaveChanges()); + if (await UpdateRoot(context, newRoot, async)) + { + Assert.Equal( + Fixture.HasIdentityResolution || !Fixture.AutoDetectChanges ? 1 : 2, + context.Set().Count(x => x.BayazId == 1)); + } }); - [ConditionalFact] - public void Can_attach_full_optional_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + [ConditionalTheory] // Issue #30135 + [InlineData(false)] + [InlineData(true)] + public virtual Task Update_root_by_collection_replacement_of_deleted_first_level(bool async) + => ExecuteWithStrategyInTransactionAsync( + async context => { - var trackedRoot = LoadOptionalGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); + PopulateGraph(context); + var newRoot = BuildNewRoot(); - context.Attach(QueryOptionalGraph(context).AsNoTracking().Single(IsTheRoot)); + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); - - Assert.Equal(0, context.SaveChanges()); + if (await UpdateRoot(context, newRoot, async)) + { + Assert.Equal(Fixture.AutoDetectChanges ? 0 : 1, context.Set().Count(x => x.BayazId == 1)); + } }); - [ConditionalFact] - public void Can_attach_full_required_non_PK_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + [ConditionalTheory] // Issue #30135 + [InlineData(false)] + [InlineData(true)] + public virtual Task Update_root_by_collection_replacement_of_inserted_second_level(bool async) + => ExecuteWithStrategyInTransactionAsync( + async context => { - var trackedRoot = LoadRequiredNonPkGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); - - context.Attach(QueryRequiredNonPkGraph(context).AsNoTracking().Single(IsTheRoot)); + PopulateGraph(context); + var newRoot = BuildNewRoot(firstLevel1: true, secondLevel1: true, thirdLevel1: true, firstLevel2: true, secondLevel2: true); - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(1, context.Set().Count(x => x.FirstLawId == 11)); - Assert.Equal(0, context.SaveChanges()); + if (await UpdateRoot(context, newRoot, async)) + { + if (Fixture.AutoDetectChanges) + { + Assert.Equal(Fixture.HasIdentityResolution ? 1 : 2, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(Fixture.HasIdentityResolution ? 0 : 2, context.Set().Count(x => x.FirstLawId == 11)); + } + else + { + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(1, context.Set().Count(x => x.FirstLawId == 11)); + } + } }); - [ConditionalFact] - public void Can_attach_full_required_AK_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + [ConditionalTheory] // Issue #30135 + [InlineData(false)] + [InlineData(true)] + public virtual Task Update_root_by_collection_replacement_of_deleted_second_level( + bool async) + => ExecuteWithStrategyInTransactionAsync( + async context => { - var trackedRoot = LoadRequiredAkGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); + PopulateGraph(context); + var newRoot = BuildNewRoot(firstLevel1: true); - context.Attach(QueryRequiredAkGraph(context).AsNoTracking().Single(IsTheRoot)); + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(1, context.Set().Count(x => x.FirstLawId == 11)); - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); - - Assert.Equal(0, context.SaveChanges()); + if (await UpdateRoot(context, newRoot, async)) + { + Assert.Equal(Fixture.HasIdentityResolution ? 0 : 1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(Fixture.AutoDetectChanges ? 0 : 1, context.Set().Count(x => x.FirstLawId == 11)); + } }); - [ConditionalFact] - public void Can_attach_full_optional_AK_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + [ConditionalTheory] // Issue #30135 + [InlineData(false)] + [InlineData(true)] + public virtual Task Update_root_by_collection_replacement_of_inserted_first_level_level(bool async) + => ExecuteWithStrategyInTransactionAsync( + async context => { - var trackedRoot = LoadOptionalAkGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); + PopulateGraph(context); + var newRoot = BuildNewRoot( + firstLevel1: true, secondLevel1: true, thirdLevel1: true, firstLevel2: true, secondLevel2: true, thirdLevel2: true); - context.Attach(QueryOptionalAkGraph(context).AsNoTracking().Single(IsTheRoot)); + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(1, context.Set().Count(x => x.FirstLawId == 11)); + Assert.Equal(1, context.Set().Count(x => x.SecondLawId == 111)); - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); - - Assert.Equal(0, context.SaveChanges()); + if (await UpdateRoot(context, newRoot, async)) + { + if (Fixture.AutoDetectChanges) + { + Assert.Equal(Fixture.HasIdentityResolution ? 1 : 2, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(Fixture.HasIdentityResolution ? 0 : 2, context.Set().Count(x => x.FirstLawId == 11)); + Assert.Equal(Fixture.HasIdentityResolution ? 0 : 2, context.Set().Count(x => x.SecondLawId == 111)); + } + else + { + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(1, context.Set().Count(x => x.FirstLawId == 11)); + Assert.Equal(1, context.Set().Count(x => x.SecondLawId == 111)); + } + } }); - [ConditionalFact] - public void Can_attach_full_required_non_PK_AK_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + [ConditionalTheory] // Issue #30135 + [InlineData(false)] + [InlineData(true)] + public virtual Task Update_root_by_collection_replacement_of_deleted_third_level(bool async) + => ExecuteWithStrategyInTransactionAsync( + async context => { - var trackedRoot = LoadRequiredNonPkAkGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); + PopulateGraph(context); + var newRoot = BuildNewRoot(firstLevel1: true, secondLevel1: true); + + Assert.Equal(1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(1, context.Set().Count(x => x.FirstLawId == 11)); + Assert.Equal(1, context.Set().Count(x => x.SecondLawId == 111)); + + if (await UpdateRoot(context, newRoot, async)) + { + Assert.Equal(Fixture.HasIdentityResolution ? 0 : 1, context.Set().Count(x => x.BayazId == 1)); + Assert.Equal(Fixture.HasIdentityResolution ? 0 : 1, context.Set().Count(x => x.FirstLawId == 11)); + Assert.Equal(Fixture.AutoDetectChanges ? 0 : 1, context.Set().Count(x => x.SecondLawId == 111)); + } + }); + + protected async Task UpdateRoot(DbContext context, Bayaz newRoot, bool async) + { + var existingRoot = context.Set() + .Include(x => x.FirstLaw) + .ThenInclude(x => x.SecondLaw) + .ThenInclude(x => x.ThirdLaw) + .Single(x => x.BayazId == newRoot.BayazId); + + existingRoot.BayazName = newRoot.BayazName; + existingRoot.FirstLaw = newRoot.FirstLaw; + + if (Fixture.ForceClientNoAction) + { + Assert.Equal( + CoreStrings.RelationshipConceptualNullSensitive(nameof(Bayaz), nameof(FirstLaw), "{BayazId: 1}"), + (await Assert.ThrowsAsync( + async () => + { + _ = async + ? await context.SaveChangesAsync() + : context.SaveChanges(); + })).Message); + + return false; + } + + _ = async + ? await context.SaveChangesAsync() + : context.SaveChanges(); + + return true; + } - context.Attach(QueryRequiredNonPkAkGraph(context).AsNoTracking().Single(IsTheRoot)); + protected void PopulateGraph(DbContext context) + { + context.Add(new Bayaz { BayazId = 1, BayazName = "bayaz" }); - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); + context.Add( + new FirstLaw + { + FirstLawId = 11, + FirstLawName = "firstLaw1", + BayazId = 1 + }); - Assert.Equal(0, context.SaveChanges()); + context.Add( + new SecondLaw + { + SecondLawId = 111, + SecondLawName = "secondLaw1", + FirstLawId = 11 }); - [ConditionalFact] - public void Can_attach_full_required_one_to_many_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + context.Add( + new ThirdLaw { - var trackedRoot = LoadOptionalOneToManyGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); + ThirdLawId = 1111, + ThirdLawName = "thirdLaw1", + SecondLawId = 111 + }); + + context.SaveChanges(); + } - context.Attach(QueryOptionalOneToManyGraph(context).AsNoTracking().Single(IsTheRoot)); + protected Bayaz BuildNewRoot( + bool firstLevel1 = false, + bool firstLevel2 = false, + bool secondLevel1 = false, + bool secondLevel2 = false, + bool thirdLevel1 = false, + bool thirdLevel2 = false) + { + var root = new Bayaz { BayazId = 1, BayazName = "bayaz" }; - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); + if (firstLevel1) + { + root.FirstLaw.Add(AddFirstLevel(secondLevel1, secondLevel2, thirdLevel1, thirdLevel2)); + } - Assert.Equal(0, context.SaveChanges()); + if (firstLevel2) + { + root.FirstLaw.Add(new FirstLaw + { + FirstLawId = 12, + FirstLawName = "firstLaw2", + BayazId = 1 }); + } - [ConditionalFact] - public void Can_attach_full_required_composite_graph_of_duplicates() - => ExecuteWithStrategyInTransaction( - context => + return root; + } + + private FirstLaw AddFirstLevel(bool secondLevel1, bool secondLevel2, bool thirdLevel1, bool thirdLevel2) + { + var firstLevel = new FirstLaw + { + FirstLawId = 11, + FirstLawName = "firstLaw1", + BayazId = 1 + }; + + if (secondLevel1) + { + firstLevel.SecondLaw.Add(AddSecondLevel(thirdLevel1, thirdLevel2)); + } + + if (secondLevel2) + { + firstLevel.SecondLaw.Add(new SecondLaw { - var trackedRoot = LoadRequiredCompositeGraph(context); - var entries = context.ChangeTracker.Entries().ToList(); + SecondLawId = 112, + SecondLawName = "secondLaw2", + FirstLawId = 11 + }); + } - context.Attach(QueryRequiredCompositeGraph(context).AsNoTracking().Single(IsTheRoot)); + return firstLevel; + } - AssertEntries(entries, context.ChangeTracker.Entries().ToList()); - AssertNavigations(trackedRoot); + private static SecondLaw AddSecondLevel(bool thirdLevel1, bool thirdLevel2) + { + var secondLevel = new SecondLaw + { + SecondLawId = 111, + SecondLawName = "secondLaw1", + FirstLawId = 11 + }; + + if (thirdLevel1) + { + secondLevel.ThirdLaw.Add(new ThirdLaw + { + ThirdLawId = 1111, + ThirdLawName = "thirdLaw1", + SecondLawId = 111 + }); + } - Assert.Equal(0, context.SaveChanges()); + if (thirdLevel2) + { + secondLevel.ThirdLaw.Add(new ThirdLaw + { + ThirdLawId = 1112, + ThirdLawName = "thirdLaw2", + SecondLawId = 111 }); + } + + return secondLevel; + } } diff --git a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs index e6df4ea04e3..f357c6c4902 100644 --- a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs @@ -10,6 +10,34 @@ public GraphUpdatesSqlServerOwnedTest(SqlServerFixture fixture) { } + // No owned types + public override Task Update_root_by_collection_replacement_of_inserted_first_level(bool async) + => Task.CompletedTask; + + // No owned types + public override Task Update_root_by_collection_replacement_of_deleted_first_level(bool async) + => Task.CompletedTask; + + // No owned types + public override Task Update_root_by_collection_replacement_of_inserted_second_level(bool async) + => Task.CompletedTask; + + // No owned types + public override Task Update_root_by_collection_replacement_of_deleted_second_level(bool async) + => Task.CompletedTask; + + // No owned types + public override Task Update_root_by_collection_replacement_of_inserted_first_level_level(bool async) + => Task.CompletedTask; + + // No owned types + public override Task Update_root_by_collection_replacement_of_deleted_third_level(bool async) + => Task.CompletedTask; + + // No owned types + public override Task Sever_relationship_that_will_later_be_deleted(bool async) + => Task.CompletedTask; + // Owned dependents are always loaded public override void Required_one_to_one_are_cascade_deleted_in_store( CascadeTiming? cascadeDeleteTiming, diff --git a/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteSnapshotNotificationsTest.cs b/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteSnapshotNotificationsTest.cs index cbb5cf5a182..3b55c4a1055 100644 --- a/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteSnapshotNotificationsTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteSnapshotNotificationsTest.cs @@ -19,7 +19,7 @@ public class SqliteFixture : GraphUpdatesSqliteFixtureBase protected override string StoreName => "GraphUpdatesSnapshotTest"; - protected override bool AutoDetectChanges + public override bool AutoDetectChanges => true; protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) diff --git a/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs b/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs index 0ff1fa938e4..033e824f294 100644 --- a/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs +++ b/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs @@ -52,7 +52,7 @@ protected override ITestStoreFactory TestStoreFactory public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) => base.AddOptions(builder.ConfigureWarnings(b => b.Ignore(SqliteEventId.CompositeKeyWithValueGeneration))); - protected virtual bool AutoDetectChanges + public override bool AutoDetectChanges => false; public override PoolableDbContext CreateContext() diff --git a/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs index 7cdb2995b11..7158e31c007 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs @@ -438,4 +438,20 @@ DELETE FROM "WithSomeDatabaseGenerated2" } #endregion Same two operations with different entity types + + public class StoreValueGenerationWithoutReturningSqliteFixture : StoreValueGenerationSqliteFixture + { + protected override string StoreName + => "StoreValueGenerationWithoutReturningTest"; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + foreach (var entity in modelBuilder.Model.GetEntityTypes()) + { + modelBuilder.Entity(entity.Name).ToTable(b => b.UseSqlReturningClause(false)); + } + } + } } From 88f308eddbc6960e4ea7ec09f6cc27105dc7521c Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Thu, 9 Feb 2023 12:07:06 +0000 Subject: [PATCH 4/8] Fix merge and add quirk --- .../ChangeTracking/Internal/ChangeDetector.cs | 7 +++++-- .../GraphUpdates/GraphUpdatesTestBase.cs | 1 + .../StoreValueGenerationLegacySqliteTest.cs | 16 ---------------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs index 78898e3f024..8b2154e44ac 100644 --- a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs +++ b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs @@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal; /// public class ChangeDetector : IChangeDetector { + private static readonly bool QuirkEnabled30135 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue30135", out var enabled) && enabled; + private readonly IDiagnosticsLogger _logger; private readonly ILoggingOptions _loggingOptions; private bool _inCascadeDelete; @@ -113,7 +116,7 @@ public virtual void PropertyChanging(InternalEntityEntry entry, IPropertyBase pr /// public virtual void DetectChanges(IStateManager stateManager) { - if (_inCascadeDelete) + if (_inCascadeDelete && !QuirkEnabled30135) { return; } @@ -168,7 +171,7 @@ public virtual void DetectChanges(IStateManager stateManager) /// public virtual void DetectChanges(InternalEntityEntry entry) { - if (_inCascadeDelete) + if (_inCascadeDelete && !QuirkEnabled30135) { return; } diff --git a/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs b/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs index a822b506d99..947855ffa43 100644 --- a/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBase.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.CompilerServices; +using Microsoft.EntityFrameworkCore.Internal; // ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local // ReSharper disable ArrangeAccessorOwnerBody diff --git a/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs index 7158e31c007..7cdb2995b11 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Update/StoreValueGenerationLegacySqliteTest.cs @@ -438,20 +438,4 @@ DELETE FROM "WithSomeDatabaseGenerated2" } #endregion Same two operations with different entity types - - public class StoreValueGenerationWithoutReturningSqliteFixture : StoreValueGenerationSqliteFixture - { - protected override string StoreName - => "StoreValueGenerationWithoutReturningTest"; - - protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) - { - base.OnModelCreating(modelBuilder, context); - - foreach (var entity in modelBuilder.Model.GetEntityTypes()) - { - modelBuilder.Entity(entity.Name).ToTable(b => b.UseSqlReturningClause(false)); - } - } - } } From 28148254fdc0739d3e33b993c2bc1fb1dc25dd8b Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Tue, 7 Feb 2023 12:34:33 +0000 Subject: [PATCH 5/8] Use default schema for TPC tables (#30214) --- .../RelationalEntityTypeExtensions.cs | 4 +- .../Migrations/ModelSnapshotSqlServerTest.cs | 441 ++++++++++-------- 2 files changed, 249 insertions(+), 196 deletions(-) diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index 98c2acb0ef8..03121fb4e24 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -145,8 +145,8 @@ public static void SetTableName(this IMutableEntityType entityType, string? name return (string?)schemaAnnotation.Value ?? GetDefaultSchema(entityType); } - return entityType.BaseType != null - ? entityType.GetRootType().GetSchema() + return entityType.BaseType != null && entityType.BaseType.GetTableName() != null + ? entityType.BaseType.GetSchema() : GetDefaultSchema(entityType); } diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index 14c43c342a7..424e75dbd45 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -331,6 +331,7 @@ public virtual void Model_annotations_are_stored_in_snapshot() AddBoilerPlate( @" modelBuilder + .HasDefaultSchema("DefaultSchema") .HasAnnotation(""AnnotationName"", ""AnnotationValue"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); @@ -340,7 +341,7 @@ public virtual void Model_annotations_are_stored_in_snapshot() SqlServerModelBuilderExtensions.HasPerformanceLevelSql(modelBuilder, ""'S0'"");"), o => { - Assert.Equal(8, o.GetAnnotations().Count()); + Assert.Equal(9, o.GetAnnotations().Count()); Assert.Equal("AnnotationValue", o["AnnotationName"]); }); @@ -355,7 +356,9 @@ public virtual void Model_Fluent_APIs_are_properly_generated() }, AddBoilerPlate( @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseHiLo(modelBuilder, ""EntityFrameworkHiLoSequence""); @@ -372,7 +375,7 @@ public virtual void Model_Fluent_APIs_are_properly_generated() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); });"), o => { @@ -393,7 +396,9 @@ public virtual void Model_fluent_APIs_for_sequence_key_are_properly_generated() }, AddBoilerPlate( @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseKeySequences(modelBuilder, ""Sequence""); @@ -404,13 +409,13 @@ public virtual void Model_fluent_APIs_for_sequence_key_are_properly_generated() b.Property(""Id"") .ValueGeneratedOnAdd() .HasColumnType(""int"") - .HasDefaultValueSql(""NEXT VALUE FOR [EntityWithOnePropertySequence]""); + .HasDefaultValueSql(""NEXT VALUE FOR [DefaultSchema].[EntityWithOnePropertySequence]""); SqlServerPropertyBuilderExtensions.UseSequence(b.Property(""Id"")); b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); });"), o => { @@ -464,7 +469,7 @@ public virtual void Entities_are_stored_in_model_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -480,8 +485,9 @@ public virtual void Entities_are_stored_in_model_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); - });"), + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); + }); +"""), o => { Assert.Equal(2, o.GetEntityTypes().Count()); @@ -516,7 +522,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT() b.HasKey(""Id""); - b.ToTable(""AbstractBase""); + b.ToTable("AbstractBase", ""DefaultSchema""); b.UseTptMappingStrategy(); }); @@ -525,7 +531,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT() { b.HasBaseType(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+AbstractBase""); - b.ToTable(""BaseEntity""); + b.ToTable("BaseEntity", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+DerivedEntity"", b => @@ -548,7 +554,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT() });"), model => { - Assert.Equal(4, model.GetAnnotations().Count()); + Assert.Equal(5, model.GetAnnotations().Count()); Assert.Equal(3, model.GetEntityTypes().Count()); var abstractBase = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+AbstractBase"); @@ -557,6 +563,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT() var baseType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+BaseEntity"); Assert.Equal("BaseEntity", baseType.GetTableName()); + Assert.Equal("DefaultSchema", baseType.GetSchema()); var derived = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+DerivedEntity"); Assert.Equal("DerivedEntity", derived.GetTableName()); @@ -588,7 +595,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT_with_one_exclu b.HasKey(""Id""); - b.ToTable(""BaseEntity""); + b.ToTable(""BaseEntity"", ""DefaultSchema""); b.UseTptMappingStrategy(); }); @@ -616,7 +623,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT_with_one_exclu });"), o => { - Assert.Equal(4, o.GetAnnotations().Count()); + Assert.Equal(5, o.GetAnnotations().Count()); Assert.Equal( "DerivedEntity", @@ -640,7 +647,7 @@ public void Views_are_stored_in_the_model_snapshot() b.ToTable((string)null); - b.ToView(""EntityWithOneProperty"", (string)null); + b.ToView(""EntityWithOneProperty"", "DefaultSchema"); });"), o => Assert.Equal("EntityWithOneProperty", o.GetEntityTypes().Single().GetViewName())); @@ -691,7 +698,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPC() b.Property(""Id"") .ValueGeneratedOnAdd() .HasColumnType(""int"") - .HasDefaultValueSql(""NEXT VALUE FOR [AbstractBaseSequence]""); + .HasDefaultValueSql(""NEXT VALUE FOR [DefaultSchema].[AbstractBaseSequence]""); SqlServerPropertyBuilderExtensions.UseSequence(b.Property(""Id"")); @@ -706,7 +713,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPC() { b.HasBaseType(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+AbstractBase""); - b.ToTable(""BaseEntity""); + b.ToTable(""BaseEntity"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+DerivedEntity"", b => @@ -722,7 +729,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPC() });"), model => { - Assert.Equal(5, model.GetAnnotations().Count()); + Assert.Equal(6, model.GetAnnotations().Count()); Assert.Equal(3, model.GetEntityTypes().Count()); var abstractBase = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+AbstractBase"); @@ -751,13 +758,13 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b.Property("Shadow").HasColumnName("Shadow"); b.ToTable( - "Order", tb => + "Order", "DefaultSchema", tb => { tb.Property(e => e.Id).UseIdentityColumn(2, 3).HasAnnotation("fii", "arr"); tb.Property("Shadow"); }); b.SplitToTable( - "SplitOrder", sb => + "SplitOrder", "DefaultSchema", sb => { sb.Property("Shadow"); sb.HasTrigger("splitTrigger").HasAnnotation("oof", "rab"); @@ -771,12 +778,12 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() od.Property("BillingShadow"); od.ToTable( - "SplitOrder", tb => + "SplitOrder", "DefaultSchema", tb => { tb.Property("BillingShadow").HasColumnName("Shadow"); }); od.SplitToTable( - "BillingDetails", sb => + "BillingDetails", "DefaultSchema", sb => { sb.Property("BillingShadow").HasColumnName("Shadow"); }); @@ -789,12 +796,12 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() od.Property("ShippingShadow"); od.ToTable( - "Order", tb => + "Order", "DefaultSchema", tb => { tb.Property("ShippingShadow").HasColumnName("Shadow"); }); od.SplitToTable( - "ShippingDetails", sb => + "ShippingDetails", "DefaultSchema", sb => { sb.Property("ShippingShadow"); }); @@ -818,7 +825,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b.HasKey(""Id""); - b.ToTable(""Order"", null, t => + b.ToTable(""Order"", "DefaultSchema", t => { t.Property(""Id"") .HasAnnotation(""fii"", ""arr"") @@ -829,7 +836,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() t.Property(""Shadow""); }); - b.SplitToTable(""SplitOrder"", null, t => + b.SplitToTable(""SplitOrder"", "DefaultSchema", t => { t.HasTrigger(""splitTrigger"") .HasAnnotation(""oof"", ""rab""); @@ -858,13 +865,13 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b1.HasKey(""OrderId""); - b1.ToTable(""SplitOrder"", null, t => + b1.ToTable(""SplitOrder"", "DefaultSchema", t => { t.Property(""BillingShadow"") .HasColumnName(""Shadow""); }); - b1.SplitToTable(""BillingDetails"", null, t => + b1.SplitToTable(""BillingDetails"", "DefaultSchema", t => { t.Property(""BillingShadow"") .HasColumnName(""Shadow""); @@ -889,7 +896,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b2.HasKey(""OrderDetailsOrderId""); - b2.ToTable(""SplitOrder""); + b2.ToTable(""SplitOrder"", ""DefaultSchema""); b2.WithOwner() .HasForeignKey(""OrderDetailsOrderId""); @@ -908,13 +915,13 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b1.HasKey(""OrderId""); - b1.ToTable(""Order"", null, t => + b1.ToTable(""Order"", "DefaultSchema", t => { t.Property(""ShippingShadow"") .HasColumnName(""Shadow""); }); - b1.SplitToTable(""ShippingDetails"", null, t => + b1.SplitToTable(""ShippingDetails"", "DefaultSchema", t => { t.Property(""ShippingShadow""); }); @@ -938,7 +945,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b2.HasKey(""OrderDetailsOrderId""); - b2.ToTable(""ShippingDetails"", (string)null); + b2.ToTable(""ShippingDetails"", "DefaultSchema"); b2.WithOwner() .HasForeignKey(""OrderDetailsOrderId""); @@ -1081,12 +1088,12 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_views() b.ToTable((string)null); - b.ToView(""EntityWithOneProperty"", null, v => + b.ToView(""EntityWithOneProperty"", "DefaultSchema", v => { v.Property(""Shadow""); }); - b.SplitToView(""SplitView"", null, v => + b.SplitToView(""SplitView"", "DefaultSchema", v => { v.Property(""Shadow""); }); @@ -1106,13 +1113,13 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_views() b1.ToTable((string)null); - b1.ToView(""EntityWithOneProperty"", null, v => + b1.ToView(""EntityWithOneProperty"", "DefaultSchema", v => { v.Property(""AlternateId"") .HasColumnName(""SomeId""); }); - b1.SplitToView(""SplitView"", null, v => + b1.SplitToView(""SplitView"", "DefaultSchema", v => { v.Property(""AlternateId"") .HasColumnName(""SomeOtherId""); @@ -1141,7 +1148,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_views() Assert.Empty(relationalModel.Tables); Assert.Equal(2, relationalModel.Views.Count()); - var mainView = relationalModel.FindView(entityWithOneProperty.GetViewName(), entityWithOneProperty.GetSchema()); + var mainView = relationalModel.FindView(entityWithOneProperty.GetViewName(), "DefaultSchema"); var fragment = entityWithOneProperty.GetMappingFragments().Single(); var splitView = relationalModel.FindView(fragment.StoreObject.Name, fragment.StoreObject.Schema); @@ -1288,7 +1295,7 @@ public virtual void Sequence_is_stored_in_snapshot_as_fluent_api() .HasAnnotation(""foo"", ""bar"");"), model => { - Assert.Equal(5, model.GetAnnotations().Count()); + Assert.Equal(6, model.GetAnnotations().Count()); var sequence = model.GetSequences().Single(); Assert.Equal(2, sequence.StartValue); @@ -1364,7 +1371,7 @@ public virtual void CheckConstraint_is_stored_in_snapshot_as_fluent_api() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties"", t => + b.ToTable(""EntityWithTwoProperties"", "DefaultSchema", t => { t.HasCheckConstraint(""AlternateId"", ""AlternateId > Id"") .HasName(""CK_Customer_AlternateId"") @@ -1404,7 +1411,7 @@ public virtual void CheckConstraint_is_only_stored_in_snapshot_once_for_TPH() b.HasKey(""Id""); - b.ToTable(""BaseEntity""); + b.ToTable(""BaseEntity"", ""DefaultSchema""); b.HasDiscriminator(""Discriminator"").HasValue(""BaseEntity""); @@ -1453,7 +1460,7 @@ public virtual void Trigger_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty"", t => + b.ToTable(""EntityWithOneProperty"", "DefaultSchema", t => { t.HasTrigger(""SomeTrigger"") .HasDatabaseName(""SomeTrg"") @@ -1495,7 +1502,7 @@ public virtual void Triggers_and_ExcludeFromMigrations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty"", t => + b.ToTable(""EntityWithOneProperty"", "DefaultSchema", t => { t.ExcludeFromMigrations(); @@ -1534,12 +1541,14 @@ public virtual void Model_use_identity_columns() builder => builder.UseIdentityColumns(), AddBoilerPlate( @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);"), o => { - Assert.Equal(4, o.GetAnnotations().Count()); + Assert.Equal(5, o.GetAnnotations().Count()); Assert.Equal(SqlServerValueGenerationStrategy.IdentityColumn, o.GetValueGenerationStrategy()); Assert.Equal(1, o.GetIdentitySeed()); Assert.Equal(1, o.GetIdentityIncrement()); @@ -1551,12 +1560,14 @@ public virtual void Model_use_identity_columns_custom_seed() builder => builder.UseIdentityColumns(5), AddBoilerPlate( @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 5L);"), o => { - Assert.Equal(4, o.GetAnnotations().Count()); + Assert.Equal(5, o.GetAnnotations().Count()); Assert.Equal(SqlServerValueGenerationStrategy.IdentityColumn, o.GetValueGenerationStrategy()); Assert.Equal(5, o.GetIdentitySeed()); Assert.Equal(1, o.GetIdentityIncrement()); @@ -1568,12 +1579,14 @@ public virtual void Model_use_identity_columns_custom_increment() builder => builder.UseIdentityColumns(increment: 5), AddBoilerPlate( @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 5);"), o => { - Assert.Equal(4, o.GetAnnotations().Count()); + Assert.Equal(5, o.GetAnnotations().Count()); Assert.Equal(SqlServerValueGenerationStrategy.IdentityColumn, o.GetValueGenerationStrategy()); Assert.Equal(1, o.GetIdentitySeed()); Assert.Equal(5, o.GetIdentityIncrement()); @@ -1592,12 +1605,14 @@ public virtual void Model_use_identity_columns_custom_seed_increment() b.HasKey("Id"); - b.ToTable("Buildings"); + b.ToTable("Buildings", "DefaultSchema"); }); }, AddBoilerPlate( @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 9223372036854775807L, 5); @@ -1611,11 +1626,11 @@ public virtual void Model_use_identity_columns_custom_seed_increment() b.HasKey(""Id""); - b.ToTable(""Buildings"", (string)null); + b.ToTable(""Buildings"", "DefaultSchema"); });"), o => { - Assert.Equal(4, o.GetAnnotations().Count()); + Assert.Equal(5, o.GetAnnotations().Count()); Assert.Equal(SqlServerValueGenerationStrategy.IdentityColumn, o.GetValueGenerationStrategy()); Assert.Equal(long.MaxValue, o.GetIdentitySeed()); Assert.Equal(5, o.GetIdentityIncrement()); @@ -1651,7 +1666,7 @@ public virtual void EntityType_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); b.HasAnnotation(""AnnotationName"", ""AnnotationValue""); });"), @@ -1684,7 +1699,7 @@ public virtual void EntityType_Fluent_APIs_are_properly_generated() SqlServerKeyBuilderExtensions.IsClustered(b.HasKey(""Id""), false); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); SqlServerEntityTypeBuilderExtensions.IsMemoryOptimized(b); });"), @@ -1715,7 +1730,7 @@ public virtual void BaseType_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""BaseEntity""); + b.ToTable(""BaseEntity"", ""DefaultSchema""); b.HasDiscriminator(""Discriminator"").HasValue(""BaseEntity""); @@ -1784,7 +1799,7 @@ public virtual void Discriminator_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""BaseEntity""); + b.ToTable(""BaseEntity"", ""DefaultSchema""); b.HasDiscriminator(""Discriminator"").IsComplete(true).HasValue(""BaseEntity""); @@ -1859,7 +1874,7 @@ public virtual void Converted_discriminator_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""BaseEntityWithStructDiscriminator""); + b.ToTable(""BaseEntityWithStructDiscriminator"", ""DefaultSchema""); b.HasDiscriminator(""Discriminator"").IsComplete(true).HasValue(""Base""); @@ -1928,7 +1943,7 @@ public virtual void Properties_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -1962,7 +1977,7 @@ public virtual void Primary_key_is_stored_in_snapshot() b.HasKey(""Id"", ""AlternateId""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -1986,7 +2001,7 @@ public void HasNoKey_is_handled() b.Property(""Id"") .HasColumnType(""int""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); });"), o => { @@ -2023,7 +2038,7 @@ public virtual void Alternate_keys_are_stored_in_snapshot() b.HasAlternateKey(""Id"", ""AlternateId""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -2060,7 +2075,7 @@ public virtual void Indexes_are_stored_in_snapshot() b.HasIndex(""AlternateId""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -2095,7 +2110,7 @@ public virtual void Indexes_are_stored_in_snapshot_including_composite_index() b.HasIndex(""Id"", ""AlternateId""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -2130,7 +2145,7 @@ public virtual void Foreign_keys_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -2149,7 +2164,7 @@ public virtual void Foreign_keys_are_stored_in_snapshot() b.HasIndex(""AlternateId"") .IsUnique(); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -2349,7 +2364,7 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i b.HasIndex(""RightsId""); - b.ToTable(""MyJoinTable"", (string)null); + b.ToTable(""MyJoinTable"", "DefaultSchema"); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+ManyToManyLeft"", b => @@ -2365,7 +2380,7 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i b.HasKey(""Id""); - b.ToTable(""ManyToManyLeft""); + b.ToTable(""ManyToManyLeft"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+ManyToManyRight"", b => @@ -2381,7 +2396,7 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i b.HasKey(""Id""); - b.ToTable(""ManyToManyRight""); + b.ToTable(""ManyToManyRight"", ""DefaultSchema""); }); modelBuilder.Entity(""ManyToManyLeftManyToManyRight"", b => @@ -2490,7 +2505,7 @@ public virtual void TableName_preserved_when_generic() b.HasKey(""Id""); - b.ToTable(""EntityWithGenericKey""); + b.ToTable(""EntityWithGenericKey"", ""DefaultSchema""); });", usingSystem: true), model => { @@ -2540,7 +2555,7 @@ public virtual void Shared_columns_are_stored_in_the_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithProperties"", (string)null); + b.ToTable(""EntityWithProperties"", "DefaultSchema"); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -2555,7 +2570,7 @@ public virtual void Shared_columns_are_stored_in_the_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithProperties"", (string)null); + b.ToTable(""EntityWithProperties"", "DefaultSchema"); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -2603,7 +2618,7 @@ public virtual void PrimaryKey_name_preserved_when_generic() b.HasKey(""Id""); - b.ToTable(""EntityWithGenericKey""); + b.ToTable(""EntityWithGenericKey"", ""DefaultSchema""); });", usingSystem: true), model => { @@ -2648,7 +2663,7 @@ public virtual void AlternateKey_name_preserved_when_generic() b.HasAlternateKey(""Property""); - b.ToTable(""EntityWithGenericProperty""); + b.ToTable(""EntityWithGenericProperty"", ""DefaultSchema""); });", usingSystem: true), model => { @@ -2683,7 +2698,7 @@ public virtual void Discriminator_of_enum() b.HasKey(""Id""); - b.ToTable(""EntityWithEnumType""); + b.ToTable(""EntityWithEnumType"", ""DefaultSchema""); b.HasDiscriminator(""Day""); });"), @@ -2715,7 +2730,7 @@ public virtual void Discriminator_of_enum_to_string() b.HasKey(""Id""); - b.ToTable(""EntityWithEnumType""); + b.ToTable(""EntityWithEnumType"", ""DefaultSchema""); b.HasDiscriminator(""Day""); });"), @@ -2763,7 +2778,7 @@ public virtual void Temporal_table_information_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); b.ToTable(tb => tb.IsTemporal(ttb => { @@ -2824,11 +2839,11 @@ public virtual void Temporal_table_information_is_stored_in_snapshot_minimal_set b.HasKey(""Id""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); b.ToTable(tb => tb.IsTemporal(ttb => { - ttb.UseHistoryTable(""EntityWithStringPropertyHistory""); + ttb.UseHistoryTable(""EntityWithStringPropertyHistory"", ""DefaultSchema""); ttb .HasPeriodStart(""PeriodStart"") .HasColumnName(""PeriodStart""); @@ -2909,7 +2924,7 @@ public virtual void Owned_types_are_stored_in_snapshot() b.HasKey(""Id"") .HasName(""PK_Custom""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); b.HasData( new @@ -2925,7 +2940,7 @@ public virtual void Owned_types_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringKey""); + b.ToTable(""EntityWithStringKey"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => @@ -2952,7 +2967,7 @@ public virtual void Owned_types_are_stored_in_snapshot() SqlServerIndexBuilderExtensions.IncludeProperties(b1.HasIndex(""Id""), new[] { ""AlternateId"" }); - b1.ToTable(""EntityWithOneProperty""); + b1.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); b1.WithOwner(""EntityWithOneProperty"") .HasForeignKey(""AlternateId"") @@ -3005,7 +3020,7 @@ public virtual void Owned_types_are_stored_in_snapshot() b1.HasIndex(""EntityWithStringKeyId""); - b1.ToTable(""EntityWithStringProperty""); + b1.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); b1.HasOne(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", ""EntityWithOneProperty"") .WithOne() @@ -3105,7 +3120,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b.HasData( new EntityWithOneProperty { Id = 1 }); - b.ToTable("EntityWithOneProperty", e => e.ExcludeFromMigrations()); + b.ToTable("EntityWithOneProperty", "DefaultSchema", e => e.ExcludeFromMigrations()); }); builder.Entity( @@ -3137,7 +3152,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b.HasKey(""Id"") .HasName(""PK_Custom""); - b.ToTable(""EntityWithOneProperty"", null, t => + b.ToTable(""EntityWithOneProperty"", "DefaultSchema", t => { t.ExcludeFromMigrations(); }); @@ -3156,7 +3171,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b.HasKey(""Id""); - b.ToTable(""EntityWithStringKey"", null, t => + b.ToTable(""EntityWithStringKey"", "DefaultSchema", t => { t.ExcludeFromMigrations(); }); @@ -3184,7 +3199,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b1.HasIndex(""Id""); - b1.ToTable(""EntityWithOneProperty""); + b1.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); b1.WithOwner(""EntityWithOneProperty"") .HasForeignKey(""AlternateId"") @@ -3237,7 +3252,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b1.HasIndex(""EntityWithStringKeyId""); - b1.ToTable(""EntityWithStringProperty"", null, t => + b1.ToTable(""EntityWithStringProperty"", "DefaultSchema", t => { t.ExcludeFromMigrations(); }); @@ -3333,7 +3348,7 @@ public virtual void Shared_owned_types_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""Order""); + b.ToTable(""Order"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Order"", b => @@ -3345,7 +3360,7 @@ public virtual void Shared_owned_types_are_stored_in_snapshot() b1.HasKey(""OrderId""); - b1.ToTable(""Order""); + b1.ToTable(""Order"", ""DefaultSchema""); b1.WithOwner() .HasForeignKey(""OrderId""); @@ -3360,7 +3375,7 @@ public virtual void Shared_owned_types_are_stored_in_snapshot() b2.HasKey(""OrderDetailsOrderId""); - b2.ToTable(""Order""); + b2.ToTable(""Order"", ""DefaultSchema""); b2.WithOwner() .HasForeignKey(""OrderDetailsOrderId""); @@ -3376,7 +3391,7 @@ public virtual void Shared_owned_types_are_stored_in_snapshot() b1.HasKey(""OrderId""); - b1.ToTable(""Order""); + b1.ToTable(""Order"", ""DefaultSchema""); b1.WithOwner() .HasForeignKey(""OrderId""); @@ -3391,7 +3406,7 @@ public virtual void Shared_owned_types_are_stored_in_snapshot() b2.HasKey(""OrderDetailsOrderId""); - b2.ToTable(""Order""); + b2.ToTable(""Order"", ""DefaultSchema""); b2.WithOwner() .HasForeignKey(""OrderDetailsOrderId""); @@ -3407,7 +3422,7 @@ public virtual void Shared_owned_types_are_stored_in_snapshot() b1.HasKey(""OrderId""); - b1.ToTable(""Order""); + b1.ToTable(""Order"", ""DefaultSchema""); b1.WithOwner() .HasForeignKey(""OrderId""); @@ -3422,7 +3437,7 @@ public virtual void Shared_owned_types_are_stored_in_snapshot() b2.HasKey(""OrderInfoOrderId""); - b2.ToTable(""Order""); + b2.ToTable(""Order"", ""DefaultSchema""); b2.WithOwner() .HasForeignKey(""OrderInfoOrderId""); @@ -3491,7 +3506,9 @@ partial class Snapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -3505,7 +3522,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey(""Id""); - b.ToTable(""TestOwner""); + b.ToTable(""TestOwner"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+TestOwner"", b => @@ -3525,7 +3542,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.ToTable((string)null); - b1.ToView(""OwnedView"", (string)null); + b1.ToView(""OwnedView"", "DefaultSchema"); b1.WithOwner() .HasForeignKey(""TestOwnerId""); @@ -3575,7 +3592,9 @@ partial class Snapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -3589,7 +3608,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey(""Id""); - b.ToTable(""TestOwner""); + b.ToTable(""TestOwner"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+TestOwner"", b => @@ -3610,7 +3629,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey(""TestOwnerId"", ""Id""); - b1.ToTable(""TestOwnee"", t => + b1.ToTable(""TestOwnee"", "DefaultSchema", t => { t.HasCheckConstraint(""CK_TestOwnee_TestEnum_Enum_Constraint"", ""[TestEnum] IN (0, 1, 2)""); }); @@ -3676,7 +3695,7 @@ public virtual void Owned_types_mapped_to_json_are_stored_in_snapshot() b.HasKey(""Id"") .HasName(""PK_Custom""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => @@ -3692,7 +3711,7 @@ public virtual void Owned_types_mapped_to_json_are_stored_in_snapshot() b1.HasKey(""EntityWithOnePropertyId""); - b1.ToTable(""EntityWithOneProperty""); + b1.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); b1.ToJson(""EntityWithTwoProperties""); @@ -3706,7 +3725,7 @@ public virtual void Owned_types_mapped_to_json_are_stored_in_snapshot() b2.HasKey(""EntityWithTwoPropertiesEntityWithOnePropertyId""); - b2.ToTable(""EntityWithOneProperty""); + b2.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); b2.WithOwner() .HasForeignKey(""EntityWithTwoPropertiesEntityWithOnePropertyId""); @@ -3725,7 +3744,7 @@ public virtual void Owned_types_mapped_to_json_are_stored_in_snapshot() b3.HasKey(""EntityWithStringKeyEntityWithTwoPropertiesEntityWithOnePropertyId"", ""Id""); - b3.ToTable(""EntityWithOneProperty""); + b3.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); b3.HasAnnotation(""Relational:JsonPropertyName"", ""JsonProps""); @@ -3852,7 +3871,7 @@ public virtual void Property_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); });"), o => Assert.Equal("AnnotationValue", o.GetEntityTypes().First().FindProperty("Id")["AnnotationName"]) ); @@ -3878,7 +3897,7 @@ public virtual void Custom_value_generator_is_ignored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); });"), o => Assert.Null(o.GetEntityTypes().First().FindProperty("Id")[CoreAnnotationNames.ValueGeneratorFactory]) ); @@ -3904,7 +3923,7 @@ public virtual void Property_isNullable_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); });"), o => Assert.False(o.GetEntityTypes().First().FindProperty("Name").IsNullable)); @@ -3934,7 +3953,7 @@ public virtual void Property_ValueGenerated_value_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });", usingSystem: true), o => Assert.Equal(ValueGenerated.OnAdd, o.GetEntityTypes().First().FindProperty("AlternateId").ValueGenerated)); @@ -3965,7 +3984,7 @@ public virtual void Property_ValueGenerated_non_identity() b.HasKey(""Id""); - b.ToTable(""EntityWithEnumType""); + b.ToTable(""EntityWithEnumType"", ""DefaultSchema""); });"), model => { @@ -3998,10 +4017,37 @@ public virtual void Property_maxLength_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); });"), o => Assert.Equal(100, o.GetEntityTypes().First().FindProperty("Name").GetMaxLength())); + + [ConditionalFact] + public virtual void Property_maximum_maxLength_is_stored_in_snapshot() + => Test( + builder => builder.Entity().Property("Name").HasMaxLength(-1), + AddBoilerPlate( + GetHeading() + +""" + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .HasMaxLength(-1) + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("EntityWithStringProperty", "DefaultSchema"); + }); +"""), + o => Assert.Equal(-1, o.GetEntityTypes().First().FindProperty("Name").GetMaxLength())); + [ConditionalFact] public virtual void Property_unicodeness_is_stored_in_snapshot() => Test( @@ -4023,7 +4069,7 @@ public virtual void Property_unicodeness_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); });"), o => Assert.False(o.GetEntityTypes().First().FindProperty("Name").IsUnicode())); @@ -4049,7 +4095,7 @@ public virtual void Property_fixedlengthness_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); });"), o => Assert.True(o.GetEntityTypes().First().FindProperty("Name").IsFixedLength())); @@ -4077,7 +4123,7 @@ public virtual void Property_precision_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithDecimalProperty""); + b.ToTable(""EntityWithDecimalProperty"", ""DefaultSchema""); });"), o => { @@ -4110,7 +4156,7 @@ public virtual void Property_precision_and_scale_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithDecimalProperty""); + b.ToTable(""EntityWithDecimalProperty"", ""DefaultSchema""); });"), o => { @@ -4149,7 +4195,7 @@ public virtual void Many_facets_chained_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); });"), o => { @@ -4184,7 +4230,7 @@ public virtual void Property_concurrencyToken_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.True(o.GetEntityTypes().First().FindProperty("AlternateId").IsConcurrencyToken)); @@ -4213,7 +4259,7 @@ public virtual void Property_column_name_annotation_is_stored_in_snapshot_as_flu b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal("CName", o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:ColumnName"])); @@ -4242,7 +4288,7 @@ public virtual void Property_column_name_on_specific_table_is_stored_in_snapshot b.HasKey(""Id""); - b.ToTable(""BaseEntity""); + b.ToTable(""BaseEntity"", ""DefaultSchema""); b.HasDiscriminator(""Discriminator"").HasValue(""BaseEntity""); @@ -4266,7 +4312,7 @@ public virtual void Property_column_name_on_specific_table_is_stored_in_snapshot b.Property(""Name"") .HasColumnType(""nvarchar(max)""); - b.ToTable(""BaseEntity"", t => + b.ToTable(""BaseEntity"", "DefaultSchema", t => { t.Property(""Name"") .HasColumnName(""DuplicateDerivedEntity_Name""); @@ -4288,7 +4334,7 @@ public virtual void Property_column_name_on_specific_table_is_stored_in_snapshot Assert.Equal( "DuplicateDerivedEntity_Name", t.FindProperty(nameof(DuplicateDerivedEntity.Name)) - .GetColumnName(StoreObjectIdentifier.Table(nameof(BaseEntity)))); + .GetColumnName(StoreObjectIdentifier.Table(nameof(BaseEntity), "DefaultSchema"))); } ); }); @@ -4317,7 +4363,7 @@ public virtual void Property_column_type_annotation_is_stored_in_snapshot_as_flu b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal("CType", o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:ColumnType"])); @@ -4347,7 +4393,7 @@ public virtual void Property_default_value_annotation_is_stored_in_snapshot_as_f b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal(1, o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:DefaultValue"])); @@ -4377,7 +4423,7 @@ public virtual void Property_default_value_annotation_is_stored_in_snapshot_as_f b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });", usingSystem: true), o => Assert.Equal(DBNull.Value, o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:DefaultValue"])); @@ -4408,7 +4454,7 @@ public virtual void Property_default_value_sql_annotation_is_stored_in_snapshot_ b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal(string.Empty, o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:DefaultValueSql"])); @@ -4438,7 +4484,7 @@ public virtual void Property_default_value_sql_annotation_is_stored_in_snapshot_ b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal("SQL", o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:DefaultValueSql"])); @@ -4468,7 +4514,7 @@ public virtual void Property_computed_column_sql_annotation_is_stored_in_snapsho b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal("SQL", o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:ComputedColumnSql"])); @@ -4498,7 +4544,7 @@ public virtual void Property_computed_column_sql_stored_annotation_is_stored_in_ b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -4532,7 +4578,7 @@ public virtual void Property_computed_column_sql_annotation_is_stored_in_snapsho b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal(string.Empty, o.GetEntityTypes().First().FindProperty("AlternateId")["Relational:ComputedColumnSql"])); @@ -4558,7 +4604,7 @@ public virtual void Property_default_value_of_enum_type_is_stored_in_snapshot_wi b.HasKey(""Id""); - b.ToTable(""EntityWithEnumType""); + b.ToTable(""EntityWithEnumType"", ""DefaultSchema""); });"), o => Assert.Equal(3L, o.GetEntityTypes().First().FindProperty("Day")["Relational:DefaultValue"])); @@ -4592,7 +4638,7 @@ public virtual void Property_enum_type_is_stored_in_snapshot_with_custom_convers b.HasKey(""Id""); - b.ToTable(""EntityWithEnumType""); + b.ToTable(""EntityWithEnumType"", ""DefaultSchema""); b.HasData( new @@ -4629,7 +4675,7 @@ public virtual void Property_of_nullable_enum() b.HasKey(""Id""); - b.ToTable(""EntityWithNullableEnumType""); + b.ToTable(""EntityWithNullableEnumType"", ""DefaultSchema""); });"), o => Assert.True(o.GetEntityTypes().First().FindProperty("Day").IsNullable)); @@ -4654,7 +4700,7 @@ public virtual void Property_of_enum_to_nullable() b.HasKey(""Id""); - b.ToTable(""EntityWithEnumType""); + b.ToTable(""EntityWithEnumType"", ""DefaultSchema""); });", usingSystem: true), o => Assert.False(o.GetEntityTypes().First().FindProperty("Day").IsNullable)); @@ -4678,7 +4724,7 @@ public virtual void Property_of_nullable_enum_to_string() b.HasKey(""Id""); - b.ToTable(""EntityWithNullableEnumType""); + b.ToTable(""EntityWithNullableEnumType"", ""DefaultSchema""); });"), o => Assert.True(o.GetEntityTypes().First().FindProperty("Day").IsNullable)); @@ -4709,7 +4755,7 @@ public virtual void Property_multiple_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -4738,7 +4784,7 @@ public virtual void Property_without_column_type() b.HasKey("Id"); - b.ToTable("Buildings"); + b.ToTable("Buildings", "DefaultSchema"); }); }, AddBoilerPlate( @@ -4754,7 +4800,7 @@ public virtual void Property_without_column_type() b.HasKey(""Id""); - b.ToTable(""Buildings"", (string)null); + b.ToTable(""Buildings"", "DefaultSchema"); });"), o => { @@ -4774,7 +4820,7 @@ public virtual void Property_with_identity_column() b.HasKey("Id"); - b.ToTable("Buildings"); + b.ToTable("Buildings", "DefaultSchema"); }); }, AddBoilerPlate( @@ -4790,7 +4836,7 @@ public virtual void Property_with_identity_column() b.HasKey(""Id""); - b.ToTable(""Buildings"", (string)null); + b.ToTable(""Buildings"", "DefaultSchema"); });"), o => { @@ -4812,7 +4858,7 @@ public virtual void Property_with_identity_column_custom_seed() b.HasKey("Id"); - b.ToTable("Buildings"); + b.ToTable("Buildings", "DefaultSchema"); }); }, AddBoilerPlate( @@ -4828,7 +4874,7 @@ public virtual void Property_with_identity_column_custom_seed() b.HasKey(""Id""); - b.ToTable(""Buildings"", (string)null); + b.ToTable(""Buildings"", "DefaultSchema"); });"), o => { @@ -4850,7 +4896,7 @@ public virtual void Property_with_identity_column_custom_increment() b.HasKey("Id"); - b.ToTable("Buildings"); + b.ToTable("Buildings", "DefaultSchema"); }); }, AddBoilerPlate( @@ -4866,7 +4912,7 @@ public virtual void Property_with_identity_column_custom_increment() b.HasKey(""Id""); - b.ToTable(""Buildings"", (string)null); + b.ToTable(""Buildings"", "DefaultSchema"); });"), o => { @@ -4888,7 +4934,7 @@ public virtual void Property_with_identity_column_custom_seed_increment() b.HasKey("Id"); - b.ToTable("Buildings"); + b.ToTable("Buildings", "DefaultSchema"); }); }, AddBoilerPlate( @@ -4904,7 +4950,7 @@ public virtual void Property_with_identity_column_custom_seed_increment() b.HasKey(""Id""); - b.ToTable(""Buildings"", (string)null); + b.ToTable(""Buildings"", "DefaultSchema"); });"), o => { @@ -4939,7 +4985,7 @@ public virtual void Property_column_order_annotation_is_stored_in_snapshot_as_fl b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal(1, o.GetEntityTypes().First().FindProperty("AlternateId").GetColumnOrder())); @@ -4949,7 +4995,9 @@ public virtual void SQLServer_model_legacy_identity_seed_int_annotation() builder => builder.HasAnnotation(SqlServerAnnotationNames.IdentitySeed, 8), AddBoilerPlate( @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 8L);"), o => Assert.Equal(8L, o.GetIdentitySeed())); @@ -4979,7 +5027,7 @@ public virtual void SQLServer_property_legacy_identity_seed_int_annotation() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal(8L, o.GetEntityTypes().First().FindProperty("Id").GetIdentitySeed())); @@ -5015,7 +5063,7 @@ public virtual void Key_annotations_are_stored_in_snapshot() b.HasAlternateKey(""AlternateId"") .HasAnnotation(""AnnotationName"", ""AnnotationValue""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal( "AnnotationValue", o.GetEntityTypes().First().GetKeys().Where(k => !k.IsPrimaryKey()).First()["AnnotationName"])); @@ -5043,7 +5091,7 @@ public virtual void Key_Fluent_APIs_are_properly_generated() SqlServerKeyBuilderExtensions.IsClustered(b.HasKey(""Id"")); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); });"), o => Assert.True(o.GetEntityTypes().First().GetKeys().Single(k => k.IsPrimaryKey()).IsClustered())); @@ -5074,7 +5122,7 @@ public virtual void Key_name_annotation_is_stored_in_snapshot_as_fluent_api() b.HasAlternateKey(""AlternateId"") .HasName(""KeyName""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal( "KeyName", o.GetEntityTypes().First().GetKeys().Where(k => !k.IsPrimaryKey()).First()["Relational:Name"])); @@ -5108,7 +5156,7 @@ public virtual void Key_multiple_annotations_are_stored_in_snapshot() .HasName(""IndexName"") .HasAnnotation(""AnnotationName"", ""AnnotationValue""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -5150,7 +5198,7 @@ public virtual void Index_annotations_are_stored_in_snapshot() b.HasIndex(""AlternateId"") .HasAnnotation(""AnnotationName"", ""AnnotationValue""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal("AnnotationValue", o.GetEntityTypes().First().GetIndexes().First()["AnnotationName"])); @@ -5182,7 +5230,7 @@ public virtual void Index_Fluent_APIs_are_properly_generated() SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex(""AlternateId"")); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.True(o.GetEntityTypes().Single().GetIndexes().Single().IsClustered())); @@ -5213,7 +5261,7 @@ public virtual void Index_IsUnique_is_stored_in_snapshot() b.HasIndex(""AlternateId"") .IsUnique(); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.True(o.GetEntityTypes().First().GetIndexes().First().IsUnique)); @@ -5301,7 +5349,7 @@ public virtual void Index_IsDescending_is_stored_in_snapshot() b.HasIndex(new[] { ""X"", ""Y"", ""Z"" }, ""IX_unspecified""); - b.ToTable(""EntityWithThreeProperties""); + b.ToTable(""EntityWithThreeProperties"", ""DefaultSchema""); });"), o => { @@ -5353,7 +5401,7 @@ public virtual void Index_database_name_annotation_is_stored_in_snapshot_as_flue b.HasIndex(""AlternateId"") .HasDatabaseName(""IndexName""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -5390,7 +5438,7 @@ public virtual void Index_filter_is_stored_in_snapshot() b.HasIndex(""AlternateId"") .HasFilter(""AlternateId <> 0""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => Assert.Equal( "AlternateId <> 0", @@ -5424,7 +5472,7 @@ public virtual void Index_multiple_annotations_are_stored_in_snapshot() b.HasIndex(new[] { ""AlternateId"" }, ""IndexName"") .HasAnnotation(""AnnotationName"", ""AnnotationValue""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); });"), o => { @@ -5467,7 +5515,7 @@ public virtual void Index_with_default_constraint_name_exceeding_max() b.HasIndex(""SomePropertyWithAnExceedinglyLongIdentifierThatCausesTheDefaultIndexNameToExceedTheMaximumIdentifierLimit""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); });"), model => Assert.Equal(128, model.GetEntityTypes().First().GetIndexes().First().GetDatabaseName().Length)); @@ -5496,7 +5544,7 @@ public virtual void IndexAttribute_causes_column_to_have_key_or_index_column_len b.HasIndex(""FirstName"", ""LastName""); - b.ToTable(""EntityWithIndexAttribute""); + b.ToTable(""EntityWithIndexAttribute"", ""DefaultSchema""); });"), model => Assert.Collection( @@ -5538,7 +5586,7 @@ public virtual void IndexAttribute_name_is_stored_in_snapshot() b.HasIndex(new[] { ""FirstName"", ""LastName"" }, ""NamedIndex""); - b.ToTable(""EntityWithNamedIndexAttribute""); + b.ToTable(""EntityWithNamedIndexAttribute"", ""DefaultSchema""); });"), model => { @@ -5586,7 +5634,7 @@ public virtual void IndexAttribute_IsUnique_is_stored_in_snapshot() .IsUnique() .HasFilter(""[FirstName] IS NOT NULL AND [LastName] IS NOT NULL""); - b.ToTable(""EntityWithUniqueIndexAttribute""); + b.ToTable(""EntityWithUniqueIndexAttribute"", ""DefaultSchema""); });"), model => { @@ -5635,7 +5683,7 @@ public virtual void IndexAttribute_IncludeProperties_generated_without_fluent_ap SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex(""Id""), new[] { ""Name"" }); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); });"), model => { @@ -5671,7 +5719,7 @@ public virtual void ForeignKey_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -5690,7 +5738,7 @@ public virtual void ForeignKey_annotations_are_stored_in_snapshot() b.HasIndex(""AlternateId"") .IsUnique(); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -5734,7 +5782,7 @@ public virtual void ForeignKey_isRequired_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringKey""); + b.ToTable(""EntityWithStringKey"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty"", b => @@ -5754,7 +5802,7 @@ public virtual void ForeignKey_isRequired_is_stored_in_snapshot() b.HasIndex(""Name"") .IsUnique(); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty"", b => @@ -5787,7 +5835,7 @@ public virtual void ForeignKey_isUnique_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithStringKey""); + b.ToTable(""EntityWithStringKey"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty"", b => @@ -5805,7 +5853,7 @@ public virtual void ForeignKey_isUnique_is_stored_in_snapshot() b.HasIndex(""Name""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty"", b => @@ -5849,7 +5897,7 @@ public virtual void ForeignKey_with_non_primary_principal_is_stored_in_snapshot( b.HasKey(""Id""); - b.ToTable(""EntityWithStringAlternateKey""); + b.ToTable(""EntityWithStringAlternateKey"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty"", b => @@ -5867,7 +5915,7 @@ public virtual void ForeignKey_with_non_primary_principal_is_stored_in_snapshot( b.HasIndex(""Name""); - b.ToTable(""EntityWithStringProperty""); + b.ToTable(""EntityWithStringProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty"", b => @@ -5905,7 +5953,7 @@ public virtual void ForeignKey_deleteBehavior_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -5921,7 +5969,7 @@ public virtual void ForeignKey_deleteBehavior_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => @@ -5957,7 +6005,7 @@ public virtual void ForeignKey_deleteBehavior_is_stored_in_snapshot_for_one_to_o b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -5973,7 +6021,7 @@ public virtual void ForeignKey_deleteBehavior_is_stored_in_snapshot_for_one_to_o b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => @@ -6018,7 +6066,7 @@ public virtual void ForeignKey_name_preserved_when_generic() b.HasKey(""Id""); - b.ToTable(""EntityWithGenericKey""); + b.ToTable(""EntityWithGenericKey"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithGenericProperty"", b => @@ -6036,7 +6084,7 @@ public virtual void ForeignKey_name_preserved_when_generic() b.HasIndex(""Property""); - b.ToTable(""EntityWithGenericProperty""); + b.ToTable(""EntityWithGenericProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithGenericProperty"", b => @@ -6099,7 +6147,7 @@ public virtual void ForeignKey_constraint_name_is_stored_in_snapshot_as_fluent_a b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6118,7 +6166,7 @@ public virtual void ForeignKey_constraint_name_is_stored_in_snapshot_as_fluent_a b.HasIndex(""AlternateId"") .IsUnique(); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6165,7 +6213,7 @@ public virtual void ForeignKey_multiple_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6184,7 +6232,7 @@ public virtual void ForeignKey_multiple_annotations_are_stored_in_snapshot() b.HasIndex(""AlternateId"") .IsUnique(); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6243,7 +6291,7 @@ public virtual void Do_not_generate_entity_type_builder_again_if_no_foreign_key_ b.HasIndex(""NavigationId""); - b.ToTable(""BaseType""); + b.ToTable(""BaseType"", ""DefaultSchema""); b.HasDiscriminator(""Discriminator"").HasValue(""BaseType""); @@ -6260,7 +6308,7 @@ public virtual void Do_not_generate_entity_type_builder_again_if_no_foreign_key_ b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+DerivedType"", b => @@ -6301,7 +6349,7 @@ public virtual void ForeignKey_principal_key_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6317,7 +6365,7 @@ public virtual void ForeignKey_principal_key_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => @@ -6365,7 +6413,7 @@ public virtual void ForeignKey_principal_key_with_non_default_name_is_stored_in_ b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6384,7 +6432,7 @@ public virtual void ForeignKey_principal_key_with_non_default_name_is_stored_in_ b.HasAlternateKey(""AlternateId"") .HasAnnotation(""Name"", ""Value""); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithOneProperty"", b => @@ -6441,7 +6489,7 @@ public virtual void Navigation_annotations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6460,7 +6508,7 @@ public virtual void Navigation_annotations_are_stored_in_snapshot() b.HasIndex(""AlternateId"") .IsUnique(); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6508,7 +6556,7 @@ public virtual void Navigation_isRequired_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty""); + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6527,7 +6575,7 @@ public virtual void Navigation_isRequired_is_stored_in_snapshot() b.HasIndex(""AlternateId"") .IsUnique(); - b.ToTable(""EntityWithTwoProperties""); + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -6710,7 +6758,9 @@ partial class Snapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -6848,7 +6898,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey(""Id""); - b.ToTable(""EntityWithManyProperties""); + b.ToTable(""EntityWithManyProperties"", ""DefaultSchema""); b.HasData( new @@ -7057,7 +7107,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) protected virtual string GetHeading(bool empty = false) => @" - modelBuilder.HasAnnotation(""Relational:MaxIdentifierLength"", 128); + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);" + (empty @@ -7107,6 +7159,7 @@ protected void Test(Action buildModel, string expectedCode, Action protected void Test(Action buildModel, string expectedCode, Action assert) { var modelBuilder = CreateConventionalModelBuilder(); + modelBuilder.HasDefaultSchema("DefaultSchema"); modelBuilder.HasChangeTrackingStrategy(ChangeTrackingStrategy.Snapshot); modelBuilder.Model.RemoveAnnotation(CoreAnnotationNames.ProductVersion); buildModel(modelBuilder); From 27aa36e58ae68dbaad3b5d6827a4c9d766e55903 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Thu, 9 Feb 2023 14:18:42 +0000 Subject: [PATCH 6/8] Revert to safest fix and add quirk --- .../RelationalEntityTypeExtensions.cs | 7 +- .../Migrations/ModelSnapshotSqlServerTest.cs | 118 +++++++----------- 2 files changed, 50 insertions(+), 75 deletions(-) diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index 03121fb4e24..f383ae43fc2 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore; /// public static class RelationalEntityTypeExtensions { + private static readonly bool QuirkEnabled29899 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29899", out var enabled) && enabled; + /// /// Gets the name used for the mapped using /// . @@ -145,8 +148,8 @@ public static void SetTableName(this IMutableEntityType entityType, string? name return (string?)schemaAnnotation.Value ?? GetDefaultSchema(entityType); } - return entityType.BaseType != null && entityType.BaseType.GetTableName() != null - ? entityType.BaseType.GetSchema() + return entityType.BaseType != null + ? entityType.GetRootType().GetSchema() ?? (QuirkEnabled29899 ? null : GetDefaultSchema(entityType)) : GetDefaultSchema(entityType); } diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index 424e75dbd45..c36838476d4 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -331,7 +331,7 @@ public virtual void Model_annotations_are_stored_in_snapshot() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""AnnotationName"", ""AnnotationValue"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); @@ -357,7 +357,7 @@ public virtual void Model_Fluent_APIs_are_properly_generated() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseHiLo(modelBuilder, ""EntityFrameworkHiLoSequence""); @@ -397,7 +397,7 @@ public virtual void Model_fluent_APIs_for_sequence_key_are_properly_generated() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseKeySequences(modelBuilder, ""Sequence""); @@ -486,8 +486,7 @@ public virtual void Entities_are_stored_in_model_snapshot() b.HasKey(""Id""); b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema""); - }); -"""), + });"), o => { Assert.Equal(2, o.GetEntityTypes().Count()); @@ -522,7 +521,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT() b.HasKey(""Id""); - b.ToTable("AbstractBase", ""DefaultSchema""); + b.ToTable(""AbstractBase"", ""DefaultSchema""); b.UseTptMappingStrategy(); }); @@ -531,7 +530,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT() { b.HasBaseType(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+AbstractBase""); - b.ToTable("BaseEntity", ""DefaultSchema""); + b.ToTable(""BaseEntity"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+DerivedEntity"", b => @@ -647,7 +646,7 @@ public void Views_are_stored_in_the_model_snapshot() b.ToTable((string)null); - b.ToView(""EntityWithOneProperty"", "DefaultSchema"); + b.ToView(""EntityWithOneProperty"", ""DefaultSchema""); });"), o => Assert.Equal("EntityWithOneProperty", o.GetEntityTypes().Single().GetViewName())); @@ -825,7 +824,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b.HasKey(""Id""); - b.ToTable(""Order"", "DefaultSchema", t => + b.ToTable(""Order"", ""DefaultSchema"", t => { t.Property(""Id"") .HasAnnotation(""fii"", ""arr"") @@ -836,7 +835,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() t.Property(""Shadow""); }); - b.SplitToTable(""SplitOrder"", "DefaultSchema", t => + b.SplitToTable(""SplitOrder"", ""DefaultSchema"", t => { t.HasTrigger(""splitTrigger"") .HasAnnotation(""oof"", ""rab""); @@ -865,13 +864,13 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b1.HasKey(""OrderId""); - b1.ToTable(""SplitOrder"", "DefaultSchema", t => + b1.ToTable(""SplitOrder"", ""DefaultSchema"", t => { t.Property(""BillingShadow"") .HasColumnName(""Shadow""); }); - b1.SplitToTable(""BillingDetails"", "DefaultSchema", t => + b1.SplitToTable(""BillingDetails"", ""DefaultSchema"", t => { t.Property(""BillingShadow"") .HasColumnName(""Shadow""); @@ -915,13 +914,13 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b1.HasKey(""OrderId""); - b1.ToTable(""Order"", "DefaultSchema", t => + b1.ToTable(""Order"", ""DefaultSchema"", t => { t.Property(""ShippingShadow"") .HasColumnName(""Shadow""); }); - b1.SplitToTable(""ShippingDetails"", "DefaultSchema", t => + b1.SplitToTable(""ShippingDetails"", ""DefaultSchema"", t => { t.Property(""ShippingShadow""); }); @@ -945,7 +944,7 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() b2.HasKey(""OrderDetailsOrderId""); - b2.ToTable(""ShippingDetails"", "DefaultSchema"); + b2.ToTable(""ShippingDetails"", ""DefaultSchema""); b2.WithOwner() .HasForeignKey(""OrderDetailsOrderId""); @@ -1088,12 +1087,12 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_views() b.ToTable((string)null); - b.ToView(""EntityWithOneProperty"", "DefaultSchema", v => + b.ToView(""EntityWithOneProperty"", ""DefaultSchema"", v => { v.Property(""Shadow""); }); - b.SplitToView(""SplitView"", "DefaultSchema", v => + b.SplitToView(""SplitView"", ""DefaultSchema"", v => { v.Property(""Shadow""); }); @@ -1113,13 +1112,13 @@ public virtual void Entity_splitting_is_stored_in_snapshot_with_views() b1.ToTable((string)null); - b1.ToView(""EntityWithOneProperty"", "DefaultSchema", v => + b1.ToView(""EntityWithOneProperty"", ""DefaultSchema"", v => { v.Property(""AlternateId"") .HasColumnName(""SomeId""); }); - b1.SplitToView(""SplitView"", "DefaultSchema", v => + b1.SplitToView(""SplitView"", ""DefaultSchema"", v => { v.Property(""AlternateId"") .HasColumnName(""SomeOtherId""); @@ -1371,7 +1370,7 @@ public virtual void CheckConstraint_is_stored_in_snapshot_as_fluent_api() b.HasKey(""Id""); - b.ToTable(""EntityWithTwoProperties"", "DefaultSchema", t => + b.ToTable(""EntityWithTwoProperties"", ""DefaultSchema"", t => { t.HasCheckConstraint(""AlternateId"", ""AlternateId > Id"") .HasName(""CK_Customer_AlternateId"") @@ -1460,7 +1459,7 @@ public virtual void Trigger_is_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty"", "DefaultSchema", t => + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema"", t => { t.HasTrigger(""SomeTrigger"") .HasDatabaseName(""SomeTrg"") @@ -1502,7 +1501,7 @@ public virtual void Triggers_and_ExcludeFromMigrations_are_stored_in_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithOneProperty"", "DefaultSchema", t => + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema"", t => { t.ExcludeFromMigrations(); @@ -1542,7 +1541,7 @@ public virtual void Model_use_identity_columns() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);"), @@ -1561,7 +1560,7 @@ public virtual void Model_use_identity_columns_custom_seed() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 5L);"), @@ -1580,7 +1579,7 @@ public virtual void Model_use_identity_columns_custom_increment() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 5);"), @@ -1611,7 +1610,7 @@ public virtual void Model_use_identity_columns_custom_seed_increment() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 9223372036854775807L, 5); @@ -1626,7 +1625,7 @@ public virtual void Model_use_identity_columns_custom_seed_increment() b.HasKey(""Id""); - b.ToTable(""Buildings"", "DefaultSchema"); + b.ToTable(""Buildings"", ""DefaultSchema""); });"), o => { @@ -2364,7 +2363,7 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i b.HasIndex(""RightsId""); - b.ToTable(""MyJoinTable"", "DefaultSchema"); + b.ToTable(""MyJoinTable"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+ManyToManyLeft"", b => @@ -2555,7 +2554,7 @@ public virtual void Shared_columns_are_stored_in_the_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithProperties"", "DefaultSchema"); + b.ToTable(""EntityWithProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -2570,7 +2569,7 @@ public virtual void Shared_columns_are_stored_in_the_snapshot() b.HasKey(""Id""); - b.ToTable(""EntityWithProperties"", "DefaultSchema"); + b.ToTable(""EntityWithProperties"", ""DefaultSchema""); }); modelBuilder.Entity(""Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithTwoProperties"", b => @@ -3152,7 +3151,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b.HasKey(""Id"") .HasName(""PK_Custom""); - b.ToTable(""EntityWithOneProperty"", "DefaultSchema", t => + b.ToTable(""EntityWithOneProperty"", ""DefaultSchema"", t => { t.ExcludeFromMigrations(); }); @@ -3171,7 +3170,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b.HasKey(""Id""); - b.ToTable(""EntityWithStringKey"", "DefaultSchema", t => + b.ToTable(""EntityWithStringKey"", ""DefaultSchema"", t => { t.ExcludeFromMigrations(); }); @@ -3252,7 +3251,7 @@ public virtual void Owned_types_are_stored_in_snapshot_when_excluded() b1.HasIndex(""EntityWithStringKeyId""); - b1.ToTable(""EntityWithStringProperty"", "DefaultSchema", t => + b1.ToTable(""EntityWithStringProperty"", ""DefaultSchema"", t => { t.ExcludeFromMigrations(); }); @@ -3507,7 +3506,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -3542,7 +3541,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.ToTable((string)null); - b1.ToView(""OwnedView"", "DefaultSchema"); + b1.ToView(""OwnedView"", ""DefaultSchema""); b1.WithOwner() .HasForeignKey(""TestOwnerId""); @@ -3593,7 +3592,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -3629,7 +3628,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey(""TestOwnerId"", ""Id""); - b1.ToTable(""TestOwnee"", "DefaultSchema", t => + b1.ToTable(""TestOwnee"", ""DefaultSchema"", t => { t.HasCheckConstraint(""CK_TestOwnee_TestEnum_Enum_Constraint"", ""[TestEnum] IN (0, 1, 2)""); }); @@ -4021,33 +4020,6 @@ public virtual void Property_maxLength_is_stored_in_snapshot() });"), o => Assert.Equal(100, o.GetEntityTypes().First().FindProperty("Name").GetMaxLength())); - - [ConditionalFact] - public virtual void Property_maximum_maxLength_is_stored_in_snapshot() - => Test( - builder => builder.Entity().Property("Name").HasMaxLength(-1), - AddBoilerPlate( - GetHeading() + -""" - modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+EntityWithStringProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Name") - .HasMaxLength(-1) - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("EntityWithStringProperty", "DefaultSchema"); - }); -"""), - o => Assert.Equal(-1, o.GetEntityTypes().First().FindProperty("Name").GetMaxLength())); - [ConditionalFact] public virtual void Property_unicodeness_is_stored_in_snapshot() => Test( @@ -4312,7 +4284,7 @@ public virtual void Property_column_name_on_specific_table_is_stored_in_snapshot b.Property(""Name"") .HasColumnType(""nvarchar(max)""); - b.ToTable(""BaseEntity"", "DefaultSchema", t => + b.ToTable(""BaseEntity"", ""DefaultSchema"", t => { t.Property(""Name"") .HasColumnName(""DuplicateDerivedEntity_Name""); @@ -4800,7 +4772,7 @@ public virtual void Property_without_column_type() b.HasKey(""Id""); - b.ToTable(""Buildings"", "DefaultSchema"); + b.ToTable(""Buildings"", ""DefaultSchema""); });"), o => { @@ -4836,7 +4808,7 @@ public virtual void Property_with_identity_column() b.HasKey(""Id""); - b.ToTable(""Buildings"", "DefaultSchema"); + b.ToTable(""Buildings"", ""DefaultSchema""); });"), o => { @@ -4874,7 +4846,7 @@ public virtual void Property_with_identity_column_custom_seed() b.HasKey(""Id""); - b.ToTable(""Buildings"", "DefaultSchema"); + b.ToTable(""Buildings"", ""DefaultSchema""); });"), o => { @@ -4912,7 +4884,7 @@ public virtual void Property_with_identity_column_custom_increment() b.HasKey(""Id""); - b.ToTable(""Buildings"", "DefaultSchema"); + b.ToTable(""Buildings"", ""DefaultSchema""); });"), o => { @@ -4950,7 +4922,7 @@ public virtual void Property_with_identity_column_custom_seed_increment() b.HasKey(""Id""); - b.ToTable(""Buildings"", "DefaultSchema"); + b.ToTable(""Buildings"", ""DefaultSchema""); });"), o => { @@ -4996,7 +4968,7 @@ public virtual void SQLServer_model_legacy_identity_seed_int_annotation() AddBoilerPlate( @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 8L);"), @@ -6759,7 +6731,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -7108,7 +7080,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) protected virtual string GetHeading(bool empty = false) => @" modelBuilder - .HasDefaultSchema("DefaultSchema") + .HasDefaultSchema(""DefaultSchema"") .HasAnnotation(""Relational:MaxIdentifierLength"", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);" From 581ad0d3a0bacc2f5eb779a184bcdc42b11a5761 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Mon, 13 Feb 2023 20:18:07 +0000 Subject: [PATCH 7/8] Store null table name in the model snapshot for TPC abstract types (#30257) --- .../Design/CSharpSnapshotGenerator.cs | 1 + .../Migrations/ModelSnapshotSqlServerTest.cs | 201 ++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 75edcecadea..74dd9204456 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -861,6 +861,7 @@ private void GenerateTableMapping( var explicitName = tableNameAnnotation != null || entityType.BaseType == null + || (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy && entityType.IsAbstract()) || entityType.BaseType.GetTableName() != tableName || hasOverrides; diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index c36838476d4..88000e03c32 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -319,6 +319,34 @@ public override bool GeneratesTemporaryValues => false; } + private abstract class Animal + { + public int Id { get; set; } + public string Name { get; set; } + } + + private abstract class Pet : Animal + { + public string Vet { get; set; } + public ICollection Humans { get; } = new List(); + } + + private class Cat : Pet + { + public string EducationLevel { get; set; } + } + + private class Dog : Pet + { + public string FavoriteToy { get; set; } + } + + private class Human : Animal + { + public Animal FavoriteAnimal { get; set; } + public ICollection Pets { get; } = new List(); + } + #region Model [ConditionalFact] @@ -745,6 +773,179 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPC() Assert.Equal("DerivedView", derived.GetViewName()); }); + [ConditionalFact] // Issue #30058 + public virtual void Non_base_abstract_base_class_with_TPC() + => Test( + builder => + { + builder.Entity().UseTpcMappingStrategy(); + builder.Entity(); + builder.Entity(); + builder.Entity(); + builder.Entity(); + }, +""" +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace RootNamespace +{ + [DbContext(typeof(DbContext))] + partial class Snapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("DefaultSchema") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.HasSequence("AnimalSequence"); + + modelBuilder.Entity("HumanPet", b => + { + b.Property("HumansId") + .HasColumnType("int"); + + b.Property("PetsId") + .HasColumnType("int"); + + b.HasKey("HumansId", "PetsId"); + + b.HasIndex("PetsId"); + + b.ToTable("HumanPet", "DefaultSchema"); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Animal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValueSql("NEXT VALUE FOR [DefaultSchema].[AnimalSequence]"); + + SqlServerPropertyBuilderExtensions.UseSequence(b.Property("Id")); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable((string)null); + + b.UseTpcMappingStrategy(); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Human", b => + { + b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Animal"); + + b.Property("FavoriteAnimalId") + .HasColumnType("int"); + + b.HasIndex("FavoriteAnimalId"); + + b.ToTable("Human", "DefaultSchema"); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Pet", b => + { + b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Animal"); + + b.Property("Vet") + .HasColumnType("nvarchar(max)"); + + b.ToTable((string)null); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Cat", b => + { + b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Pet"); + + b.Property("EducationLevel") + .HasColumnType("nvarchar(max)"); + + b.ToTable("Cat", "DefaultSchema"); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Dog", b => + { + b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Pet"); + + b.Property("FavoriteToy") + .HasColumnType("nvarchar(max)"); + + b.ToTable("Dog", "DefaultSchema"); + }); + + modelBuilder.Entity("HumanPet", b => + { + b.HasOne("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Human", null) + .WithMany() + .HasForeignKey("HumansId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Pet", null) + .WithMany() + .HasForeignKey("PetsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Human", b => + { + b.HasOne("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Animal", "FavoriteAnimal") + .WithMany() + .HasForeignKey("FavoriteAnimalId"); + + b.Navigation("FavoriteAnimal"); + }); +#pragma warning restore 612, 618 + } + } +} + +""", + model => + { + Assert.Equal(6, model.GetAnnotations().Count()); + Assert.Equal(6, model.GetEntityTypes().Count()); + + var animalType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Animal"); + Assert.Null(animalType.GetTableName()); + Assert.Null(animalType.GetViewName()); + Assert.Equal("TPC", animalType.GetMappingStrategy()); + + var petType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Pet"); + Assert.Null(petType.GetTableName()); + Assert.Null(petType.GetViewName()); + + var catType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Cat"); + Assert.Equal("Cat", catType.GetTableName()); + Assert.Null(catType.GetViewName()); + + var dogType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Dog"); + Assert.Equal("Dog", dogType.GetTableName()); + Assert.Null(dogType.GetViewName()); + + var humanType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Human"); + Assert.Equal("Human", humanType.GetTableName()); + Assert.Null(humanType.GetViewName()); + + var humanPetType = model.FindEntityType("HumanPet"); + Assert.Equal("HumanPet", humanPetType.GetTableName()); + Assert.Null(humanPetType.GetViewName()); + }); + [ConditionalFact] public virtual void Entity_splitting_is_stored_in_snapshot_with_tables() => Test( From 77aac275cda14c8b80344c95b9c8fc8770e68f5f Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Mon, 13 Feb 2023 23:39:56 +0000 Subject: [PATCH 8/8] Add quirk --- .../Migrations/Design/CSharpSnapshotGenerator.cs | 5 ++++- .../Migrations/ModelSnapshotSqlServerTest.cs | 16 +++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 74dd9204456..1af9d7ba44e 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Design; /// public class CSharpSnapshotGenerator : ICSharpSnapshotGenerator { + private static readonly bool QuirkEnabled30058 + = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue30058", out var enabled) && enabled; + private static readonly MethodInfo HasAnnotationMethodInfo = typeof(ModelBuilder).GetRuntimeMethod(nameof(ModelBuilder.HasAnnotation), new[] { typeof(string), typeof(string) })!; @@ -861,7 +864,7 @@ private void GenerateTableMapping( var explicitName = tableNameAnnotation != null || entityType.BaseType == null - || (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy && entityType.IsAbstract()) + || (!QuirkEnabled30058 && (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy && entityType.IsAbstract())) || entityType.BaseType.GetTableName() != tableName || hasOverrides; diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index 88000e03c32..1e91b989766 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -802,9 +802,7 @@ partial class Snapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("DefaultSchema") - .HasAnnotation("Relational:MaxIdentifierLength", 128); + modelBuilder.HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -822,7 +820,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("PetsId"); - b.ToTable("HumanPet", "DefaultSchema"); + b.ToTable("HumanPet"); }); modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Animal", b => @@ -830,7 +828,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") - .HasDefaultValueSql("NEXT VALUE FOR [DefaultSchema].[AnimalSequence]"); + .HasDefaultValueSql("NEXT VALUE FOR [AnimalSequence]"); SqlServerPropertyBuilderExtensions.UseSequence(b.Property("Id")); @@ -853,7 +851,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("FavoriteAnimalId"); - b.ToTable("Human", "DefaultSchema"); + b.ToTable("Human"); }); modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Pet", b => @@ -873,7 +871,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("EducationLevel") .HasColumnType("nvarchar(max)"); - b.ToTable("Cat", "DefaultSchema"); + b.ToTable("Cat"); }); modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Dog", b => @@ -883,7 +881,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("FavoriteToy") .HasColumnType("nvarchar(max)"); - b.ToTable("Dog", "DefaultSchema"); + b.ToTable("Dog"); }); modelBuilder.Entity("HumanPet", b => @@ -917,7 +915,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) """, model => { - Assert.Equal(6, model.GetAnnotations().Count()); + Assert.Equal(5, model.GetAnnotations().Count()); Assert.Equal(6, model.GetEntityTypes().Count()); var animalType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Animal");