Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ModelBuilder API for sentinels #30809

Merged
merged 1 commit into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,24 @@ public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
/// <returns><see langword="true" /> if the maximum length of data allowed can be set for this property.</returns>
bool CanSetMaxLength(int? maxLength, bool fromDataAnnotation = false);

/// <summary>
/// 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.
/// </summary>
/// <param name="sentinel">The sentinel value.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The same builder instance if the configuration was applied, <see langword="null" /> otherwise.</returns>
IConventionPropertyBuilder? HasSentinel(object? sentinel, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the sentinel can be set for this property from the current configuration source.
/// </summary>
/// <param name="sentinel">The sentinel value.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns><see langword="true" /> if the sentinel can be set for this property.</returns>
bool CanSetSentinel(object? sentinel, bool fromDataAnnotation = false);

/// <summary>
/// Configures whether the property as capable of persisting unicode characters.
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions src/EFCore/Metadata/Builders/PropertiesConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ public virtual PropertiesConfigurationBuilder HaveMaxLength(int maxLength)
return this;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="sentinel">The sentinel value.</param>
/// <returns>The same builder instance so that multiple configuration calls can be chained.</returns>
public virtual PropertiesConfigurationBuilder HaveSentinel(object? sentinel)
{
Configuration.SetSentinel(sentinel);

return this;
}

/// <summary>
/// Configures the precision and scale of the property.
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions src/EFCore/Metadata/Builders/PropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ public virtual PropertyBuilder HasMaxLength(int maxLength)
return this;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="sentinel">The sentinel value.</param>
/// <returns>The same builder instance if the configuration was applied, <see langword="null" /> otherwise.</returns>
public virtual PropertyBuilder HasSentinel(object? sentinel)
{
Builder.HasSentinel(sentinel, ConfigurationSource.Explicit);

return this;
}

/// <summary>
/// Configures the precision and scale of the property.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions src/EFCore/Metadata/Builders/PropertyBuilder`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ public PropertyBuilder(IMutableProperty property)
public new virtual PropertyBuilder<TProperty> HasMaxLength(int maxLength)
=> (PropertyBuilder<TProperty>)base.HasMaxLength(maxLength);

/// <summary>
/// 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.
/// </summary>
/// <param name="sentinel">The sentinel value.</param>
/// <returns>The same builder instance if the configuration was applied, <see langword="null" /> otherwise.</returns>
public new virtual PropertyBuilder<TProperty> HasSentinel(object? sentinel)
=> (PropertyBuilder<TProperty>)base.HasSentinel(sentinel);

/// <summary>
/// Configures the precision and scale of the property.
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ public virtual TypeMappingConfigurationBuilder HasMaxLength(int maxLength)
return this;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="sentinel">The sentinel value.</param>
/// <returns>The same builder instance if the configuration was applied, <see langword="null" /> otherwise.</returns>
public virtual TypeMappingConfigurationBuilder HasSentinel(object? sentinel)
{
Configuration.SetSentinel(sentinel);

return this;
}

/// <summary>
/// Configures the precision and scale of the property.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ public TypeMappingConfigurationBuilder(PropertyConfiguration scalar)
public new virtual TypeMappingConfigurationBuilder<TProperty> HasMaxLength(int maxLength)
=> (TypeMappingConfigurationBuilder<TProperty>)base.HasMaxLength(maxLength);

/// <summary>
/// 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.
/// </summary>
/// <param name="sentinel">The sentinel value.</param>
/// <returns>The same builder instance if the configuration was applied, <see langword="null" /> otherwise.</returns>
public new virtual TypeMappingConfigurationBuilder<TProperty> HasSentinel(object? sentinel)
=> (TypeMappingConfigurationBuilder<TProperty>)base.HasSentinel(sentinel);

/// <summary>
/// Configures the precision and scale of the property.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ public static class CoreAnnotationNames
/// </summary>
public const string MaxLength = "MaxLength";

/// <summary>
/// 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.
/// </summary>
public const string Sentinel = "Sentinel";

/// <summary>
/// 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
Expand Down Expand Up @@ -317,6 +325,7 @@ public static class CoreAnnotationNames
public static readonly ISet<string> AllNames = new HashSet<string>
{
MaxLength,
Sentinel,
Precision,
Scale,
Unicode,
Expand Down
46 changes: 46 additions & 0 deletions src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,34 @@ public virtual bool CanSetMaxLength(int? maxLength, ConfigurationSource? configu
=> configurationSource.Overrides(Metadata.GetMaxLengthConfigurationSource())
|| Metadata.GetMaxLength() == maxLength;

/// <summary>
/// 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.
/// </summary>
public virtual InternalPropertyBuilder? HasSentinel(object? sentinel, ConfigurationSource configurationSource)
{
if (CanSetSentinel(sentinel, configurationSource))
{
Metadata.SetSentinel(sentinel, configurationSource);

return this;
}

return null;
}

/// <summary>
/// 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.
/// </summary>
public virtual bool CanSetSentinel(object? sentinel, ConfigurationSource? configurationSource)
=> configurationSource.Overrides(Metadata.GetSentinelConfigurationSource())
|| Equals(Metadata.Sentinel, sentinel);

/// <summary>
/// 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
Expand Down Expand Up @@ -1013,6 +1041,24 @@ bool IConventionPropertyBaseBuilder.CanSetPropertyAccessMode(PropertyAccessMode?
bool IConventionPropertyBuilder.CanSetMaxLength(int? maxLength, bool fromDataAnnotation)
=> CanSetMaxLength(maxLength, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
/// 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.
/// </summary>
IConventionPropertyBuilder? IConventionPropertyBuilder.HasSentinel(object? sentinel, bool fromDataAnnotation)
=> HasSentinel(sentinel, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
/// 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.
/// </summary>
bool IConventionPropertyBuilder.CanSetSentinel(object? sentinel, bool fromDataAnnotation)
=> CanSetSentinel(sentinel, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
/// 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
Expand Down
26 changes: 21 additions & 5 deletions src/EFCore/Metadata/Internal/PropertyConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -121,6 +119,24 @@ public virtual void SetMaxLength(int? maxLength)
this[CoreAnnotationNames.MaxLength] = maxLength;
}

/// <summary>
/// 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.
/// </summary>
public virtual int? GetSentinel()
=> (int?)this[CoreAnnotationNames.Sentinel];

/// <summary>
/// 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.
/// </summary>
public virtual void SetSentinel(object? sentinel)
=> this[CoreAnnotationNames.Sentinel] = sentinel;

/// <summary>
/// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity<CruiserWithSentinel>(
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);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity<CruiserWithSentinel>(
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);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<Blog>(
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));
});
}
}
Loading