diff --git a/src/EFCore.Abstractions/PrecisionAttribute.cs b/src/EFCore.Abstractions/PrecisionAttribute.cs
new file mode 100644
index 00000000000..f7ba482b7d3
--- /dev/null
+++ b/src/EFCore.Abstractions/PrecisionAttribute.cs
@@ -0,0 +1,62 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+
+namespace Microsoft.EntityFrameworkCore
+{
+ ///
+ /// Configures the precision of data that is allowed in this property.
+ /// For example, if the property is a
+ /// then this is the maximum number of digits.
+ ///
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
+ public class PrecisionAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The precision of the property.
+ /// The scale of the property.
+ public PrecisionAttribute(int precision, int scale)
+ {
+ if (precision < 0)
+ {
+ throw new ArgumentException(AbstractionsStrings.ArgumentIsNegativeNumber(nameof(precision)));
+ }
+
+ if (scale < 0)
+ {
+ throw new ArgumentException(AbstractionsStrings.ArgumentIsNegativeNumber(nameof(scale)));
+ }
+
+ Precision = precision;
+ Scale = scale;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The precision of the property.
+ public PrecisionAttribute(int precision)
+ {
+ if (precision < 0)
+ {
+ throw new ArgumentException(AbstractionsStrings.ArgumentIsNegativeNumber(nameof(precision)));
+ }
+
+ Precision = precision;
+ }
+
+ ///
+ /// The precision of the property.
+ ///
+ public int Precision { get; }
+
+ ///
+ /// The scale of the property.
+ ///
+ public int? Scale { get; }
+ }
+}
diff --git a/src/EFCore.Abstractions/Properties/AbstractionsStrings.Designer.cs b/src/EFCore.Abstractions/Properties/AbstractionsStrings.Designer.cs
index e322de38345..0ca04113d9e 100644
--- a/src/EFCore.Abstractions/Properties/AbstractionsStrings.Designer.cs
+++ b/src/EFCore.Abstractions/Properties/AbstractionsStrings.Designer.cs
@@ -29,6 +29,14 @@ public static string ArgumentIsEmpty([CanBeNull] object argumentName)
GetString("ArgumentIsEmpty", nameof(argumentName)),
argumentName);
+ ///
+ /// The number argument '{argumentName}' cannot be negative number.
+ ///
+ public static string ArgumentIsNegativeNumber([CanBeNull] object argumentName)
+ => string.Format(
+ GetString("ArgumentIsNegativeNumber", nameof(argumentName)),
+ argumentName);
+
///
/// The collection argument '{argumentName}' must not contain any empty elements.
///
diff --git a/src/EFCore.Abstractions/Properties/AbstractionsStrings.resx b/src/EFCore.Abstractions/Properties/AbstractionsStrings.resx
index ad0e3c343b3..7bdf6f59663 100644
--- a/src/EFCore.Abstractions/Properties/AbstractionsStrings.resx
+++ b/src/EFCore.Abstractions/Properties/AbstractionsStrings.resx
@@ -120,6 +120,9 @@
The string argument '{argumentName}' cannot be empty.
+
+ The number argument '{argumentName}' cannot be negative number.
+
The collection argument '{argumentName}' must not contain any empty elements.
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
index d66e3a818fe..3e21d3c4f05 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
@@ -595,25 +595,25 @@ private void GenerateProperty(IProperty property, bool useDataAnnotations)
lines.Add(
$".{nameof(PropertyBuilder.HasMaxLength)}({_code.Literal(maxLength.Value)})");
}
- }
- var precision = property.GetPrecision();
- var scale = property.GetScale();
- if (precision != null && scale != null && scale != 0)
- {
- lines.Add(
- $".{nameof(PropertyBuilder.HasPrecision)}({_code.Literal(precision.Value)}, {_code.Literal(scale.Value)})");
- }
- else if (precision != null)
- {
- lines.Add(
- $".{nameof(PropertyBuilder.HasPrecision)}({_code.Literal(precision.Value)})");
- }
+ var precision = property.GetPrecision();
+ var scale = property.GetScale();
+ if (precision != null && scale != null && scale != 0)
+ {
+ lines.Add(
+ $".{nameof(PropertyBuilder.HasPrecision)}({_code.Literal(precision.Value)}, {_code.Literal(scale.Value)})");
+ }
+ else if (precision != null)
+ {
+ lines.Add(
+ $".{nameof(PropertyBuilder.HasPrecision)}({_code.Literal(precision.Value)})");
+ }
- if (property.IsUnicode() != null)
- {
- lines.Add(
- $".{nameof(PropertyBuilder.IsUnicode)}({(property.IsUnicode() == false ? "false" : "")})");
+ if (property.IsUnicode() != null)
+ {
+ lines.Add(
+ $".{nameof(PropertyBuilder.IsUnicode)}({(property.IsUnicode() == false ? "false" : "")})");
+ }
}
var defaultValue = property.GetDefaultValue();
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.cs
index 8f70fa8a790..950844b6968 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.cs
@@ -293,6 +293,7 @@ protected virtual void GeneratePropertyDataAnnotations([NotNull] IProperty prope
GenerateColumnAttribute(property);
GenerateMaxLengthAttribute(property);
GenerateUnicodeAttribute(property);
+ GeneratePrecisionAttribute(property);
var annotations = _annotationCodeGenerator
.FilterIgnoredAnnotations(property.GetAnnotations())
@@ -390,6 +391,24 @@ private void GenerateUnicodeAttribute(IProperty property)
}
}
+ private void GeneratePrecisionAttribute(IProperty property)
+ {
+ var precision = property.GetPrecision();
+ if (precision.HasValue)
+ {
+ var precisionAttribute = new AttributeWriter(nameof(PrecisionAttribute));
+ precisionAttribute.AddParameter(_code.Literal(precision.Value));
+
+ var scale = property.GetScale();
+ if (scale.HasValue)
+ {
+ precisionAttribute.AddParameter(_code.Literal(scale.Value));
+ }
+
+ _sb.AppendLine(precisionAttribute.ToString());
+ }
+ }
+
///
/// 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/Builders/IConventionPropertyBuilder.cs b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
index 971be5eadde..f207465ff08 100644
--- a/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
@@ -182,6 +182,46 @@ public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
/// if the capability of persisting unicode characters can be configured for this property.
bool CanSetIsUnicode(bool? unicode, bool fromDataAnnotation = false);
+ ///
+ /// Configures the precision of the property.
+ ///
+ /// The precision of the property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the configuration was applied,
+ /// otherwise.
+ ///
+ IConventionPropertyBuilder HasPrecision(int? precision, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the precision of data allowed can be set for this property
+ /// from the current configuration source.
+ ///
+ /// The precision of the property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the precision of data allowed can be set for this property.
+ bool CanSetPrecision(int? precision, bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the scale of the property.
+ ///
+ /// The scale of the property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the configuration was applied,
+ /// otherwise.
+ ///
+ IConventionPropertyBuilder HasScale(int? scale, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the scale of data allowed can be set for this property
+ /// from the current configuration source.
+ ///
+ /// The scale of the property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the scale of data allowed can be set for this property.
+ bool CanSetScale(int? scale, bool fromDataAnnotation = false);
+
///
/// Configures whether this property can be modified before the entity is saved to the database.
///
diff --git a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs
index d4203620973..bf7c3273cca 100644
--- a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs
+++ b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs
@@ -114,6 +114,7 @@ public virtual ConventionSet CreateConventionSet()
var timestampAttributeConvention = new TimestampAttributeConvention(Dependencies);
var backingFieldAttributeConvention = new BackingFieldAttributeConvention(Dependencies);
var unicodeAttributeConvention = new UnicodeAttributeConvention(Dependencies);
+ var precisionAttributeConvention = new PrecisionAttributeConvention(Dependencies);
conventionSet.PropertyAddedConventions.Add(backingFieldAttributeConvention);
conventionSet.PropertyAddedConventions.Add(backingFieldConvention);
@@ -128,6 +129,7 @@ public virtual ConventionSet CreateConventionSet()
conventionSet.PropertyAddedConventions.Add(keyDiscoveryConvention);
conventionSet.PropertyAddedConventions.Add(foreignKeyPropertyDiscoveryConvention);
conventionSet.PropertyAddedConventions.Add(unicodeAttributeConvention);
+ conventionSet.PropertyAddedConventions.Add(precisionAttributeConvention);
conventionSet.EntityTypePrimaryKeyChangedConventions.Add(foreignKeyPropertyDiscoveryConvention);
conventionSet.EntityTypePrimaryKeyChangedConventions.Add(valueGeneratorConvention);
diff --git a/src/EFCore/Metadata/Conventions/PrecisionAttributeConvention.cs b/src/EFCore/Metadata/Conventions/PrecisionAttributeConvention.cs
new file mode 100644
index 00000000000..12e14aec393
--- /dev/null
+++ b/src/EFCore/Metadata/Conventions/PrecisionAttributeConvention.cs
@@ -0,0 +1,46 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Reflection;
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
+{
+ ///
+ /// A convention that configures the Precision based on the applied on the property.
+ ///
+ public class PrecisionAttributeConvention : PropertyAttributeConventionBase
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// Parameter object containing dependencies for this convention.
+ public PrecisionAttributeConvention([NotNull] ProviderConventionSetBuilderDependencies dependencies)
+ : base(dependencies)
+ {
+ }
+
+ ///
+ /// Called after a property is added to the entity type with an attribute on the associated CLR property or field.
+ ///
+ /// The builder for the property.
+ /// The attribute.
+ /// The member that has the attribute.
+ /// Additional information associated with convention execution.
+ protected override void ProcessPropertyAdded(
+ IConventionPropertyBuilder propertyBuilder,
+ PrecisionAttribute attribute,
+ MemberInfo clrMember,
+ IConventionContext context)
+ {
+ propertyBuilder.HasPrecision(attribute.Precision, fromDataAnnotation: true);
+
+ if (attribute.Scale.HasValue)
+ {
+ propertyBuilder.HasScale(attribute.Scale, fromDataAnnotation: true);
+ }
+ }
+ }
+}
diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
index 7161fc37207..5da17744b13 100644
--- a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
@@ -858,6 +858,42 @@ IConventionPropertyBuilder IConventionPropertyBuilder.IsUnicode(bool? unicode, b
bool IConventionPropertyBuilder.CanSetIsUnicode(bool? unicode, bool fromDataAnnotation)
=> CanSetIsUnicode(unicode, 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.HasPrecision(int? precision, bool fromDataAnnotation)
+ => HasPrecision(precision, 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.CanSetPrecision(int? precision, bool fromDataAnnotation)
+ => CanSetPrecision(precision, 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.HasScale(int? scale, bool fromDataAnnotation)
+ => HasScale(scale, 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.CanSetScale(int? scale, bool fromDataAnnotation)
+ => CanSetScale(scale, 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/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs
index 849e9f2c29a..9c4314f9100 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs
@@ -375,6 +375,28 @@ public void ComputedColumnSql_works()
});
}
+ [ConditionalFact]
+ public void IsUnicode_works()
+ {
+ Test(
+ modelBuilder => {
+ modelBuilder.Entity("Entity").Property("UnicodeColumn").IsUnicode();
+ modelBuilder.Entity("Entity").Property("NonUnicodeColumn").IsUnicode(false);
+ },
+ new ModelCodeGenerationOptions(),
+ code => {
+ Assert.Contains("Property(e => e.UnicodeColumn).IsUnicode()", code.ContextFile.Code);
+ Assert.Contains("Property(e => e.NonUnicodeColumn).IsUnicode(false)", code.ContextFile.Code);
+ },
+ model =>
+ {
+ var entity = model.FindEntityType("TestNamespace.Entity");
+ Assert.True(entity.GetProperty("UnicodeColumn").IsUnicode());
+ Assert.False(entity.GetProperty("NonUnicodeColumn").IsUnicode());
+ });
+ }
+
+
[ConditionalFact]
public void ComputedColumnSql_works_stored()
{
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs
index 99ea4f5f595..913d0b25682 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs
@@ -901,6 +901,63 @@ public partial class Entity
});
}
+ [ConditionalFact]
+ public void PrecisionAttribute_is_generated_for_property()
+ {
+ Test(
+ modelBuilder => modelBuilder
+ .Entity(
+ "Entity",
+ x =>
+ {
+ x.Property("Id");
+ x.Property("A").HasPrecision(10);
+ x.Property("B").HasPrecision(14, 3);
+ x.Property("C").HasPrecision(5);
+ x.Property("D").HasPrecision(3);
+ }),
+ new ModelCodeGenerationOptions { UseDataAnnotations = true },
+ code =>
+ {
+ AssertFileContents(
+ @"using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore;
+
+#nullable disable
+
+namespace TestNamespace
+{
+ public partial class Entity
+ {
+ [Key]
+ public int Id { get; set; }
+ [Precision(10)]
+ public decimal A { get; set; }
+ [Precision(14, 3)]
+ public decimal B { get; set; }
+ [Precision(5)]
+ public DateTime C { get; set; }
+ [Precision(3)]
+ public DateTimeOffset D { get; set; }
+ }
+}
+",
+ code.AdditionalFiles.Single(f => f.Path == "Entity.cs"));
+ },
+ model =>
+ {
+ var entitType = model.FindEntityType("TestNamespace.Entity");
+ Assert.Equal(10, entitType.GetProperty("A").GetPrecision());
+ Assert.Equal(14, entitType.GetProperty("B").GetPrecision());
+ Assert.Equal(3, entitType.GetProperty("B").GetScale());
+ Assert.Equal(5, entitType.GetProperty("C").GetPrecision());
+ Assert.Equal(3, entitType.GetProperty("D").GetPrecision());
+ });
+ }
+
[ConditionalFact]
public void Comments_are_generated()
{
diff --git a/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs b/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
index 2bba7f319ab..ff93e1580e0 100644
--- a/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
+++ b/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
@@ -2415,6 +2415,54 @@ protected class UnicodeAnnotationClass
public string PersonAddress;
}
+ [ConditionalFact]
+ public virtual void PrecisionAttribute_sets_precision_for_properties_and_fields()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity(b =>
+ {
+ b.Property(e => e.DecimalField);
+ b.Property(e => e.DateTimeField);
+ b.Property(e => e.DateTimeOffsetField);
+ });
+
+ Validate(modelBuilder);
+
+ Assert.Equal(10, GetProperty(modelBuilder, "DecimalProperty").GetPrecision());
+ Assert.Equal(2, GetProperty(modelBuilder, "DecimalProperty").GetScale());
+ Assert.Equal(5, GetProperty(modelBuilder, "DateTimeProperty").GetPrecision());
+ Assert.Equal(5, GetProperty(modelBuilder, "DateTimeOffsetProperty").GetPrecision());
+
+ Assert.Equal(10, GetProperty(modelBuilder, "DecimalField").GetPrecision());
+ Assert.Equal(2, GetProperty(modelBuilder, "DecimalField").GetScale());
+ Assert.Equal(5, GetProperty(modelBuilder, "DateTimeField").GetPrecision());
+ Assert.Equal(5, GetProperty(modelBuilder, "DateTimeOffsetField").GetPrecision());
+ }
+
+ protected class PrecisionAnnotationClass
+ {
+ public int Id { get; set; }
+
+ [Precision(10, 2)]
+ public decimal DecimalProperty { get; set; }
+
+ [Precision(5)]
+ public DateTime DateTimeProperty { get; set; }
+
+ [Precision(5)]
+ public DateTimeOffset DateTimeOffsetProperty { get; set; }
+
+ [Precision(10, 2)]
+ public string DecimalField;
+
+ [Precision(5)]
+ public DateTime DateTimeField;
+
+ [Precision(5)]
+ public DateTimeOffset DateTimeOffsetField;
+ }
+
[ConditionalFact]
public virtual void OwnedEntityTypeAttribute_configures_one_reference_as_owned()
{
diff --git a/test/EFCore.Tests/Infrastructure/AnnotationTest.cs b/test/EFCore.Tests/Infrastructure/AnnotationTest.cs
index 0d9b208edb1..22a605de932 100644
--- a/test/EFCore.Tests/Infrastructure/AnnotationTest.cs
+++ b/test/EFCore.Tests/Infrastructure/AnnotationTest.cs
@@ -26,5 +26,19 @@ public void Can_create_annotation()
Assert.Equal("Foo", annotation.Name);
Assert.Equal("Bar", annotation.Value);
}
+
+ [ConditionalFact]
+ public void NegativeNumberArguments_PrecisionAttribute_Throws()
+ {
+ Assert.Equal(
+ AbstractionsStrings.ArgumentIsNegativeNumber("precision"),
+ Assert.Throws(() => new PrecisionAttribute(-1)).Message);
+ Assert.Equal(
+ AbstractionsStrings.ArgumentIsNegativeNumber("scale"),
+ Assert.Throws(() => new PrecisionAttribute(3, -2)).Message);
+ Assert.Equal(
+ AbstractionsStrings.ArgumentIsNegativeNumber("precision"),
+ Assert.Throws(() => new PrecisionAttribute(-5, 4)).Message);
+ }
}
}
diff --git a/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs
index 727b1e18354..cbb17bdbaf8 100644
--- a/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs
+++ b/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs
@@ -594,6 +594,59 @@ public void UnicodeAttribute_on_field_sets_unicode_with_conventional_builder()
}
#endregion
+ #region PrecisionAttribute
+ [ConditionalFact]
+ public void PrecisionAttribute_overrides_configuration_from_convention_source()
+ {
+ var entityTypeBuilder = CreateInternalEntityTypeBuilder();
+
+ var propertyBuilder = entityTypeBuilder.Property(typeof(decimal), "DecimalProperty", ConfigurationSource.Explicit);
+
+ propertyBuilder.HasPrecision(12, ConfigurationSource.Convention);
+ propertyBuilder.HasScale(5, ConfigurationSource.Convention);
+
+ RunConvention(propertyBuilder);
+
+ Assert.Equal(10, propertyBuilder.Metadata.GetPrecision());
+ Assert.Equal(2, propertyBuilder.Metadata.GetScale());
+ }
+
+ [ConditionalFact]
+ public void PrecisionAttribute_does_not_override_configuration_from_explicit_source()
+ {
+ var entityTypeBuilder = CreateInternalEntityTypeBuilder();
+
+ var propertyBuilder = entityTypeBuilder.Property(typeof(decimal), "DecimalProperty", ConfigurationSource.Explicit);
+
+ propertyBuilder.HasPrecision(12, ConfigurationSource.Explicit);
+ propertyBuilder.HasScale(5, ConfigurationSource.Explicit);
+
+ RunConvention(propertyBuilder);
+
+ Assert.Equal(12, propertyBuilder.Metadata.GetPrecision());
+ Assert.Equal(5, propertyBuilder.Metadata.GetScale());
+ }
+
+ [ConditionalFact]
+ public void PrecisionAttribute_sets_precision_with_conventional_builder()
+ {
+ var modelBuilder = CreateModelBuilder();
+ var entityTypeBuilder = modelBuilder.Entity();
+
+ Assert.Equal(10, entityTypeBuilder.Property(e => e.DecimalProperty).Metadata.GetPrecision());
+ Assert.Equal(2, entityTypeBuilder.Property(e => e.DecimalProperty).Metadata.GetScale());
+ }
+
+ [ConditionalFact]
+ public void PrecisionAttribute_on_field_sets_precision_with_conventional_builder()
+ {
+ var modelBuilder = CreateModelBuilder();
+ var entityTypeBuilder = modelBuilder.Entity();
+
+ Assert.Equal(10, entityTypeBuilder.Property(nameof(F.DecimalField)).Metadata.GetPrecision());
+ Assert.Equal(2, entityTypeBuilder.Property(nameof(F.DecimalField)).Metadata.GetScale());
+ }
+ #endregion
[ConditionalFact]
public void Property_attribute_convention_runs_for_private_property()
{
@@ -649,6 +702,9 @@ private static void RunConvention(InternalPropertyBuilder propertyBuilder)
new UnicodeAttributeConvention(dependencies)
.ProcessPropertyAdded(propertyBuilder, context);
+
+ new PrecisionAttributeConvention(dependencies)
+ .ProcessPropertyAdded(propertyBuilder, context);
}
private void RunConvention(InternalEntityTypeBuilder entityTypeBuilder)
@@ -688,6 +744,9 @@ private class A
[Unicode(false)]
public string NonUnicodeProperty { get; set; }
+ [Precision(10, 2)]
+ public decimal DecimalProperty { get; set; }
+
[Key]
public int MyPrimaryKey { get; set; }
@@ -749,6 +808,9 @@ public class F
[Unicode(false)]
public string NonUnicodeField;
+ [Precision(10, 2)]
+ public decimal DecimalField;
+
[Key]
public int MyPrimaryKey;