Skip to content

Commit

Permalink
ModelBuilder API for sentinels
Browse files Browse the repository at this point in the history
Part of #701
  • Loading branch information
ajcvickers committed May 2, 2023
1 parent bd59d2c commit 27aaca7
Show file tree
Hide file tree
Showing 22 changed files with 409 additions and 162 deletions.
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
10 changes: 10 additions & 0 deletions src/EFCore/Metadata/Builders/TypeMappingConfigurationBuilder`.cs
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

0 comments on commit 27aaca7

Please sign in to comment.