diff --git a/src/EFCore/Metadata/Builders/PropertyBuilder.cs b/src/EFCore/Metadata/Builders/PropertyBuilder.cs index 848a3f29809..594397e7fda 100644 --- a/src/EFCore/Metadata/Builders/PropertyBuilder.cs +++ b/src/EFCore/Metadata/Builders/PropertyBuilder.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; @@ -399,6 +400,46 @@ public virtual PropertyBuilder HasConversion([CanBeNull] ValueConverter converte return this; } + /// + /// 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. + /// + /// The comparer to use for values before conversion. + /// The type to convert to and from. + /// The same builder instance so that multiple configuration calls can be chained. + public virtual PropertyBuilder HasConversion([CanBeNull] ValueComparer valueComparer) + => HasConversion(typeof(TProvider), valueComparer); + + /// + /// 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. + /// + /// The type to convert to and from. + /// The comparer to use for values before conversion. + /// The same builder instance so that multiple configuration calls can be chained. + public virtual PropertyBuilder HasConversion([CanBeNull] Type providerClrType, [CanBeNull] ValueComparer valueComparer) + { + Builder.HasConversion(providerClrType, ConfigurationSource.Explicit); + Builder.HasValueComparer(valueComparer, ConfigurationSource.Explicit); + + return this; + } + + /// + /// Configures the property so that the property value is converted to and from the database + /// using the given . + /// + /// The converter to use. + /// The comparer to use for values before conversion. + /// The same builder instance so that multiple configuration calls can be chained. + public virtual PropertyBuilder HasConversion([CanBeNull] ValueConverter converter, [CanBeNull] ValueComparer valueComparer) + { + Builder.HasConversion(converter, ConfigurationSource.Explicit); + Builder.HasValueComparer(valueComparer, ConfigurationSource.Explicit); + + return this; + } + #region Hidden System.Object members /// diff --git a/src/EFCore/Metadata/Builders/PropertyBuilder`.cs b/src/EFCore/Metadata/Builders/PropertyBuilder`.cs index 6134bc9374c..573fdb61f66 100644 --- a/src/EFCore/Metadata/Builders/PropertyBuilder`.cs +++ b/src/EFCore/Metadata/Builders/PropertyBuilder`.cs @@ -4,6 +4,7 @@ using System; using System.Linq.Expressions; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Utilities; @@ -306,6 +307,72 @@ public virtual PropertyBuilder HasConversion([CanBeNull] V public new virtual PropertyBuilder HasConversion([CanBeNull] ValueConverter converter) => (PropertyBuilder)base.HasConversion(converter); + /// + /// 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. + /// + /// The type to convert to and from. + /// The comparer to use for values before conversion. + /// The same builder instance so that multiple configuration calls can be chained. + public new virtual PropertyBuilder HasConversion([CanBeNull] ValueComparer valueComparer) + => (PropertyBuilder)base.HasConversion(valueComparer); + + /// + /// 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. + /// + /// The type to convert to and from. + /// The comparer to use for values before conversion. + /// The same builder instance so that multiple configuration calls can be chained. + public new virtual PropertyBuilder HasConversion( + [CanBeNull] Type providerClrType, + [CanBeNull] ValueComparer valueComparer) + => (PropertyBuilder)base.HasConversion(providerClrType, valueComparer); + + /// + /// Configures the property so that the property value is converted to and from the database + /// using the given conversion expressions. + /// + /// The store type generated by the conversions. + /// An expression to convert objects when writing data to the store. + /// An expression to convert objects when reading data from the store. + /// The comparer to use for values before conversion. + /// The same builder instance so that multiple configuration calls can be chained. + public virtual PropertyBuilder HasConversion( + [NotNull] Expression> convertToProviderExpression, + [NotNull] Expression> convertFromProviderExpression, + [CanBeNull] ValueComparer valueComparer) + => HasConversion( + new ValueConverter( + Check.NotNull(convertToProviderExpression, nameof(convertToProviderExpression)), + Check.NotNull(convertFromProviderExpression, nameof(convertFromProviderExpression))), + valueComparer); + + /// + /// Configures the property so that the property value is converted to and from the database + /// using the given . + /// + /// The store type generated by the converter. + /// The converter to use. + /// The comparer to use for values before conversion. + /// The same builder instance so that multiple configuration calls can be chained. + public virtual PropertyBuilder HasConversion( + [CanBeNull] ValueConverter converter, + [CanBeNull] ValueComparer valueComparer) + => HasConversion((ValueConverter)converter, valueComparer); + + /// + /// Configures the property so that the property value is converted to and from the database + /// using the given . + /// + /// The converter to use. + /// The comparer to use for values before conversion. + /// The same builder instance so that multiple configuration calls can be chained. + public new virtual PropertyBuilder HasConversion( + [CanBeNull] ValueConverter converter, + [CanBeNull] ValueComparer valueComparer) + => (PropertyBuilder)base.HasConversion(converter, valueComparer); + /// /// /// Sets the to use for this property. diff --git a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs index a6281c93e1d..590964176fa 100644 --- a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs +++ b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs @@ -998,15 +998,20 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity( b => { - var property = b.Property(e => e.StringKeyDataTypeId) - .HasConversion(v => "KeyValue=" + v, v => v.Substring(9)).Metadata; - - property.SetValueComparer(caseInsensitiveComparer); + b.Property(e => e.StringKeyDataTypeId) + .HasConversion( + v => "KeyValue=" + v, + v => v.Substring(9), + caseInsensitiveComparer); }); modelBuilder.Entity( b => { + var bytesComparer = new ValueComparer( + (v1, v2) => v1.SequenceEqual(v2), + v => v.GetHashCode()); + b.Property(e => e.String3) .HasConversion( new ValueConverter( @@ -1020,33 +1025,28 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con .HasConversion( new ValueConverter( v => v.Reverse().Concat(new byte[] { 4, 20 }).ToArray(), - v => v.Reverse().Skip(2).ToArray())) + v => v.Reverse().Skip(2).ToArray()), + bytesComparer) .HasMaxLength(7); b.Property(e => e.ByteArray9000) .HasConversion( - BytesToStringConverter.DefaultInfo.Create()) + BytesToStringConverter.DefaultInfo.Create(), + bytesComparer) .HasMaxLength(LongStringLength * 2); - - var bytesComparer = new ValueComparer( - (v1, v2) => v1.SequenceEqual(v2), - v => v.GetHashCode()); - - b.Property(e => e.ByteArray5).Metadata.SetValueComparer(bytesComparer); - b.Property(e => e.ByteArray9000).Metadata.SetValueComparer(bytesComparer); }); modelBuilder.Entity( b => { - b.Property(e => e.Strings).HasConversion(v => string.Join(",", v), v => v.Split(new[] { ',' }).ToList()); - b.Property(e => e.Id).ValueGeneratedNever(); - - var comparer = new ValueComparer>( - (v1, v2) => v1.SequenceEqual(v2), - v => v.GetHashCode()); + b.Property(e => e.Strings).HasConversion( + v => string.Join(",", v), + v => v.Split(new[] { ',' }).ToList(), + new ValueComparer>( + (v1, v2) => v1.SequenceEqual(v2), + v => v.GetHashCode())); - b.Property(e => e.Strings).Metadata.SetValueComparer(comparer); + b.Property(e => e.Id).ValueGeneratedNever(); }); modelBuilder.Entity( @@ -1063,14 +1063,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con b.HasKey(c => c.CounterId); b.Property(c => c.Discriminator).HasConversion( d => StringToDictionarySerializer.Serialize(d), - json => StringToDictionarySerializer.Deserialize(json)); - - var comparer = new ValueComparer>( - (v1, v2) => v1.SequenceEqual(v2), - v => v.GetHashCode(), - v => (IDictionary)new Dictionary(v)); - - b.Property(e => e.Discriminator).Metadata.SetValueComparer(comparer); + json => StringToDictionarySerializer.Deserialize(json), + new ValueComparer>( + (v1, v2) => v1.SequenceEqual(v2), + v => v.GetHashCode(), + v => (IDictionary)new Dictionary(v))); }); var urlConverter = new UrlSchemeRemover(); @@ -1127,8 +1124,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { b.Property(e => e.Tags).HasConversion( c => string.Join(",", c), - s => s.Split(',', StringSplitOptions.None).ToList()).Metadata - .SetValueComparer(new ValueComparer>(favorStructuralComparisons: true)); + s => s.Split(',', StringSplitOptions.None).ToList(), + new ValueComparer>(favorStructuralComparisons: true)); b.HasData(new CollectionScalar { @@ -1140,8 +1137,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity( b => { - b.Property(e => e.Roles).HasConversion(new RolesToStringConveter()).Metadata - .SetValueComparer(new ValueComparer>(favorStructuralComparisons: true)); + b.Property(e => e.Roles).HasConversion( + new RolesToStringConveter(), + new ValueComparer>(favorStructuralComparisons: true)); b.HasData(new CollectionEnum {