From 27aaca79e4c7da96185f9a500347668ddeaba93a Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Tue, 2 May 2023 11:26:17 +0100 Subject: [PATCH] ModelBuilder API for sentinels Part of #701 --- .../Builders/IConventionPropertyBuilder.cs | 18 ++ .../PropertiesConfigurationBuilder.cs | 14 ++ .../Metadata/Builders/PropertyBuilder.cs | 14 ++ .../Metadata/Builders/PropertyBuilder`.cs | 10 + .../TypeMappingConfigurationBuilder.cs | 14 ++ .../TypeMappingConfigurationBuilder`.cs | 10 + .../Metadata/Internal/CoreAnnotationNames.cs | 9 + .../Internal/InternalPropertyBuilder.cs | 46 ++++ .../Internal/PropertyConfiguration.cs | 26 +- .../GraphUpdatesSqlServerOwnedTest.cs | 2 +- .../GraphUpdatesSqlServerTestBase.cs | 2 +- ...verSentinelValueGenerationScenariosTest.cs | 12 +- ...lServerValueGenerationScenariosTestBase.cs | 48 ++-- .../StoreGeneratedSentinelSqlServerTest.cs | 234 +++++++++--------- .../GraphUpdatesSqliteTestBase.cs | 2 +- .../ChangeTracking/EntityEntryTest.cs | 6 +- test/EFCore.Tests/DbContextServicesTest.cs | 6 +- .../Internal/InternalPropertyBuilderTest.cs | 31 +++ .../ModelBuilding/ModelBuilderGenericTest.cs | 3 + .../ModelBuilderNonGenericTest.cs | 3 + .../ModelBuilding/ModelBuilderTestBase.cs | 1 + .../ModelBuilding/NonRelationshipTestBase.cs | 60 +++++ 22 files changed, 409 insertions(+), 162 deletions(-) diff --git a/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs index c94deac720f..8868fe52f08 100644 --- a/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs +++ b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs @@ -161,6 +161,24 @@ public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder /// if the maximum length of data allowed can be set for this property. bool CanSetMaxLength(int? maxLength, bool fromDataAnnotation = false); + /// + /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the + /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of + /// the property. + /// + /// The sentinel value. + /// Indicates whether the configuration was specified using a data annotation. + /// The same builder instance if the configuration was applied, otherwise. + IConventionPropertyBuilder? HasSentinel(object? sentinel, bool fromDataAnnotation = false); + + /// + /// Returns a value indicating whether the sentinel can be set for this property from the current configuration source. + /// + /// The sentinel value. + /// Indicates whether the configuration was specified using a data annotation. + /// if the sentinel can be set for this property. + bool CanSetSentinel(object? sentinel, bool fromDataAnnotation = false); + /// /// Configures whether the property as capable of persisting unicode characters. /// diff --git a/src/EFCore/Metadata/Builders/PropertiesConfigurationBuilder.cs b/src/EFCore/Metadata/Builders/PropertiesConfigurationBuilder.cs index 072223dec5b..ee4d38fce89 100644 --- a/src/EFCore/Metadata/Builders/PropertiesConfigurationBuilder.cs +++ b/src/EFCore/Metadata/Builders/PropertiesConfigurationBuilder.cs @@ -69,6 +69,20 @@ public virtual PropertiesConfigurationBuilder HaveMaxLength(int maxLength) return this; } + /// + /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the + /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of + /// the property. + /// + /// The sentinel value. + /// The same builder instance so that multiple configuration calls can be chained. + public virtual PropertiesConfigurationBuilder HaveSentinel(object? sentinel) + { + Configuration.SetSentinel(sentinel); + + return this; + } + /// /// Configures the precision and scale of the property. /// diff --git a/src/EFCore/Metadata/Builders/PropertyBuilder.cs b/src/EFCore/Metadata/Builders/PropertyBuilder.cs index 3fc9f570b35..4f9155f3c9d 100644 --- a/src/EFCore/Metadata/Builders/PropertyBuilder.cs +++ b/src/EFCore/Metadata/Builders/PropertyBuilder.cs @@ -95,6 +95,20 @@ public virtual PropertyBuilder HasMaxLength(int maxLength) return this; } + /// + /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the + /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of + /// the property. + /// + /// The sentinel value. + /// The same builder instance if the configuration was applied, otherwise. + public virtual PropertyBuilder HasSentinel(object? sentinel) + { + Builder.HasSentinel(sentinel, ConfigurationSource.Explicit); + + return this; + } + /// /// Configures the precision and scale of the property. /// diff --git a/src/EFCore/Metadata/Builders/PropertyBuilder`.cs b/src/EFCore/Metadata/Builders/PropertyBuilder`.cs index 031868f7be3..a4264488fc0 100644 --- a/src/EFCore/Metadata/Builders/PropertyBuilder`.cs +++ b/src/EFCore/Metadata/Builders/PropertyBuilder`.cs @@ -63,6 +63,16 @@ public PropertyBuilder(IMutableProperty property) public new virtual PropertyBuilder HasMaxLength(int maxLength) => (PropertyBuilder)base.HasMaxLength(maxLength); + /// + /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the + /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of + /// the property. + /// + /// The sentinel value. + /// The same builder instance if the configuration was applied, otherwise. + public new virtual PropertyBuilder HasSentinel(object? sentinel) + => (PropertyBuilder)base.HasSentinel(sentinel); + /// /// Configures the precision and scale of the property. /// diff --git a/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder.cs b/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder.cs index 9d84aa09cf9..e050fb12a86 100644 --- a/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder.cs +++ b/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder.cs @@ -74,6 +74,20 @@ public virtual TypeMappingConfigurationBuilder HasMaxLength(int maxLength) return this; } + /// + /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the + /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of + /// the property. + /// + /// The sentinel value. + /// The same builder instance if the configuration was applied, otherwise. + public virtual TypeMappingConfigurationBuilder HasSentinel(object? sentinel) + { + Configuration.SetSentinel(sentinel); + + return this; + } + /// /// Configures the precision and scale of the property. /// diff --git a/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder`.cs b/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder`.cs index 5d602145e71..67787f39ca8 100644 --- a/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder`.cs +++ b/src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder`.cs @@ -53,6 +53,16 @@ public TypeMappingConfigurationBuilder(PropertyConfiguration scalar) public new virtual TypeMappingConfigurationBuilder HasMaxLength(int maxLength) => (TypeMappingConfigurationBuilder)base.HasMaxLength(maxLength); + /// + /// Configures the value that will be used to determine if the property has been set or not. If the property is set to the + /// sentinel value, then it is considered not set. By default, the sentinel value is the CLR default value for the type of + /// the property. + /// + /// The sentinel value. + /// The same builder instance if the configuration was applied, otherwise. + public new virtual TypeMappingConfigurationBuilder HasSentinel(object? sentinel) + => (TypeMappingConfigurationBuilder)base.HasSentinel(sentinel); + /// /// Configures the precision and scale of the property. /// diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs index dc238cab330..3df816e2e66 100644 --- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs +++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs @@ -19,6 +19,14 @@ public static class CoreAnnotationNames /// public const string MaxLength = "MaxLength"; + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public const string Sentinel = "Sentinel"; + /// /// 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 @@ -317,6 +325,7 @@ public static class CoreAnnotationNames public static readonly ISet AllNames = new HashSet { MaxLength, + Sentinel, Precision, Scale, Unicode, diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs index 4fe06855d01..a6d43af00c8 100644 --- a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs @@ -193,6 +193,34 @@ public virtual bool CanSetMaxLength(int? maxLength, ConfigurationSource? configu => configurationSource.Overrides(Metadata.GetMaxLengthConfigurationSource()) || Metadata.GetMaxLength() == maxLength; + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual InternalPropertyBuilder? HasSentinel(object? sentinel, ConfigurationSource configurationSource) + { + if (CanSetSentinel(sentinel, configurationSource)) + { + Metadata.SetSentinel(sentinel, configurationSource); + + return this; + } + + return null; + } + + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual bool CanSetSentinel(object? sentinel, ConfigurationSource? configurationSource) + => configurationSource.Overrides(Metadata.GetSentinelConfigurationSource()) + || Equals(Metadata.Sentinel, sentinel); + /// /// 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 @@ -1013,6 +1041,24 @@ bool IConventionPropertyBaseBuilder.CanSetPropertyAccessMode(PropertyAccessMode? bool IConventionPropertyBuilder.CanSetMaxLength(int? maxLength, bool fromDataAnnotation) => CanSetMaxLength(maxLength, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IConventionPropertyBuilder? IConventionPropertyBuilder.HasSentinel(object? sentinel, bool fromDataAnnotation) + => HasSentinel(sentinel, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + bool IConventionPropertyBuilder.CanSetSentinel(object? sentinel, bool fromDataAnnotation) + => CanSetSentinel(sentinel, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + /// /// 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 diff --git a/src/EFCore/Metadata/Internal/PropertyConfiguration.cs b/src/EFCore/Metadata/Internal/PropertyConfiguration.cs index 59f0541633a..f760e2c73a4 100644 --- a/src/EFCore/Metadata/Internal/PropertyConfiguration.cs +++ b/src/EFCore/Metadata/Internal/PropertyConfiguration.cs @@ -46,23 +46,21 @@ public virtual void Apply(IMutableProperty property) { case CoreAnnotationNames.MaxLength: property.SetMaxLength((int?)annotation.Value); - + break; + case CoreAnnotationNames.Sentinel: + property.Sentinel = annotation.Value; break; case CoreAnnotationNames.Unicode: property.SetIsUnicode((bool?)annotation.Value); - break; case CoreAnnotationNames.Precision: property.SetPrecision((int?)annotation.Value); - break; case CoreAnnotationNames.Scale: property.SetScale((int?)annotation.Value); - break; case CoreAnnotationNames.ProviderClrType: property.SetProviderClrType((Type?)annotation.Value); - break; case CoreAnnotationNames.ValueConverterType: if (ClrType.UnwrapNullableType() == property.ClrType.UnwrapNullableType()) @@ -121,6 +119,24 @@ public virtual void SetMaxLength(int? maxLength) this[CoreAnnotationNames.MaxLength] = maxLength; } + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual int? GetSentinel() + => (int?)this[CoreAnnotationNames.Sentinel]; + + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual void SetSentinel(object? sentinel) + => this[CoreAnnotationNames.Sentinel] = sentinel; + /// /// 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 diff --git a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs index 859014b1a05..60390b6a4cd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerOwnedTest.cs @@ -565,7 +565,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity( b => { - b.Property(e => e.IdUserState).HasDefaultValue(1).Metadata.Sentinel = 667; + b.Property(e => e.IdUserState).HasDefaultValue(1).HasSentinel(667); b.HasOne(e => e.UserState).WithMany(e => e.Users).HasForeignKey(e => e.IdUserState); }); } diff --git a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTestBase.cs index 46bbe702578..25f01f86e3a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTestBase.cs +++ b/test/EFCore.SqlServer.FunctionalTests/GraphUpdates/GraphUpdatesSqlServerTestBase.cs @@ -53,7 +53,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity( b => { - b.Property(e => e.IdUserState).HasDefaultValue(1).Metadata.Sentinel = 667; + b.Property(e => e.IdUserState).HasDefaultValue(1).HasSentinel(667); b.HasOne(e => e.UserState).WithMany(e => e.Users).HasForeignKey(e => e.IdUserState); }); diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerSentinelValueGenerationScenariosTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerSentinelValueGenerationScenariosTest.cs index 7eaeadff57b..4d910ff4201 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerSentinelValueGenerationScenariosTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerSentinelValueGenerationScenariosTest.cs @@ -51,12 +51,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = IntSentinel; - b.Property(e => e.Name).Metadata.Sentinel = StringSentinel; - b.Property(e => e.CreatedOn).Metadata.Sentinel = DateTimeSentinel; - b.Property(e => e.GeometryCollection).Metadata.Sentinel = GeometryCollectionSentinel; - b.Property(e => e.OtherId).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.NeedsConverter).Metadata.Sentinel = new NeedsConverter(IntSentinel); + b.Property(e => e.Id).HasSentinel(IntSentinel); + b.Property(e => e.Name).HasSentinel(StringSentinel); + b.Property(e => e.CreatedOn).HasSentinel(DateTimeSentinel); + b.Property(e => e.GeometryCollection).HasSentinel(GeometryCollectionSentinel); + b.Property(e => e.OtherId).HasSentinel(NullableIntSentinel); + b.Property(e => e.NeedsConverter).HasSentinel(new NeedsConverter(IntSentinel)); }); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerValueGenerationScenariosTestBase.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerValueGenerationScenariosTestBase.cs index 242dd36ba6c..fe4bce881df 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerValueGenerationScenariosTestBase.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerValueGenerationScenariosTestBase.cs @@ -324,8 +324,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Entity() .Property(e => e.Id) .HasDefaultValueSql("'i' + CAST((NEXT VALUE FOR MyStringSequence) AS VARCHAR(20))") - .Metadata - .Sentinel = _stringSentinel; + .HasSentinel(_stringSentinel); } } @@ -424,7 +423,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Entity() .Property(e => e.Id) .HasConversion() - .Metadata.Sentinel = _uintSentinel; + .HasSentinel(_uintSentinel); } } @@ -478,7 +477,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Entity() .Property(e => e.Id) .ValueGeneratedOnAdd() - .Metadata.Sentinel = _sentinel; + .HasSentinel(_sentinel); } } @@ -539,7 +538,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Entity() .Property(e => e.Id) .ValueGeneratedOnAdd() - .Metadata.Sentinel = _sentinel; + .HasSentinel(_sentinel); } } @@ -606,7 +605,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) : int.Parse(v), v => v.ToString()) .ValueGeneratedOnAdd() - .Metadata.Sentinel = _sentinel; + .HasSentinel(_sentinel); } } @@ -693,7 +692,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Entity() .Property(e => e.Id) .ValueGeneratedNever() - .Metadata.Sentinel = _sentinel; + .HasSentinel(_sentinel); } } @@ -898,11 +897,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = _intSentinel; + b.Property(e => e.Id).HasSentinel(_intSentinel); - var property = b.Property(e => e.CreatedOn).HasDefaultValueSql("getdate()"); + var property = b.Property(e => e.CreatedOn).HasDefaultValueSql("getdate()").HasSentinel(_dateTimeSentinel); property.Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Throw); - property.Metadata.Sentinel = _dateTimeSentinel; }); } } @@ -962,15 +960,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = _intSentinel; + b.Property(e => e.Id).HasSentinel(_intSentinel); var property = b.Property(e => e.FullName) .HasComputedColumnSql("FirstName + ' ' + LastName") + .HasSentinel(_stringSentinel) .Metadata; property.SetBeforeSaveBehavior(PropertySaveBehavior.Throw); property.SetAfterSaveBehavior(PropertySaveBehavior.Throw); - property.Sentinel = _stringSentinel; }); } } @@ -1063,15 +1061,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = _intSentinel; + b.Property(e => e.Id).HasSentinel(_intSentinel); var property = modelBuilder.Entity() .Property(e => e.FullName) .HasComputedColumnSql("[dbo].[GetFullName]([FirstName], [LastName])") + .HasSentinel(_stringSentinel) .Metadata; property.SetAfterSaveBehavior(PropertySaveBehavior.Throw); - property.Sentinel = _stringSentinel; }); } } @@ -1316,8 +1314,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) eb => { eb.HasAlternateKey(e => e.NotId); - eb.Property(e => e.NotId).ValueGeneratedOnAdd().Metadata.Sentinel = _sentinel; - eb.Property(e => e.Id).Metadata.Sentinel = _sentinel; + eb.Property(e => e.NotId).ValueGeneratedOnAdd().HasSentinel(_sentinel); + eb.Property(e => e.Id).HasSentinel(_sentinel); }); } } @@ -1362,8 +1360,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = _sentinel; - b.Property(e => e.NotId).ValueGeneratedOnAdd().Metadata.Sentinel = _sentinel; + b.Property(e => e.Id).HasSentinel(_sentinel); + b.Property(e => e.NotId).ValueGeneratedOnAdd().HasSentinel(_sentinel); }); } } @@ -1426,8 +1424,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Entity( eb => { - eb.Property(e => e.Id).HasDefaultValueSql("newsequentialid()").Metadata.Sentinel = _sentinel; - eb.Property(e => e.NotId).HasDefaultValueSql("newsequentialid()").Metadata.Sentinel = _sentinel; + eb.Property(e => e.Id).HasDefaultValueSql("newsequentialid()").HasSentinel(_sentinel); + eb.Property(e => e.NotId).HasDefaultValueSql("newsequentialid()").HasSentinel(_sentinel); }); } } @@ -1517,7 +1515,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Entity() .Property(e => e.Id) .ValueGeneratedNever() - .Metadata.Sentinel = _sentinel; + .HasSentinel(_sentinel); } } @@ -1557,10 +1555,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) var property = modelBuilder .Entity() .Property(e => e.Id) - .HasDefaultValueSql("next value for MySequence"); + .HasDefaultValueSql("next value for MySequence") + .HasSentinel(_sentinel); property.Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Throw); - property.Metadata.Sentinel = _sentinel; } } @@ -1715,11 +1713,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = _intSentinel; + b.Property(e => e.Id).HasSentinel(_intSentinel); b.Property(e => e.Timestamp) .ValueGeneratedOnAddOrUpdate() .IsConcurrencyToken() - .Metadata.Sentinel = _timestampSentinel; + .HasSentinel(_timestampSentinel); }); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/StoreGeneratedSentinelSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/StoreGeneratedSentinelSqlServerTest.cs index 3070c8c5837..6765ae7401f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/StoreGeneratedSentinelSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/StoreGeneratedSentinelSqlServerTest.cs @@ -102,223 +102,223 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = IntSentinel; - b.Property(e => e.NotStoreGenerated).Metadata.Sentinel = StringSentinel; - b.Property(e => e.Identity).Metadata.Sentinel = StringSentinel; - b.Property(e => e.IdentityReadOnlyBeforeSave).Metadata.Sentinel = StringSentinel; - b.Property(e => e.IdentityReadOnlyAfterSave).Metadata.Sentinel = StringSentinel; - b.Property(e => e.AlwaysIdentity).Metadata.Sentinel = StringSentinel; - b.Property(e => e.AlwaysIdentityReadOnlyBeforeSave).Metadata.Sentinel = StringSentinel; - b.Property(e => e.AlwaysIdentityReadOnlyAfterSave).Metadata.Sentinel = StringSentinel; - b.Property(e => e.Computed).Metadata.Sentinel = StringSentinel; - b.Property(e => e.ComputedReadOnlyBeforeSave).Metadata.Sentinel = StringSentinel; - b.Property(e => e.ComputedReadOnlyAfterSave).Metadata.Sentinel = StringSentinel; - b.Property(e => e.AlwaysComputed).Metadata.Sentinel = StringSentinel; - b.Property(e => e.AlwaysComputedReadOnlyBeforeSave).Metadata.Sentinel = StringSentinel; - b.Property(e => e.AlwaysComputedReadOnlyAfterSave).Metadata.Sentinel = StringSentinel; + b.Property(e => e.Id).HasSentinel(IntSentinel); + b.Property(e => e.NotStoreGenerated).HasSentinel(StringSentinel); + b.Property(e => e.Identity).HasSentinel(StringSentinel); + b.Property(e => e.IdentityReadOnlyBeforeSave).HasSentinel(StringSentinel); + b.Property(e => e.IdentityReadOnlyAfterSave).HasSentinel(StringSentinel); + b.Property(e => e.AlwaysIdentity).HasSentinel(StringSentinel); + b.Property(e => e.AlwaysIdentityReadOnlyBeforeSave).HasSentinel(StringSentinel); + b.Property(e => e.AlwaysIdentityReadOnlyAfterSave).HasSentinel(StringSentinel); + b.Property(e => e.Computed).HasSentinel(StringSentinel); + b.Property(e => e.ComputedReadOnlyBeforeSave).HasSentinel(StringSentinel); + b.Property(e => e.ComputedReadOnlyAfterSave).HasSentinel(StringSentinel); + b.Property(e => e.AlwaysComputed).HasSentinel(StringSentinel); + b.Property(e => e.AlwaysComputedReadOnlyBeforeSave).HasSentinel(StringSentinel); + b.Property(e => e.AlwaysComputedReadOnlyAfterSave).HasSentinel(StringSentinel); }); modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = IntSentinel; - b.Property(e => e.Never).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverUseBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverIgnoreBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverThrowBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverUseBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverIgnoreBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverThrowBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverUseBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverIgnoreBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.NeverThrowBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - - b.Property(e => e.OnAdd).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddUseBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddIgnoreBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddThrowBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddUseBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddIgnoreBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddThrowBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddUseBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddIgnoreBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddThrowBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - - b.Property(e => e.OnAddOrUpdate).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateUseBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateIgnoreBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateThrowBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateUseBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateIgnoreBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateThrowBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateUseBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateIgnoreBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnAddOrUpdateThrowBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - - b.Property(e => e.OnUpdate).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateUseBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateIgnoreBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateThrowBeforeUseAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateUseBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateIgnoreBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateThrowBeforeIgnoreAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateUseBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateIgnoreBeforeThrowAfter).Metadata.Sentinel = StringSentinel; - b.Property(e => e.OnUpdateThrowBeforeThrowAfter).Metadata.Sentinel = StringSentinel; + b.Property(e => e.Id).HasSentinel(IntSentinel); + b.Property(e => e.Never).HasSentinel(StringSentinel); + b.Property(e => e.NeverUseBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverIgnoreBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverThrowBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverUseBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverIgnoreBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverThrowBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverUseBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverIgnoreBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.NeverThrowBeforeThrowAfter).HasSentinel(StringSentinel); + + b.Property(e => e.OnAdd).HasSentinel(StringSentinel); + b.Property(e => e.OnAddUseBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddIgnoreBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddThrowBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddUseBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddIgnoreBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddThrowBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddUseBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddIgnoreBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddThrowBeforeThrowAfter).HasSentinel(StringSentinel); + + b.Property(e => e.OnAddOrUpdate).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateUseBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateIgnoreBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateThrowBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateUseBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateIgnoreBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateThrowBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateUseBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateIgnoreBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnAddOrUpdateThrowBeforeThrowAfter).HasSentinel(StringSentinel); + + b.Property(e => e.OnUpdate).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateUseBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateIgnoreBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateThrowBeforeUseAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateUseBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateIgnoreBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateThrowBeforeIgnoreAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateUseBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateIgnoreBeforeThrowAfter).HasSentinel(StringSentinel); + b.Property(e => e.OnUpdateThrowBeforeThrowAfter).HasSentinel(StringSentinel); }); modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.NullableAsNonNullable).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.NonNullableAsNullable).Metadata.Sentinel = IntSentinel; + b.Property(e => e.Id).HasSentinel(NullableIntSentinel); + b.Property(e => e.NullableAsNonNullable).HasSentinel(NullableIntSentinel); + b.Property(e => e.NonNullableAsNullable).HasSentinel(IntSentinel); }); modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.NullableBackedBoolTrueDefault).Metadata.Sentinel = NullableBoolSentinel; - b.Property(e => e.NullableBackedIntNonZeroDefault).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.NullableBackedBoolFalseDefault).Metadata.Sentinel = NullableBoolSentinel; - b.Property(e => e.NullableBackedIntZeroDefault).Metadata.Sentinel = NullableIntSentinel; + b.Property(e => e.Id).HasSentinel(NullableIntSentinel); + b.Property(e => e.NullableBackedBoolTrueDefault).HasSentinel(NullableBoolSentinel); + b.Property(e => e.NullableBackedIntNonZeroDefault).HasSentinel(NullableIntSentinel); + b.Property(e => e.NullableBackedBoolFalseDefault).HasSentinel(NullableBoolSentinel); + b.Property(e => e.NullableBackedIntZeroDefault).HasSentinel(NullableIntSentinel); }); modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.NullableBackedBoolTrueDefault).Metadata.Sentinel = NullableBoolSentinel; - b.Property(e => e.NullableBackedIntNonZeroDefault).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.NullableBackedBoolFalseDefault).Metadata.Sentinel = NullableBoolSentinel; - b.Property(e => e.NullableBackedIntZeroDefault).Metadata.Sentinel = NullableIntSentinel; + b.Property(e => e.Id).HasSentinel(NullableIntSentinel); + b.Property(e => e.NullableBackedBoolTrueDefault).HasSentinel(NullableBoolSentinel); + b.Property(e => e.NullableBackedIntNonZeroDefault).HasSentinel(NullableIntSentinel); + b.Property(e => e.NullableBackedBoolFalseDefault).HasSentinel(NullableBoolSentinel); + b.Property(e => e.NullableBackedIntZeroDefault).HasSentinel(NullableIntSentinel); }); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = IntSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(IntSentinel); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = IntSentinel; - modelBuilder.Entity().Property(e => e.PrincipalId).Metadata.Sentinel = IntSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(IntSentinel); + modelBuilder.Entity().Property(e => e.PrincipalId).HasSentinel(IntSentinel); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = WrappedIntHiLoKeyClassSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = WrappedIntHiLoKeyStructSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = WrappedIntHiLoKeyRecordSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(WrappedIntHiLoKeyClassSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(WrappedIntHiLoKeyStructSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(WrappedIntHiLoKeyRecordSentinel); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = IntSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = GuidSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = GuidSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = ShortSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(IntSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(GuidSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(GuidSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(ShortSentinel); modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = NullableIntSentinel; - b.Property(e => e.Name).Metadata.Sentinel = StringSentinel; + b.Property(e => e.Id).HasSentinel(NullableIntSentinel); + b.Property(e => e.Name).HasSentinel(StringSentinel); }); modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = IntSentinel; - b.Property(e => e.Name).Metadata.Sentinel = StringSentinel; + b.Property(e => e.Id).HasSentinel(IntSentinel); + b.Property(e => e.Name).HasSentinel(StringSentinel); }); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = IntSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = IntSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(IntSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(IntSentinel); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedIntKeyClassSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedIntClassSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedIntKeyClassSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedIntClassSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedIntKeyStructSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedIntStructSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedIntKeyStructSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedIntStructSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedIntKeyRecordSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedIntRecordSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedIntKeyRecordSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedIntRecordSentinel); }); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = LongSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = LongToDecimalPrincipalSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(LongSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(LongToDecimalPrincipalSentinel); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedGuidKeyClassSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedGuidClassSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedGuidKeyClassSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedGuidClassSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedGuidKeyStructSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedGuidStructSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedGuidKeyStructSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedGuidStructSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedGuidKeyRecordSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedGuidRecordSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedGuidKeyRecordSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedGuidRecordSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedStringKeyClassSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedStringClassSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedStringKeyClassSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedStringClassSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedStringKeyStructSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedStringStructSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedStringKeyStructSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedStringStructSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedStringKeyRecordSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedStringRecordSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedStringKeyRecordSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedStringRecordSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedUriKeyClassSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedUriClassSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedUriKeyClassSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedUriClassSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedUriKeyStructSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedUriStructSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedUriKeyStructSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedUriStructSentinel); }); modelBuilder.Entity( entity => { - entity.Property(e => e.Id).Metadata.Sentinel = WrappedUriKeyRecordSentinel; - entity.Property(e => e.NonKey).Metadata.Sentinel = WrappedUriRecordSentinel; + entity.Property(e => e.Id).HasSentinel(WrappedUriKeyRecordSentinel); + entity.Property(e => e.NonKey).HasSentinel(WrappedUriRecordSentinel); }); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = UriSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(UriSentinel); ; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = KeyEnumSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(KeyEnumSentinel); ; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = GuidAsStringSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = StringAsGuidSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(GuidAsStringSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(StringAsGuidSentinel); } } } diff --git a/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs b/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs index 04a2ee79623..6764c2e600c 100644 --- a/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs +++ b/test/EFCore.Sqlite.FunctionalTests/GraphUpdates/GraphUpdatesSqliteTestBase.cs @@ -91,7 +91,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity( b => { - b.Property(e => e.IdUserState).HasDefaultValue(1).Metadata.Sentinel = 667; + b.Property(e => e.IdUserState).HasDefaultValue(1).HasSentinel(667); b.HasOne(e => e.UserState).WithMany(e => e.Users).HasForeignKey(e => e.IdUserState); }); diff --git a/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs b/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs index 9bb5423ee53..2e39b0445be 100644 --- a/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs @@ -157,13 +157,13 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity( b => { - b.Property(e => e.Id).Metadata.Sentinel = 667; + b.Property(e => e.Id).HasSentinel(667); b.HasOne(e => e.Dependent) .WithOne(e => e.Principal) .HasForeignKey(e => e.Id); }); - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = 667; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(667); modelBuilder.Entity().Property(e => e.Id).ValueGeneratedNever(); @@ -181,7 +181,7 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) b => { b.HasKey(e => new { e.Id1, e.Id2 }); - b.Property(e => e.Id2).ValueGeneratedOnAdd().Metadata.Sentinel = true; + b.Property(e => e.Id2).ValueGeneratedOnAdd().HasSentinel(true); }); } } diff --git a/test/EFCore.Tests/DbContextServicesTest.cs b/test/EFCore.Tests/DbContextServicesTest.cs index c1cc2763199..29fa6cae2f5 100644 --- a/test/EFCore.Tests/DbContextServicesTest.cs +++ b/test/EFCore.Tests/DbContextServicesTest.cs @@ -861,9 +861,9 @@ protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBu protected internal override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = IntSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = IntSentinel; - modelBuilder.Entity().Property(e => e.Id).Metadata.Sentinel = GuidSentinel; + modelBuilder.Entity().Property(e => e.Id).HasSentinel(IntSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(IntSentinel); + modelBuilder.Entity().Property(e => e.Id).HasSentinel(GuidSentinel); } } diff --git a/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs index ce771033de9..0d6d73151b1 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs @@ -117,6 +117,37 @@ public void Can_only_override_existing_MaxLength_value_explicitly() Assert.Equal(2, metadata.GetMaxLength().Value); } + [ConditionalFact] + public void Can_only_override_lower_or_equal_source_sentinel() + { + var builder = CreateInternalPropertyBuilder(); + var metadata = builder.Metadata; + + Assert.NotNull(builder.HasSentinel(1, ConfigurationSource.DataAnnotation)); + Assert.NotNull(builder.HasSentinel(2, ConfigurationSource.DataAnnotation)); + + Assert.Equal(2, metadata.Sentinel); + + Assert.Null(builder.HasSentinel(1, ConfigurationSource.Convention)); + Assert.Equal(2, metadata.Sentinel); + } + + [ConditionalFact] + public void Can_only_override_existing_sentinel_value_explicitly() + { + var metadata = CreateProperty(); + metadata.SetSentinel(1, ConfigurationSource.Explicit); + var builder = metadata.Builder; + + Assert.NotNull(builder.HasSentinel(1, ConfigurationSource.DataAnnotation)); + Assert.Null(builder.HasSentinel(2, ConfigurationSource.DataAnnotation)); + + Assert.Equal(1, metadata.Sentinel); + + Assert.NotNull(builder.HasSentinel(2, ConfigurationSource.Explicit)); + Assert.Equal(2, metadata.Sentinel); + } + [ConditionalFact] public void Can_only_override_lower_or_equal_source_Precision() { diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs index e00358d5610..136f94c24d5 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs @@ -471,6 +471,9 @@ public override TestPropertyBuilder IsRequired(bool isRequired = true public override TestPropertyBuilder HasMaxLength(int maxLength) => Wrap(PropertyBuilder.HasMaxLength(maxLength)); + public override TestPropertyBuilder HasSentinel(object? sentinel) + => Wrap(PropertyBuilder.HasSentinel(sentinel)); + public override TestPropertyBuilder HasPrecision(int precision) => Wrap(PropertyBuilder.HasPrecision(precision)); diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs index e3e88baa4a3..5fa2ad3385d 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs @@ -551,6 +551,9 @@ public override TestPropertyBuilder IsRequired(bool isRequired = true public override TestPropertyBuilder HasMaxLength(int maxLength) => Wrap(PropertyBuilder.HasMaxLength(maxLength)); + public override TestPropertyBuilder HasSentinel(object? sentinel) + => Wrap(PropertyBuilder.HasSentinel(sentinel)); + public override TestPropertyBuilder HasPrecision(int precision) => Wrap(PropertyBuilder.HasPrecision(precision)); diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs index 1e42c728396..80c7aaf02dc 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs @@ -374,6 +374,7 @@ public abstract class TestPropertyBuilder public abstract TestPropertyBuilder HasAnnotation(string annotation, object? value); public abstract TestPropertyBuilder IsRequired(bool isRequired = true); public abstract TestPropertyBuilder HasMaxLength(int maxLength); + public abstract TestPropertyBuilder HasSentinel(object? sentinel); public abstract TestPropertyBuilder HasPrecision(int precision); public abstract TestPropertyBuilder HasPrecision(int precision, int scale); public abstract TestPropertyBuilder IsUnicode(bool unicode = true); diff --git a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs index ea60bd5b12f..edadba300ea 100644 --- a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs @@ -1437,6 +1437,65 @@ public virtual void Can_set_max_length_for_property_type() Assert.Equal(100, entityType.FindProperty("Bottom").GetMaxLength()); } + [ConditionalFact] + public virtual void Can_set_sentinel_for_properties() + { + var modelBuilder = CreateModelBuilder(); + + modelBuilder.Entity( + b => + { + b.Property(e => e.Up).HasSentinel(1); + b.Property(e => e.Down).HasSentinel("100"); + b.Property("Charm").HasSentinel(-1); + b.Property("Strange").HasSentinel("-1"); + b.Property("Top").HasSentinel(77); + b.Property("Bottom").HasSentinel("100"); + }); + + var model = modelBuilder.FinalizeModel(); + var entityType = model.FindEntityType(typeof(Quarks))!; + + Assert.Equal(0, entityType.FindProperty(Customer.IdProperty.Name)!.Sentinel); + Assert.Equal(1, entityType.FindProperty("Up")!.Sentinel); + Assert.Equal("100", entityType.FindProperty("Down")!.Sentinel); + Assert.Equal(-1, entityType.FindProperty("Charm")!.Sentinel); + Assert.Equal("-1", entityType.FindProperty("Strange")!.Sentinel); + Assert.Equal(77, entityType.FindProperty("Top")!.Sentinel); + Assert.Equal("100", entityType.FindProperty("Bottom")!.Sentinel); + } + + [ConditionalFact] + public virtual void Can_set_sentinel_for_property_type() + { + var modelBuilder = CreateModelBuilder( + c => + { + c.Properties().HaveSentinel(-1); + c.Properties().HaveSentinel("100"); + }); + + modelBuilder.Entity( + b => + { + b.Property("Charm"); + b.Property("Strange"); + b.Property("Top"); + b.Property("Bottom"); + }); + + var model = modelBuilder.FinalizeModel(); + var entityType = model.FindEntityType(typeof(Quarks))!; + + Assert.Equal(-1, entityType.FindProperty(Customer.IdProperty.Name)!.Sentinel); + Assert.Equal(-1, entityType.FindProperty("Up")!.Sentinel); + Assert.Equal("100", entityType.FindProperty("Down")!.Sentinel); + Assert.Equal(-1, entityType.FindProperty("Charm")!.Sentinel); + Assert.Equal("100", entityType.FindProperty("Strange")!.Sentinel); + Assert.Equal(-1, entityType.FindProperty("Top")!.Sentinel); + Assert.Equal("100", entityType.FindProperty("Bottom")!.Sentinel); + } + [ConditionalFact] public virtual void Can_set_unbounded_max_length_for_property_type() { @@ -1825,6 +1884,7 @@ public virtual void PropertyBuilder_methods_can_be_chained() .ValueGeneratedOnUpdate() .IsUnicode() .HasMaxLength(100) + .HasSentinel(null) .HasPrecision(10, 1) .HasValueGenerator() .HasValueGenerator(typeof(CustomValueGenerator))