Skip to content

Commit

Permalink
Add API to configure ValueConverter and ValueComparer types
Browse files Browse the repository at this point in the history
Part of #1906
  • Loading branch information
AndriySvyryd committed Apr 22, 2021
1 parent bc66120 commit b02e139
Show file tree
Hide file tree
Showing 21 changed files with 736 additions and 40 deletions.
2 changes: 0 additions & 2 deletions src/EFCore/ChangeTracking/Internal/ValueComparerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ public static ValueComparer ToNonNullNullableComparer(this ValueComparer compare

private sealed class NonNullNullableValueComparer<T> : ValueComparer<T>
{
#pragma warning disable CA1061 // Do not hide base class methods
public NonNullNullableValueComparer(
#pragma warning restore CA1061 // Do not hide base class methods
LambdaExpression equalsExpression,
LambdaExpression hashCodeExpression,
LambdaExpression snapshotExpression)
Expand Down
59 changes: 57 additions & 2 deletions src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,35 @@ bool CanSetValueGeneratorFactory(
/// </returns>
bool CanSetConversion(Type? providerClrType, bool fromDataAnnotation = false);

/// <summary>
/// Configures the property so that the property value is converted to and from the database
/// using the given <see cref="ValueConverter" />.
/// </summary>
/// <param name="converterType">
/// A type that derives from <see cref="ValueConverter"/>,
/// or <see langword="null" /> to remove any previously set converter.
/// </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? HasConverter(Type? converterType, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the <see cref="ValueConverter" /> can be configured for this property
/// from the current configuration source.
/// </summary>
/// <param name="converterType">
/// A type that derives from <see cref="ValueConverter"/>,
/// or <see langword="null" /> to remove any previously set converter.
/// </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns>
/// <see langword="true" /> if the <see cref="ValueConverter" /> can be configured for this property.
/// </returns>
bool CanSetConverter(Type? converterType, bool fromDataAnnotation = false);

/// <summary>
/// Configures the <see cref="CoreTypeMapping" /> for this property.
/// </summary>
Expand Down Expand Up @@ -421,8 +450,7 @@ bool CanSetValueGeneratorFactory(
/// <param name="comparer"> The comparer, or <see langword="null" /> to remove any previously set comparer. </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.
/// The same builder instance if the configuration was applied, <see langword="null" /> otherwise.
/// </returns>
IConventionPropertyBuilder? HasValueComparer(ValueComparer? comparer, bool fromDataAnnotation = false);

Expand All @@ -437,6 +465,33 @@ bool CanSetValueGeneratorFactory(
/// </returns>
bool CanSetValueComparer(ValueComparer? comparer, bool fromDataAnnotation = false);

/// <summary>
/// Configures the <see cref="ValueComparer" /> for this property.
/// </summary>
/// <param name="comparerType">
/// A type that derives from <see cref="ValueComparer"/>,
/// or <see langword="null" /> to remove any previously set comparer.
/// </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? HasValueComparer(Type? comparerType, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the given <see cref="ValueComparer" />
/// can be configured for this property from the current configuration source.
/// </summary>
/// <param name="comparerType">
/// A type that derives from <see cref="ValueComparer"/>,
/// or <see langword="null" /> to remove any previously set comparer.
/// </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns>
/// <see langword="true" /> if the given <see cref="ValueComparer" /> can be configured for this property.
/// </returns>
bool CanSetValueComparer(Type? comparerType, bool fromDataAnnotation = false);

/// <summary>
/// Configures the <see cref="ValueComparer" /> to be used for key comparisons for this property.
/// </summary>
Expand Down
27 changes: 27 additions & 0 deletions src/EFCore/Metadata/Builders/PropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,33 @@ public virtual PropertyBuilder HasConversion(ValueConverter? converter, ValueCom
return this;
}

/// <summary>
/// Configures the property so that the property value is converted to and from the database
/// using the given <see cref="ValueConverter" />.
/// </summary>
/// <typeparam name="TConverter"> A type that derives from <see cref="ValueConverter"/>. </typeparam>
/// <typeparam name="TComparer"> A type that derives from <see cref="ValueComparer"/>. </typeparam>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public virtual PropertyBuilder HasConversion<TConverter, TComparer>()
where TConverter : ValueConverter
where TComparer : ValueComparer
=> HasConversion(typeof(TConverter), typeof(TComparer));

/// <summary>
/// Configures the property so that the property value is converted to and from the database
/// using the given <see cref="ValueConverter" />.
/// </summary>
/// <param name="converterType"> A type that derives from <see cref="ValueConverter"/>. </param>
/// <param name="comparerType"> A type that derives from <see cref="ValueComparer"/>. </param>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public virtual PropertyBuilder HasConversion(Type? converterType, Type? comparerType)
{
Builder.HasConverter(converterType, ConfigurationSource.Explicit);
Builder.HasValueComparer(comparerType, ConfigurationSource.Explicit);

return this;
}

#region Hidden System.Object members

/// <summary>
Expand Down
54 changes: 38 additions & 16 deletions src/EFCore/Metadata/Builders/PropertyBuilder`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,26 @@ public PropertyBuilder(IMutableProperty property)
public new virtual PropertyBuilder<TProperty> HasField(string fieldName)
=> (PropertyBuilder<TProperty>)base.HasField(fieldName);

/// <summary>
/// <para>
/// Sets the <see cref="PropertyAccessMode" /> to use for this property.
/// </para>
/// <para>
/// By default, the backing field, if one is found by convention or has been specified, is used when
/// new objects are constructed, typically when entities are queried from the database.
/// Properties are used for all other accesses. Calling this method will change that behavior
/// for this property as described in the <see cref="PropertyAccessMode" /> enum.
/// </para>
/// <para>
/// Calling this method overrides for this property any access mode that was set on the
/// entity type or model.
/// </para>
/// </summary>
/// <param name="propertyAccessMode"> The <see cref="PropertyAccessMode" /> to use for this property. </param>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public new virtual PropertyBuilder<TProperty> UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
=> (PropertyBuilder<TProperty>)base.UsePropertyAccessMode(propertyAccessMode);

/// <summary>
/// Configures the property so that the property value is converted to the given type before
/// writing to the database and converted back when reading from the database.
Expand Down Expand Up @@ -439,23 +459,25 @@ public virtual PropertyBuilder<TProperty> HasConversion<TProvider>(
=> (PropertyBuilder<TProperty>)base.HasConversion(converter, valueComparer);

/// <summary>
/// <para>
/// Sets the <see cref="PropertyAccessMode" /> to use for this property.
/// </para>
/// <para>
/// By default, the backing field, if one is found by convention or has been specified, is used when
/// new objects are constructed, typically when entities are queried from the database.
/// Properties are used for all other accesses. Calling this method will change that behavior
/// for this property as described in the <see cref="PropertyAccessMode" /> enum.
/// </para>
/// <para>
/// Calling this method overrides for this property any access mode that was set on the
/// entity type or model.
/// </para>
/// Configures the property so that the property value is converted to and from the database
/// using the given <see cref="ValueConverter" />.
/// </summary>
/// <param name="propertyAccessMode"> The <see cref="PropertyAccessMode" /> to use for this property. </param>
/// <typeparam name="TConverter"> A type that derives from <see cref="ValueConverter"/>. </typeparam>
/// <typeparam name="TComparer"> A type that derives from <see cref="ValueComparer"/>. </typeparam>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public new virtual PropertyBuilder<TProperty> UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
=> (PropertyBuilder<TProperty>)base.UsePropertyAccessMode(propertyAccessMode);
public new virtual PropertyBuilder<TProperty> HasConversion<TConverter, TComparer>()
where TConverter : ValueConverter
where TComparer : ValueComparer
=> (PropertyBuilder<TProperty>)base.HasConversion<TConverter, TComparer>();

/// <summary>
/// Configures the property so that the property value is converted to and from the database
/// using the given <see cref="ValueConverter" />.
/// </summary>
/// <param name="converterType"> A type that derives from <see cref="ValueConverter"/>. </param>
/// <param name="comparerType"> A type that derives from <see cref="ValueComparer"/>. </param>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public new virtual PropertyBuilder<TProperty> HasConversion(Type? converterType, Type? comparerType)
=> (PropertyBuilder<TProperty>)base.HasConversion(converterType, comparerType);
}
}
4 changes: 4 additions & 0 deletions src/EFCore/Metadata/Conventions/SlimModelConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,12 @@ protected virtual void ProcessPropertyAnnotations(
annotations.Remove(CoreAnnotationNames.Precision);
annotations.Remove(CoreAnnotationNames.Scale);
annotations.Remove(CoreAnnotationNames.ProviderClrType);
annotations.Remove(CoreAnnotationNames.ValueGeneratorFactory);
annotations.Remove(CoreAnnotationNames.ValueGeneratorFactoryType);
annotations.Remove(CoreAnnotationNames.ValueConverter);
annotations.Remove(CoreAnnotationNames.ValueConverterType);
annotations.Remove(CoreAnnotationNames.ValueComparer);
annotations.Remove(CoreAnnotationNames.ValueComparerType);
}
}

Expand Down
22 changes: 21 additions & 1 deletion src/EFCore/Metadata/IConventionProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,17 @@ bool IsImplicitlyCreated()
/// <param name="converter"> The converter, or <see langword="null" /> to remove any previously set converter. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> The configured value. </returns>
ValueConverter? SetValueConverter(ValueConverter? converter, bool fromDataAnnotation = false);
ValueConverter? SetValueConverter(ValueConverter? converter, bool fromDataAnnotation = false);

/// <summary>
/// Sets the custom <see cref="ValueConverter" /> for this property.
/// </summary>
/// <param name="converterType">
/// A type that derives from <see cref="ValueConverter"/>, or <see langword="null" /> to remove any previously set converter.
/// </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> The configured value. </returns>
Type? SetValueConverter(Type? converterType, bool fromDataAnnotation = false);

/// <summary>
/// Returns the configuration source for <see cref="IReadOnlyProperty.GetValueConverter" />.
Expand Down Expand Up @@ -372,6 +382,16 @@ bool IsImplicitlyCreated()
/// <returns> The configured value. </returns>
ValueComparer? SetValueComparer(ValueComparer? comparer, bool fromDataAnnotation = false);

/// <summary>
/// Sets the custom <see cref="ValueComparer" /> for this property.
/// </summary>
/// <param name="comparerType">
/// A type that derives from <see cref="ValueComparer"/>, or <see langword="null" /> to remove any previously set comparer.
/// </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> The configured value. </returns>
Type? SetValueComparer(Type? comparerType, bool fromDataAnnotation = false);

/// <summary>
/// Returns the configuration source for <see cref="IReadOnlyProperty.GetValueComparer" />.
/// </summary>
Expand Down
16 changes: 16 additions & 0 deletions src/EFCore/Metadata/IMutableProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,14 @@ public interface IMutableProperty : IReadOnlyProperty, IMutablePropertyBase
/// <param name="converter"> The converter, or <see langword="null" /> to remove any previously set converter. </param>
void SetValueConverter(ValueConverter? converter);

/// <summary>
/// Sets the custom <see cref="ValueConverter" /> for this property.
/// </summary>
/// <param name="converterType">
/// A type that derives from <see cref="ValueConverter"/>, or <see langword="null" /> to remove any previously set converter.
/// </param>
void SetValueConverter(Type? converterType);

/// <summary>
/// Sets the type that the property value will be converted to before being sent to the database provider.
/// </summary>
Expand All @@ -227,5 +235,13 @@ public interface IMutableProperty : IReadOnlyProperty, IMutablePropertyBase
/// </summary>
/// <param name="comparer"> The comparer, or <see langword="null" /> to remove any previously set comparer. </param>
void SetValueComparer(ValueComparer? comparer);

/// <summary>
/// Sets the custom <see cref="ValueComparer" /> for this property.
/// </summary>
/// <param name="comparerType">
/// A type that derives from <see cref="ValueComparer"/>, or <see langword="null" /> to remove any previously set comparer.
/// </param>
void SetValueComparer(Type? comparerType);
}
}
18 changes: 18 additions & 0 deletions src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ public static class CoreAnnotationNames
/// </summary>
public const string ValueConverter = "ValueConverter";

/// <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 ValueConverterType = "ValueConverterType";

/// <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 All @@ -134,6 +142,14 @@ public static class CoreAnnotationNames
/// </summary>
public const string ValueComparer = "ValueComparer";

/// <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 ValueComparerType = "ValueComparerType";

/// <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 @@ -295,7 +311,9 @@ public static class CoreAnnotationNames
DiscriminatorMappingComplete,
DiscriminatorValue,
ValueConverter,
ValueConverterType,
ValueComparer,
ValueComparerType,
#pragma warning disable 618
KeyValueComparer,
StructuralValueComparer,
Expand Down
Loading

0 comments on commit b02e139

Please sign in to comment.