diff --git a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs
index debfc0ca146..f0e7432747b 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs
@@ -148,6 +148,20 @@ protected virtual void Generate([NotNull] AddColumnOperation operation, [NotNull
.Append(Code.Literal(operation.MaxLength.Value));
}
+ if (operation.Precision.HasValue)
+ {
+ builder.AppendLine(",")
+ .Append("precision: ")
+ .Append(Code.Literal(operation.Precision.Value));
+ }
+
+ if (operation.Scale.HasValue)
+ {
+ builder.AppendLine(",")
+ .Append("scale: ")
+ .Append(Code.Literal(operation.Scale.Value));
+ }
+
if (operation.IsRowVersion)
{
builder
@@ -492,6 +506,20 @@ protected virtual void Generate([NotNull] AlterColumnOperation operation, [NotNu
.Append(Code.Literal(operation.MaxLength.Value));
}
+ if (operation.Precision.HasValue)
+ {
+ builder.AppendLine(",")
+ .Append("precision: ")
+ .Append(Code.Literal(operation.Precision.Value));
+ }
+
+ if (operation.Scale.HasValue)
+ {
+ builder.AppendLine(",")
+ .Append("scale: ")
+ .Append(Code.Literal(operation.Scale.Value));
+ }
+
if (operation.IsRowVersion)
{
builder
@@ -569,6 +597,20 @@ protected virtual void Generate([NotNull] AlterColumnOperation operation, [NotNu
.Append(Code.Literal(operation.OldColumn.MaxLength.Value));
}
+ if (operation.OldColumn.Precision.HasValue)
+ {
+ builder.AppendLine(",")
+ .Append("oldPrecision: ")
+ .Append(Code.Literal(operation.OldColumn.Precision.Value));
+ }
+
+ if (operation.OldColumn.Scale.HasValue)
+ {
+ builder.AppendLine(",")
+ .Append("oldScale: ")
+ .Append(Code.Literal(operation.OldColumn.Scale.Value));
+ }
+
if (operation.OldColumn.IsRowVersion)
{
builder
@@ -1033,6 +1075,22 @@ protected virtual void Generate([NotNull] CreateTableOperation operation, [NotNu
.Append(", ");
}
+ if (column.Precision.HasValue)
+ {
+ builder
+ .Append("precision: ")
+ .Append(Code.Literal(column.Precision.Value))
+ .Append(", ");
+ }
+
+ if (column.Scale.HasValue)
+ {
+ builder
+ .Append("scale: ")
+ .Append(Code.Literal(column.Scale.Value))
+ .Append(", ");
+ }
+
if (column.IsRowVersion)
{
builder.Append("rowVersion: true, ");
diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
index e6a44b3ae98..5fd4fad80cb 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
@@ -576,6 +576,10 @@ protected virtual void GeneratePropertyAnnotations([NotNull] IProperty property,
nameof(PropertyBuilder.HasMaxLength),
stringBuilder);
+ GenerateFluentApiForPrecisionAndScale(
+ ref annotations,
+ stringBuilder);
+
GenerateFluentApiForAnnotation(
ref annotations,
CoreAnnotationNames.Unicode,
@@ -1300,6 +1304,50 @@ protected virtual void GenerateFluentApiForAnnotation(
}
}
+ ///
+ /// Generates a Fluent API call for the Precision and Scale annotations.
+ ///
+ /// The list of annotations.
+ /// The builder code is added to.
+ protected virtual void GenerateFluentApiForPrecisionAndScale(
+ [NotNull] ref List annotations,
+ [NotNull] IndentedStringBuilder stringBuilder)
+ {
+ var precisionAnnotation = annotations
+ .FirstOrDefault(a => a.Name == CoreAnnotationNames.Precision);
+ var precisionValue = precisionAnnotation?.Value;
+
+ if (precisionValue != null)
+ {
+ stringBuilder
+ .AppendLine()
+ .Append(".")
+ .Append(nameof(PropertyBuilder.HasPrecision))
+ .Append("(")
+ .Append(Code.UnknownLiteral(precisionValue));
+
+ var scaleAnnotation = annotations
+ .FirstOrDefault(a => a.Name == CoreAnnotationNames.Scale);
+ var scaleValue = (int?)scaleAnnotation?.Value;
+
+ if (scaleValue != null)
+ {
+ if (scaleValue != 0)
+ {
+ stringBuilder
+ .Append(", ")
+ .Append(Code.UnknownLiteral(scaleValue));
+ }
+
+ annotations.Remove(scaleAnnotation);
+ }
+
+ stringBuilder.Append(")");
+
+ annotations.Remove(precisionAnnotation);
+ }
+ }
+
///
/// Generates code for an annotation.
///
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
index 12341702c57..c3806d3088c 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
@@ -633,6 +633,8 @@ private void GenerateProperty(IProperty property, bool useDataAnnotations)
var annotations = property.GetAnnotations().ToList();
RemoveAnnotation(ref annotations, CoreAnnotationNames.MaxLength);
+ RemoveAnnotation(ref annotations, CoreAnnotationNames.Precision);
+ RemoveAnnotation(ref annotations, CoreAnnotationNames.Scale);
RemoveAnnotation(ref annotations, CoreAnnotationNames.TypeMapping);
RemoveAnnotation(ref annotations, CoreAnnotationNames.Unicode);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ColumnName);
@@ -695,6 +697,21 @@ private void GenerateProperty(IProperty property, bool useDataAnnotations)
}
}
+ 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(
diff --git a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
index 81fca09a0e6..766504dc43f 100644
--- a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
@@ -444,6 +444,20 @@ protected virtual PropertyBuilder VisitColumn([NotNull] EntityTypeBuilder builde
property.HasMaxLength(typeScaffoldingInfo.ScaffoldMaxLength.Value);
}
+ if (typeScaffoldingInfo.ScaffoldPrecision.HasValue)
+ {
+ if (typeScaffoldingInfo.ScaffoldScale.HasValue)
+ {
+ property.HasPrecision(
+ typeScaffoldingInfo.ScaffoldPrecision.Value,
+ typeScaffoldingInfo.ScaffoldScale.Value);
+ }
+ else
+ {
+ property.HasPrecision(typeScaffoldingInfo.ScaffoldPrecision.Value);
+ }
+ }
+
if (column.ValueGenerated == ValueGenerated.OnAdd)
{
property.ValueGeneratedOnAdd();
diff --git a/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs b/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs
index 3b2ba15e9d0..54fc9694210 100644
--- a/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs
@@ -57,104 +57,98 @@ public ScaffoldingTypeMapper([NotNull] IRelationalTypeMappingSource typeMappingS
bool? scaffoldUnicode = null;
bool? scaffoldFixedLength = null;
int? scaffoldMaxLength = null;
-
- if (mapping.ClrType == typeof(byte[]))
+ int? scaffoldPrecision = null;
+ int? scaffoldScale = null;
+
+ var unwrappedClrType = mapping.ClrType.UnwrapNullableType();
+
+ // Check for inference
+ var defaultTypeMapping = _typeMappingSource.FindMapping(
+ unwrappedClrType,
+ null,
+ keyOrIndex,
+ unicode: mapping.IsUnicode,
+ size: mapping.Size,
+ rowVersion: rowVersion,
+ fixedLength: mapping.IsFixedLength,
+ precision: mapping.Precision,
+ scale: mapping.Scale);
+
+ if (defaultTypeMapping != null
+ && string.Equals(defaultTypeMapping.StoreType, storeType, StringComparison.OrdinalIgnoreCase))
{
- // Check for inference
- var byteArrayMapping = _typeMappingSource.FindMapping(
- typeof(byte[]),
+ canInfer = true;
+
+ // Check for Unicode
+ var unicodeMapping = _typeMappingSource.FindMapping(
+ unwrappedClrType,
null,
keyOrIndex,
+ unicode: null,
+ size: mapping.Size,
rowVersion: rowVersion,
+ fixedLength: mapping.IsFixedLength,
+ precision: mapping.Precision,
+ scale: mapping.Scale);
+
+ scaffoldUnicode = unicodeMapping.IsUnicode != defaultTypeMapping.IsUnicode ? (bool?)defaultTypeMapping.IsUnicode : null;
+
+ // Check for fixed-length
+ var fixedLengthMapping = _typeMappingSource.FindMapping(
+ unwrappedClrType,
+ null,
+ keyOrIndex,
+ unicode: mapping.IsUnicode,
size: mapping.Size,
- fixedLength: mapping.IsFixedLength);
-
- if (byteArrayMapping.StoreType.Equals(storeType, StringComparison.OrdinalIgnoreCase))
- {
- canInfer = true;
-
- // Check for fixed-length
- var fixedLengthMapping = _typeMappingSource.FindMapping(
- typeof(byte[]),
- null,
- keyOrIndex,
- rowVersion: rowVersion,
- size: mapping.Size,
- fixedLength: false);
-
- scaffoldFixedLength = fixedLengthMapping.IsFixedLength != byteArrayMapping.IsFixedLength
- ? (bool?)byteArrayMapping.IsFixedLength
- : null;
-
- // Check for size
- var sizedMapping = _typeMappingSource.FindMapping(
- typeof(byte[]),
- null,
- keyOrIndex,
- rowVersion: rowVersion,
- fixedLength: mapping.IsFixedLength);
-
- scaffoldMaxLength = sizedMapping.Size != byteArrayMapping.Size ? byteArrayMapping.Size : null;
- }
- }
- else if (mapping.ClrType == typeof(string))
- {
- // Check for inference
- var stringMapping = _typeMappingSource.FindMapping(
- typeof(string),
+ fixedLength: null,
+ precision: mapping.Precision,
+ scale: mapping.Scale);
+
+ scaffoldFixedLength = fixedLengthMapping.IsFixedLength != defaultTypeMapping.IsFixedLength
+ ? (bool?)defaultTypeMapping.IsFixedLength
+ : null;
+
+ // Check for size (= max-length)
+ var sizedMapping = _typeMappingSource.FindMapping(
+ unwrappedClrType,
+ null,
+ keyOrIndex,
+ unicode: mapping.IsUnicode,
+ size: null,
+ rowVersion: rowVersion,
+ fixedLength: false, // Fixed length with no size is not valid
+ precision: mapping.Precision,
+ scale: mapping.Scale);
+
+ scaffoldMaxLength = sizedMapping.Size != defaultTypeMapping.Size ? defaultTypeMapping.Size : null;
+
+ // Check for precision
+ var precisionMapping = _typeMappingSource.FindMapping(
+ unwrappedClrType,
null,
keyOrIndex,
unicode: mapping.IsUnicode,
size: mapping.Size,
- fixedLength: mapping.IsFixedLength);
-
- if (stringMapping.StoreType.Equals(storeType, StringComparison.OrdinalIgnoreCase))
- {
- canInfer = true;
-
- // Check for Unicode
- var unicodeMapping = _typeMappingSource.FindMapping(
- typeof(string),
- null,
- keyOrIndex,
- unicode: true,
- size: mapping.Size,
- fixedLength: mapping.IsFixedLength);
-
- scaffoldUnicode = unicodeMapping.IsUnicode != stringMapping.IsUnicode ? (bool?)stringMapping.IsUnicode : null;
-
- // Check for fixed-length
- var fixedLengthMapping = _typeMappingSource.FindMapping(
- typeof(string),
- null,
- keyOrIndex,
- unicode: mapping.IsUnicode,
- size: mapping.Size,
- fixedLength: false);
-
- scaffoldFixedLength = fixedLengthMapping.IsFixedLength != stringMapping.IsFixedLength
- ? (bool?)stringMapping.IsFixedLength
- : null;
-
- var sizedMapping = _typeMappingSource.FindMapping(
- typeof(string),
- null,
- keyOrIndex,
- unicode: mapping.IsUnicode,
- fixedLength: false); // Fixed length with no size is not valid
-
- scaffoldMaxLength = sizedMapping.Size != stringMapping.Size ? stringMapping.Size : null;
- }
- }
- else
- {
- var defaultMapping = _typeMappingSource.FindMapping(mapping.ClrType);
+ rowVersion: rowVersion,
+ fixedLength: mapping.IsFixedLength,
+ precision: null,
+ scale: mapping.Scale);
+
+ scaffoldPrecision = precisionMapping.Precision != defaultTypeMapping.Precision ? defaultTypeMapping.Precision : null;
+
+ // Check for scale
+ var scaleMapping = _typeMappingSource.FindMapping(
+ unwrappedClrType,
+ null,
+ keyOrIndex,
+ unicode: mapping.IsUnicode,
+ size: mapping.Size,
+ rowVersion: rowVersion,
+ fixedLength: mapping.IsFixedLength,
+ precision: mapping.Precision,
+ scale: null);
- if (string.Equals(defaultMapping?.StoreType, storeType, StringComparison.OrdinalIgnoreCase)
- && mapping.ClrType.UnwrapNullableType() != typeof(decimal))
- {
- canInfer = true;
- }
+ scaffoldScale = scaleMapping.Scale != defaultTypeMapping.Scale ? defaultTypeMapping.Scale : null;
}
return new TypeScaffoldingInfo(
@@ -162,7 +156,9 @@ public ScaffoldingTypeMapper([NotNull] IRelationalTypeMappingSource typeMappingS
canInfer,
scaffoldUnicode,
scaffoldMaxLength,
- scaffoldFixedLength);
+ scaffoldFixedLength,
+ scaffoldPrecision,
+ scaffoldScale);
}
}
}
diff --git a/src/EFCore.Design/Scaffolding/Internal/TypeScaffoldingInfo.cs b/src/EFCore.Design/Scaffolding/Internal/TypeScaffoldingInfo.cs
index 6baa1219495..0bcad593be1 100644
--- a/src/EFCore.Design/Scaffolding/Internal/TypeScaffoldingInfo.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/TypeScaffoldingInfo.cs
@@ -28,13 +28,17 @@ public TypeScaffoldingInfo(
bool inferred,
bool? scaffoldUnicode,
int? scaffoldMaxLength,
- bool? scaffoldFixedLength)
+ bool? scaffoldFixedLength,
+ int? scaffoldPrecision,
+ int? scaffoldScale)
{
Check.NotNull(clrType, nameof(clrType));
IsInferred = inferred;
ScaffoldUnicode = scaffoldUnicode;
ScaffoldMaxLength = scaffoldMaxLength;
+ ScaffoldPrecision = scaffoldPrecision;
+ ScaffoldScale = scaffoldScale;
ScaffoldFixedLength = scaffoldFixedLength;
ClrType = clrType;
}
@@ -78,5 +82,21 @@ public TypeScaffoldingInfo(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual int? ScaffoldMaxLength { get; }
+
+ ///
+ /// 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.
+ ///
+ public virtual int? ScaffoldPrecision { get; }
+
+ ///
+ /// 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.
+ ///
+ public virtual int? ScaffoldScale { get; }
}
}
diff --git a/src/EFCore.Relational/Metadata/IColumn.cs b/src/EFCore.Relational/Metadata/IColumn.cs
index 68cbab91468..9c93f86ec6e 100644
--- a/src/EFCore.Relational/Metadata/IColumn.cs
+++ b/src/EFCore.Relational/Metadata/IColumn.cs
@@ -27,6 +27,18 @@ public interface IColumn : IColumnBase
///
int? MaxLength => PropertyMappings.First().Property.GetMaxLength();
+ ///
+ /// Gets the precision of data that is allowed in this column. For example, if the property is a '
+ /// then this is the maximum number of digits.
+ ///
+ int? Precision => PropertyMappings.First().Property.GetPrecision();
+
+ ///
+ /// Gets the scale of data that is allowed in this column. For example, if the property is a '
+ /// then this is the maximum number of decimal places.
+ ///
+ int? Scale => PropertyMappings.First().Property.GetScale();
+
///
/// Gets a value indicating whether or not the property can persist Unicode characters.
///
diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
index 95e0928272a..168c0c57885 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
@@ -869,6 +869,8 @@ private bool PropertyStructureEquals(IProperty source, IProperty target)
&& source.IsConcurrencyToken == target.IsConcurrencyToken
&& source.ValueGenerated == target.ValueGenerated
&& source.GetMaxLength() == target.GetMaxLength()
+ && source.GetPrecision() == target.GetPrecision()
+ && source.GetScale() == target.GetScale()
&& source.IsColumnNullable() == target.IsColumnNullable()
&& source.IsUnicode() == target.IsUnicode()
&& source.IsFixedLength() == target.IsFixedLength()
@@ -1066,6 +1068,8 @@ private void Initialize(
columnOperation.ColumnType = column.Type;
columnOperation.MaxLength = column.MaxLength;
+ columnOperation.Precision = column.Precision;
+ columnOperation.Scale = column.Scale;
columnOperation.IsUnicode = column.IsUnicode;
columnOperation.IsFixedLength = column.IsFixedLength;
columnOperation.IsRowVersion = column.IsRowVersion;
diff --git a/src/EFCore.Relational/Migrations/MigrationBuilder.cs b/src/EFCore.Relational/Migrations/MigrationBuilder.cs
index 4da394f0eeb..7b3238fadc0 100644
--- a/src/EFCore.Relational/Migrations/MigrationBuilder.cs
+++ b/src/EFCore.Relational/Migrations/MigrationBuilder.cs
@@ -61,6 +61,12 @@ public MigrationBuilder([CanBeNull] string activeProvider)
/// The SQL expression to use to compute the column value.
/// Indicates whether or not the column is constrained to fixed-length data.
/// A comment to associate with the column.
+ ///
+ /// The maximum number of digits that is allowed in this column, or null if not specified or not applicable.
+ ///
+ ///
+ /// The maximum number of decimal places that is allowed in this column, or null if not specified or not applicable.
+ ///
/// A builder to allow annotations to be added to the operation.
public virtual OperationBuilder AddColumn(
[NotNull] string name,
@@ -75,7 +81,9 @@ public virtual OperationBuilder AddColumn(
[CanBeNull] string defaultValueSql = null,
[CanBeNull] string computedColumnSql = null,
bool? fixedLength = null,
- [CanBeNull] string comment = null)
+ [CanBeNull] string comment = null,
+ int? precision = null,
+ int? scale = null)
{
Check.NotEmpty(name, nameof(name));
Check.NotEmpty(table, nameof(table));
@@ -95,7 +103,9 @@ public virtual OperationBuilder AddColumn(
DefaultValueSql = defaultValueSql,
ComputedColumnSql = computedColumnSql,
IsFixedLength = fixedLength,
- Comment = comment
+ Comment = comment,
+ Precision = precision,
+ Scale = scale,
};
Operations.Add(operation);
@@ -346,6 +356,18 @@ public virtual OperationBuilder AddUniqueConstrain
/// Indicates whether or not the column was previously constrained to fixed-length data.
/// A comment to associate with the column.
/// The previous comment to associate with the column.
+ ///
+ /// The maximum number of digits that is allowed in this column, or null if not specified or not applicable.
+ ///
+ ///
+ /// The previous maximum number of digits that is allowed in this column, or null if not specified or not applicable.
+ ///
+ ///
+ /// The maximum number of decimal places that is allowed in this column, or null if not specified or not applicable.
+ ///
+ ///
+ /// The previous maximum number of decimal places that is allowed in this column, or null if not specified or not applicable.
+ ///
/// A builder to allow annotations to be added to the operation.
public virtual AlterOperationBuilder AlterColumn(
[NotNull] string name,
@@ -371,7 +393,11 @@ public virtual AlterOperationBuilder AlterColumn(
bool? fixedLength = null,
bool? oldFixedLength = null,
[CanBeNull] string comment = null,
- [CanBeNull] string oldComment = null)
+ [CanBeNull] string oldComment = null,
+ int? precision = null,
+ int? oldPrecision = null,
+ int? scale = null,
+ int? oldScale = null)
{
Check.NotEmpty(name, nameof(name));
Check.NotEmpty(table, nameof(table));
@@ -392,6 +418,8 @@ public virtual AlterOperationBuilder AlterColumn(
ComputedColumnSql = computedColumnSql,
IsFixedLength = fixedLength,
Comment = comment,
+ Precision = precision,
+ Scale = scale,
OldColumn = new ColumnOperation
{
ClrType = oldClrType ?? typeof(T),
@@ -404,7 +432,9 @@ public virtual AlterOperationBuilder AlterColumn(
DefaultValueSql = oldDefaultValueSql,
ComputedColumnSql = oldComputedColumnSql,
IsFixedLength = oldFixedLength,
- Comment = oldComment
+ Comment = oldComment,
+ Precision = oldPrecision,
+ Scale = oldScale
}
};
diff --git a/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs b/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs
index 8fce8d9985b..d4cf5caea70 100644
--- a/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs
+++ b/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs
@@ -1201,6 +1201,8 @@ protected virtual string GetColumnType(
{
if (operation.IsUnicode == column.IsUnicode
&& operation.MaxLength == column.MaxLength
+ && operation.Precision == column.Precision
+ && operation.Scale == column.Scale
&& operation.IsFixedLength == column.IsFixedLength
&& operation.IsRowVersion == column.IsRowVersion)
{
@@ -1219,7 +1221,9 @@ protected virtual string GetColumnType(
operation.IsUnicode,
operation.MaxLength,
operation.IsRowVersion,
- operation.IsFixedLength)
+ operation.IsFixedLength,
+ operation.Precision,
+ operation.Scale)
.StoreType;
}
diff --git a/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs b/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs
index c0142ab04a4..a0a9f2ca42f 100644
--- a/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs
+++ b/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs
@@ -45,6 +45,8 @@ public ColumnsBuilder([NotNull] CreateTableOperation createTableOperation)
/// The SQL expression to use to compute the column value.
/// Indicates whether or not the column is constrained to fixed-length data.
/// A comment to be applied to the table.
+ /// The maximum number of digits for data in the column.
+ /// The maximum number of decimal places for data in the column.
/// The same builder so that multiple calls can be chained.
public virtual OperationBuilder Column(
[CanBeNull] string type = null,
@@ -57,7 +59,9 @@ public virtual OperationBuilder Column(
[CanBeNull] string defaultValueSql = null,
[CanBeNull] string computedColumnSql = null,
bool? fixedLength = null,
- [CanBeNull] string comment = null)
+ [CanBeNull] string comment = null,
+ int? precision = null,
+ int? scale = null)
{
var operation = new AddColumnOperation
{
@@ -74,7 +78,9 @@ public virtual OperationBuilder Column(
DefaultValueSql = defaultValueSql,
ComputedColumnSql = computedColumnSql,
IsFixedLength = fixedLength,
- Comment = comment
+ Comment = comment,
+ Precision = precision,
+ Scale = scale
};
_createTableOperation.Columns.Add(operation);
diff --git a/src/EFCore.Relational/Migrations/Operations/ColumnOperation.cs b/src/EFCore.Relational/Migrations/Operations/ColumnOperation.cs
index 67e55b1cf0d..e9b27e4d8ad 100644
--- a/src/EFCore.Relational/Migrations/Operations/ColumnOperation.cs
+++ b/src/EFCore.Relational/Migrations/Operations/ColumnOperation.cs
@@ -39,6 +39,18 @@ public class ColumnOperation : MigrationOperation
///
public virtual int? MaxLength { get; set; }
+ ///
+ /// The maximum number of digits that the column can store, or null
+ /// if this is not specified or does not apply to this column type.
+ ///
+ public virtual int? Precision { get; set; }
+
+ ///
+ /// The maximum number of decimal places that the column can store, or null
+ /// if this is not specified or does not apply to this column type.
+ ///
+ public virtual int? Scale { get; set; }
+
///
/// Indicates whether or not this column acts as an automatic concurrency token in the same vein
/// as 'rowversion'/'timestamp' columns on SQL Server.
diff --git a/src/EFCore.Relational/Storage/DecimalTypeMapping.cs b/src/EFCore.Relational/Storage/DecimalTypeMapping.cs
index 0ddead7ed5d..d334f0a8bb7 100644
--- a/src/EFCore.Relational/Storage/DecimalTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/DecimalTypeMapping.cs
@@ -24,10 +24,14 @@ public class DecimalTypeMapping : RelationalTypeMapping
///
/// The name of the database type.
/// The to be used.
+ /// The precision of data the property is configured to store, or null if the default precision is required.
+ /// The scale of data the property is configured to store, or null if the default scale is required.
public DecimalTypeMapping(
[NotNull] string storeType,
- DbType? dbType = null)
- : base(storeType, typeof(decimal), dbType)
+ DbType? dbType = null,
+ int? precision = null,
+ int? scale = null)
+ : base(storeType, typeof(decimal), dbType, precision: precision, scale: scale)
{
}
diff --git a/src/EFCore.Relational/Storage/Internal/DbParameterCollectionExtensions.cs b/src/EFCore.Relational/Storage/Internal/DbParameterCollectionExtensions.cs
index 5ec9b0d198b..f84009a8f1a 100644
--- a/src/EFCore.Relational/Storage/Internal/DbParameterCollectionExtensions.cs
+++ b/src/EFCore.Relational/Storage/Internal/DbParameterCollectionExtensions.cs
@@ -150,7 +150,7 @@ private static void FormatParameterValue(StringBuilder builder, object parameter
{
builder
.Append('\'')
- .Append(((DateTime)parameterValue).ToString("s"))
+ .Append(((DateTime)parameterValue).ToString("o"))
.Append('\'');
}
else if (parameterValue.GetType() == typeof(DateTimeOffset))
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
index 763759ac5c6..9a7236a388e 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
@@ -315,15 +315,21 @@ private static string GetBaseName(string storeType)
/// The to be used.
/// A value indicating whether the type should handle Unicode data or not.
/// The size of data the property is configured to store, or null if no size is configured.
+ /// A value indicating whether the type has fixed length data or not.
+ /// The precision of data the property is configured to store, or null if no precision is configured.
+ /// The scale of data the property is configured to store, or null if no scale is configured.
protected RelationalTypeMapping(
[NotNull] string storeType,
[NotNull] Type clrType,
DbType? dbType = null,
bool unicode = false,
- int? size = null)
+ int? size = null,
+ bool fixedLength = false,
+ int? precision = null,
+ int? scale = null)
: this(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(clrType), storeType, StoreTypePostfix.None, dbType, unicode, size))
+ new CoreTypeMappingParameters(clrType), storeType, StoreTypePostfix.None, dbType, unicode, size, fixedLength, precision, scale))
{
}
@@ -404,6 +410,16 @@ public virtual RelationalTypeMapping Clone(in RelationalTypeMappingInfo mappingI
///
public virtual int? Size => Parameters.Size;
+ ///
+ /// Gets the precision of data the property is configured to store, or null if no precision is configured.
+ ///
+ public virtual int? Precision => Parameters.Precision;
+
+ ///
+ /// Gets the scale of data the property is configured to store, or null if no scale is configured.
+ ///
+ public virtual int? Scale => Parameters.Scale;
+
///
/// Gets a value indicating whether the type is constrained to fixed-length data.
///
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs
index dd557517062..bf209c4014a 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs
@@ -9,7 +9,6 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
@@ -415,6 +414,7 @@ protected virtual string ParseStoreTypeName(
var openParen = storeTypeName.IndexOf("(", StringComparison.Ordinal);
if (openParen > 0)
{
+ string storeTypeNameBase = storeTypeName.Substring(0, openParen).Trim();
var closeParen = storeTypeName.IndexOf(")", openParen + 1, StringComparison.Ordinal);
if (closeParen > openParen)
{
@@ -435,16 +435,35 @@ protected virtual string ParseStoreTypeName(
else if (int.TryParse(
storeTypeName.Substring(openParen + 1, closeParen - openParen - 1).Trim(), out var parsedSize))
{
- size = parsedSize;
- precision = parsedSize;
+ if (StoreTypeNameBaseUsesPrecision(storeTypeNameBase))
+ {
+ precision = parsedSize;
+ scale = 0;
+ }
+ else
+ {
+ size = parsedSize;
+ }
}
- return storeTypeName.Substring(0, openParen).Trim();
+ return storeTypeNameBase;
}
}
}
return storeTypeName;
}
+
+ ///
+ /// Returns whether the store type name base interprets
+ /// nameBase(n) as a precision rather than a length
+ ///
+ /// The name base of the store type
+ ///
+ /// true if the store type name base interprets nameBase(n)
+ /// as a precision rather than a length, false otherwise.
+ ///
+ protected virtual bool StoreTypeNameBaseUsesPrecision([NotNull] string storeTypeNameBase)
+ => false;
}
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs
index 174bbb22843..4f0a07a32e6 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs
@@ -16,7 +16,19 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal
///
public class SqlServerDateTimeOffsetTypeMapping : DateTimeOffsetTypeMapping
{
- private const string DateTimeOffsetFormatConst = "{0:yyyy-MM-ddTHH:mm:ss.fffffffzzz}";
+ // Note: this array will be accessed using the precision as an index
+ // so the order of the entries in this array is important
+ private readonly string[] _dateTimeOffsetFormats =
+ {
+ "'{0:yyyy-MM-ddTHH:mm:sszzz}'",
+ "'{0:yyyy-MM-ddTHH:mm:ss.fzzz}'",
+ "'{0:yyyy-MM-ddTHH:mm:ss.ffzzz}'",
+ "'{0:yyyy-MM-ddTHH:mm:ss.fffzzz}'",
+ "'{0:yyyy-MM-ddTHH:mm:ss.ffffzzz}'",
+ "'{0:yyyy-MM-ddTHH:mm:ss.fffffzzz}'",
+ "'{0:yyyy-MM-ddTHH:mm:ss.ffffffzzz}'",
+ "'{0:yyyy-MM-ddTHH:mm:ss.fffffffzzz}'"
+ };
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -56,7 +68,23 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p
/// 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.
///
- protected override string SqlLiteralFormatString => "'" + DateTimeOffsetFormatConst + "'";
+ protected override string SqlLiteralFormatString
+ {
+ get
+ {
+ if (Precision.HasValue)
+ {
+ var precision = Precision.Value;
+ if (precision <= 7
+ && precision >= 0)
+ {
+ return _dateTimeOffsetFormats[precision];
+ }
+ }
+
+ return _dateTimeOffsetFormats[7];
+ }
+ }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -73,6 +101,11 @@ protected override void ConfigureParameter(DbParameter parameter)
{
parameter.Size = Size.Value;
}
+
+ if (Precision.HasValue)
+ {
+ parameter.Precision = unchecked((byte)Precision.Value);
+ }
}
}
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs
index 32a41f1d254..cf8f171b50f 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs
@@ -21,6 +21,8 @@ public class SqlServerDateTimeTypeMapping : DateTimeTypeMapping
private const string SmallDateTimeFormatConst = "'{0:yyyy-MM-ddTHH:mm:ss}'";
private const string DateTimeFormatConst = "'{0:yyyy-MM-ddTHH:mm:ss.fff}'";
+ // Note: this array will be accessed using the precision as an index
+ // so the order of the entries in this array is important
private readonly string[] _dateTime2Formats =
{
"'{0:yyyy-MM-ddTHH:mm:ss}'",
@@ -78,6 +80,11 @@ protected override void ConfigureParameter(DbParameter parameter)
{
parameter.Size = Size.Value;
}
+
+ if (Precision.HasValue)
+ {
+ parameter.Precision = unchecked((byte)Precision.Value);
+ }
}
///
@@ -107,13 +114,13 @@ protected override string SqlLiteralFormatString
case "smalldatetime":
return SmallDateTimeFormatConst;
default:
- if (Size.HasValue)
+ if (Precision.HasValue)
{
- var size = Size.Value;
- if (size <= 7
- && size >= 0)
+ var precision = Precision.Value;
+ if (precision <= 7
+ && precision >= 0)
{
- return _dateTime2Formats[size];
+ return _dateTime2Formats[precision];
}
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs
index a11761176d6..3b600e51dd1 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs
@@ -73,6 +73,16 @@ protected override void ConfigureParameter(DbParameter parameter)
{
parameter.Size = Size.Value;
}
+
+ if (Precision.HasValue)
+ {
+ parameter.Precision = unchecked((byte)Precision.Value);
+ }
+
+ if (Scale.HasValue)
+ {
+ parameter.Scale = unchecked((byte)Scale.Value);
+ }
}
}
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
index db01c6a4cbe..5903bc8b8f4 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
@@ -359,5 +359,10 @@ private RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingIn
return null;
}
+
+ private static readonly List _nameBasesUsingPrecision =
+ new List() { "decimal", "dec", "numeric", "datetime2", "datetimeoffset" };
+ protected override bool StoreTypeNameBaseUsesPrecision(string storeTypeNameBase)
+ => _nameBasesUsingPrecision.Contains(storeTypeNameBase);
}
}
diff --git a/src/EFCore/Extensions/ConventionPropertyExtensions.cs b/src/EFCore/Extensions/ConventionPropertyExtensions.cs
index 3cda66dcb59..04252efe63f 100644
--- a/src/EFCore/Extensions/ConventionPropertyExtensions.cs
+++ b/src/EFCore/Extensions/ConventionPropertyExtensions.cs
@@ -110,6 +110,46 @@ public static IEnumerable GetContainingKeys([NotNull] this IConv
public static ConfigurationSource? GetMaxLengthConfigurationSource([NotNull] this IConventionProperty property)
=> property.FindAnnotation(CoreAnnotationNames.MaxLength)?.GetConfigurationSource();
+ ///
+ /// Sets 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.
+ ///
+ /// The property to get the precision of.
+ /// The maximum number of digits that is allowed in this property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ public static int? SetPrecision([NotNull] this IConventionProperty property, int? precision, bool fromDataAnnotation = false)
+ => property.AsProperty().SetPrecision(
+ precision, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// Returns the configuration source for .
+ ///
+ /// The property to find configuration source for.
+ /// The configuration source for .
+ public static ConfigurationSource? GetPrecisionConfigurationSource([NotNull] this IConventionProperty property)
+ => property.FindAnnotation(CoreAnnotationNames.Precision)?.GetConfigurationSource();
+
+ ///
+ /// Sets the scale of data that is allowed in this property.
+ /// For example, if the property is a
+ /// then this is the maximum number of decimal places.
+ ///
+ /// The property to get the precision of.
+ /// The maximum number of decimal places that is allowed in this property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ public static int? SetScale([NotNull] this IConventionProperty property, int? scale, bool fromDataAnnotation = false)
+ => property.AsProperty().SetScale(
+ scale, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// Returns the configuration source for .
+ ///
+ /// The property to find configuration source for.
+ /// The configuration source for .
+ public static ConfigurationSource? GetScaleConfigurationSource([NotNull] this IConventionProperty property)
+ => property.FindAnnotation(CoreAnnotationNames.Scale)?.GetConfigurationSource();
+
///
/// Sets a value indicating whether this property can persist Unicode characters.
///
diff --git a/src/EFCore/Extensions/MutablePropertyExtensions.cs b/src/EFCore/Extensions/MutablePropertyExtensions.cs
index 8cedf558167..8e805676521 100644
--- a/src/EFCore/Extensions/MutablePropertyExtensions.cs
+++ b/src/EFCore/Extensions/MutablePropertyExtensions.cs
@@ -90,6 +90,26 @@ public static IEnumerable GetContainingKeys([NotNull] this IMutable
public static void SetMaxLength([NotNull] this IMutableProperty property, int? maxLength)
=> property.AsProperty().SetMaxLength(maxLength, ConfigurationSource.Explicit);
+ ///
+ /// Sets 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.
+ ///
+ /// The property to set the precision of.
+ /// The maximum number of digits that is allowed in this property.
+ public static void SetPrecision([NotNull] this IMutableProperty property, int? precision)
+ => property.AsProperty().SetPrecision(precision, ConfigurationSource.Explicit);
+
+ ///
+ /// Sets the scale of data that is allowed in this property.
+ /// For example, if the property is a
+ /// then this is the maximum number of decimal places.
+ ///
+ /// The property to set the scale of.
+ /// The maximum number of decimal places that is allowed in this property.
+ public static void SetScale([NotNull] this IMutableProperty property, int? scale)
+ => property.AsProperty().SetScale(scale, ConfigurationSource.Explicit);
+
///
/// Sets a value indicating whether this property can persist Unicode characters.
///
diff --git a/src/EFCore/Extensions/PropertyExtensions.cs b/src/EFCore/Extensions/PropertyExtensions.cs
index a99b72a58fb..d075e25d529 100644
--- a/src/EFCore/Extensions/PropertyExtensions.cs
+++ b/src/EFCore/Extensions/PropertyExtensions.cs
@@ -223,6 +223,34 @@ public static IEnumerable GetContainingKeys([NotNull] this IProperty prope
return (int?)property[CoreAnnotationNames.MaxLength];
}
+ ///
+ /// Gets 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.
+ ///
+ /// The property to get the precision of.
+ /// The precision, or null if none if defined.
+ public static int? GetPrecision([NotNull] this IProperty property)
+ {
+ Check.NotNull(property, nameof(property));
+
+ return (int?)property[CoreAnnotationNames.Precision];
+ }
+
+ ///
+ /// Gets the scale of data that is allowed in this property.
+ /// For example, if the property is a
+ /// then this is the maximum number of decimal places.
+ ///
+ /// The property to get the scale of.
+ /// The scale, or null if none if defined.
+ public static int? GetScale([NotNull] this IProperty property)
+ {
+ Check.NotNull(property, nameof(property));
+
+ return (int?)property[CoreAnnotationNames.Scale];
+ }
+
///
/// Gets a value indicating whether or not the property can persist Unicode characters.
///
diff --git a/src/EFCore/Metadata/Builders/PropertyBuilder.cs b/src/EFCore/Metadata/Builders/PropertyBuilder.cs
index c6f1353c3c8..9ea3f26745d 100644
--- a/src/EFCore/Metadata/Builders/PropertyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/PropertyBuilder.cs
@@ -91,6 +91,38 @@ public virtual PropertyBuilder HasMaxLength(int maxLength)
return this;
}
+ ///
+ /// Configures the precision and scale of the property.
+ ///
+ /// The precision of the property.
+ /// The scale of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual PropertyBuilder HasPrecision(int precision, int scale)
+ {
+ Builder.HasPrecision(precision, ConfigurationSource.Explicit);
+ Builder.HasScale(scale, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
+ ///
+ ///
+ /// Configures the precision of the property.
+ ///
+ ///
+ /// Note: has the side-effect of setting the scale to 0.
+ ///
+ ///
+ /// The precision of the property.
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual PropertyBuilder HasPrecision(int precision)
+ {
+ Builder.HasPrecision(precision, ConfigurationSource.Explicit);
+ Builder.HasScale(0, ConfigurationSource.Explicit);
+
+ return this;
+ }
+
///
/// Configures whether the property as capable of persisting unicode characters.
/// Can only be set on properties.
diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
index cb288e46dbe..c9823806574 100644
--- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
+++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
@@ -22,6 +22,22 @@ public static class CoreAnnotationNames
///
public const string MaxLength = "MaxLength";
+ ///
+ /// 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.
+ ///
+ public const string Precision = "Precision";
+
+ ///
+ /// 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.
+ ///
+ public const string Scale = "Scale";
+
///
/// 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
@@ -256,6 +272,8 @@ public static class CoreAnnotationNames
public static readonly ISet AllNames = new HashSet
{
MaxLength,
+ Precision,
+ Scale,
Unicode,
ProductVersion,
ValueGeneratorFactory,
diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
index cf50a3c7658..726a8303f87 100644
--- a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
@@ -225,6 +225,62 @@ public virtual bool CanSetMaxLength(int? maxLength, ConfigurationSource? configu
=> configurationSource.Overrides(Metadata.GetMaxLengthConfigurationSource())
|| Metadata.GetMaxLength() == maxLength;
+ ///
+ /// 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.
+ ///
+ public virtual InternalPropertyBuilder HasPrecision(int? precision, ConfigurationSource configurationSource)
+ {
+ if (CanSetPrecision(precision, configurationSource))
+ {
+ Metadata.SetPrecision(precision, configurationSource);
+
+ return this;
+ }
+
+ return null;
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual bool CanSetPrecision(int? precision, ConfigurationSource? configurationSource)
+ => configurationSource.Overrides(Metadata.GetPrecisionConfigurationSource())
+ || Metadata.GetPrecision() == precision;
+
+ ///
+ /// 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.
+ ///
+ public virtual InternalPropertyBuilder HasScale(int? scale, ConfigurationSource configurationSource)
+ {
+ if (CanSetScale(scale, configurationSource))
+ {
+ Metadata.SetScale(scale, configurationSource);
+
+ return this;
+ }
+
+ return null;
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual bool CanSetScale(int? scale, ConfigurationSource? configurationSource)
+ => configurationSource.Overrides(Metadata.GetScaleConfigurationSource())
+ || Metadata.GetScale() == scale;
+
///
/// 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/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs
index 7d2bc8def0c..d0947fe6eaf 100644
--- a/src/EFCore/Metadata/Internal/Property.cs
+++ b/src/EFCore/Metadata/Internal/Property.cs
@@ -305,6 +305,42 @@ public virtual bool IsConcurrencyToken
return unicode;
}
+ ///
+ /// 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.
+ ///
+ public virtual int? SetPrecision(int? precision, ConfigurationSource configurationSource)
+ {
+ if (precision != null && precision < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(precision));
+ }
+
+ this.SetOrRemoveAnnotation(CoreAnnotationNames.Precision, precision, configurationSource);
+
+ return precision;
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual int? SetScale(int? scale, ConfigurationSource configurationSource)
+ {
+ if (scale != null && scale < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(scale));
+ }
+
+ this.SetOrRemoveAnnotation(CoreAnnotationNames.Scale, scale, configurationSource);
+
+ return scale;
+ }
+
///
/// 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/Storage/TypeMappingInfo.cs b/src/EFCore/Storage/TypeMappingInfo.cs
index ed29b937720..02993a66406 100644
--- a/src/EFCore/Storage/TypeMappingInfo.cs
+++ b/src/EFCore/Storage/TypeMappingInfo.cs
@@ -57,6 +57,8 @@ public TypeMappingInfo(
ValueConverter customConverter = null;
int? size = null;
+ int? precision = null;
+ int? scale = null;
bool? isUnicode = null;
for (var i = 0; i < principals.Count; i++)
{
@@ -79,6 +81,24 @@ public TypeMappingInfo(
}
}
+ if (precision == null)
+ {
+ var precisionFromProperty = principal.GetPrecision();
+ if (precisionFromProperty != null)
+ {
+ precision = precisionFromProperty;
+ }
+ }
+
+ if (scale == null)
+ {
+ var scaleFromProperty = principal.GetScale();
+ if (scaleFromProperty != null)
+ {
+ scale = scaleFromProperty;
+ }
+ }
+
if (isUnicode == null)
{
var unicode = principal.IsUnicode();
@@ -97,8 +117,8 @@ public TypeMappingInfo(
IsUnicode = isUnicode ?? mappingHints?.IsUnicode ?? fallbackUnicode;
IsRowVersion = property.IsConcurrencyToken && property.ValueGenerated == ValueGenerated.OnAddOrUpdate;
ClrType = (customConverter?.ProviderClrType ?? property.ClrType).UnwrapNullableType();
- Scale = mappingHints?.Scale ?? fallbackScale;
- Precision = mappingHints?.Precision ?? fallbackPrecision;
+ Scale = scale ?? mappingHints?.Scale ?? fallbackScale;
+ Precision = precision ?? mappingHints?.Precision ?? fallbackPrecision;
}
///
diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs
index a9f2a9fa1c1..2a66cd275b6 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs
@@ -76,6 +76,8 @@ public void AddColumnOperation_all_args()
ColumnType = "int",
IsUnicode = false,
MaxLength = 30,
+ Precision = 10,
+ Scale = 5,
IsRowVersion = true,
IsNullable = true,
DefaultValue = 1,
@@ -98,6 +100,10 @@ public void AddColumnOperation_all_args()
+ _eol
+ " maxLength: 30,"
+ _eol
+ + " precision: 10,"
+ + _eol
+ + " scale: 5,"
+ + _eol
+ " rowVersion: true,"
+ _eol
+ " nullable: true,"
@@ -517,6 +523,8 @@ public void AlterColumnOperation_required_args()
Assert.Null(o.IsUnicode);
Assert.Null(o.IsFixedLength);
Assert.Null(o.MaxLength);
+ Assert.Null(o.Precision);
+ Assert.Null(o.Scale);
Assert.False(o.IsRowVersion);
Assert.False(o.IsNullable);
Assert.Null(o.DefaultValue);
@@ -527,6 +535,8 @@ public void AlterColumnOperation_required_args()
Assert.Null(o.OldColumn.IsUnicode);
Assert.Null(o.OldColumn.IsFixedLength);
Assert.Null(o.OldColumn.MaxLength);
+ Assert.Null(o.OldColumn.Precision);
+ Assert.Null(o.OldColumn.Scale);
Assert.False(o.OldColumn.IsRowVersion);
Assert.False(o.OldColumn.IsNullable);
Assert.Null(o.OldColumn.DefaultValue);
@@ -548,6 +558,8 @@ public void AlterColumnOperation_all_args()
ColumnType = "int",
IsUnicode = false,
MaxLength = 30,
+ Precision = 10,
+ Scale = 5,
IsRowVersion = true,
IsNullable = true,
DefaultValue = 1,
@@ -559,6 +571,8 @@ public void AlterColumnOperation_all_args()
ColumnType = "string",
IsUnicode = false,
MaxLength = 20,
+ Precision = 5,
+ Scale = 1,
IsRowVersion = true,
IsNullable = true,
DefaultValue = 0,
@@ -582,6 +596,10 @@ public void AlterColumnOperation_all_args()
+ _eol
+ " maxLength: 30,"
+ _eol
+ + " precision: 10,"
+ + _eol
+ + " scale: 5,"
+ + _eol
+ " rowVersion: true,"
+ _eol
+ " nullable: true,"
@@ -600,6 +618,10 @@ public void AlterColumnOperation_all_args()
+ _eol
+ " oldMaxLength: 20,"
+ _eol
+ + " oldPrecision: 5,"
+ + _eol
+ + " oldScale: 1,"
+ + _eol
+ " oldRowVersion: true,"
+ _eol
+ " oldNullable: true,"
@@ -617,6 +639,8 @@ public void AlterColumnOperation_all_args()
Assert.False(o.IsUnicode);
Assert.True(o.IsFixedLength);
Assert.Equal(30, o.MaxLength);
+ Assert.Equal(10, o.Precision);
+ Assert.Equal(5, o.Scale);
Assert.True(o.IsRowVersion);
Assert.True(o.IsNullable);
Assert.Equal(1, o.DefaultValue);
@@ -628,6 +652,8 @@ public void AlterColumnOperation_all_args()
Assert.False(o.OldColumn.IsUnicode);
Assert.True(o.OldColumn.IsFixedLength);
Assert.Equal(20, o.OldColumn.MaxLength);
+ Assert.Equal(5, o.OldColumn.Precision);
+ Assert.Equal(1, o.OldColumn.Scale);
Assert.True(o.OldColumn.IsRowVersion);
Assert.True(o.OldColumn.IsNullable);
Assert.Equal(0, o.OldColumn.DefaultValue);
@@ -723,6 +749,8 @@ public void AlterColumnOperation_computedColumnSql()
Assert.Null(o.OldColumn.IsUnicode);
Assert.Null(o.OldColumn.IsFixedLength);
Assert.Null(o.OldColumn.MaxLength);
+ Assert.Null(o.OldColumn.Precision);
+ Assert.Null(o.OldColumn.Scale);
Assert.False(o.OldColumn.IsRowVersion);
Assert.False(o.OldColumn.IsNullable);
Assert.Null(o.OldColumn.DefaultValue);
@@ -1134,6 +1162,8 @@ public void CreateTableOperation_Columns_all_args()
IsUnicode = false,
IsFixedLength = true,
MaxLength = 30,
+ Precision = 20,
+ Scale = 10,
IsRowVersion = true,
IsNullable = true,
DefaultValue = 1
@@ -1150,7 +1180,7 @@ public void CreateTableOperation_Columns_all_args()
+ _eol
+ " {"
+ _eol
- + " PostId = table.Column(name: \"Post Id\", type: \"int\", unicode: false, fixedLength: true, maxLength: 30, rowVersion: true, nullable: true, defaultValue: 1)"
+ + " PostId = table.Column(name: \"Post Id\", type: \"int\", unicode: false, fixedLength: true, maxLength: 30, precision: 20, scale: 10, rowVersion: true, nullable: true, defaultValue: 1)"
+ _eol
+ " },"
+ _eol
diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
index f94aedd94ec..7083dc8e5d0 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
@@ -43,6 +43,8 @@ public void Test_new_annotations_handled_for_entity_types()
var notForEntityType = new HashSet
{
CoreAnnotationNames.MaxLength,
+ CoreAnnotationNames.Precision,
+ CoreAnnotationNames.Scale,
CoreAnnotationNames.Unicode,
CoreAnnotationNames.ProductVersion,
CoreAnnotationNames.ValueGeneratorFactory,
@@ -169,6 +171,7 @@ public void Test_new_annotations_handled_for_properties()
var forProperty = new Dictionary
{
{ CoreAnnotationNames.MaxLength, (256, $@"{columnMapping}{_nl}.{nameof(PropertyBuilder.HasMaxLength)}(256)") },
+ { CoreAnnotationNames.Precision, (4, $@"{columnMapping}{_nl}.{nameof(PropertyBuilder.HasPrecision)}(4)") },
{ CoreAnnotationNames.Unicode, (false, $@"{columnMapping}{_nl}.{nameof(PropertyBuilder.IsUnicode)}(false)") },
{
CoreAnnotationNames.ValueConverter, (new ValueConverter(v => v, v => (int)v),
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs
index 5f72f73bd53..32cc80d21f9 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpDbContextGeneratorTest.cs
@@ -264,6 +264,33 @@ public void ValueGenerated_works()
});
}
+ [ConditionalFact]
+ public void HasPrecision_works()
+ {
+ Test(
+ modelBuilder => modelBuilder.Entity(
+ "Entity",
+ x =>
+ {
+ x.Property("HasPrecision").HasPrecision(12);
+ x.Property("HasPrecisionAndScale").HasPrecision(14, 7);
+ }),
+ new ModelCodeGenerationOptions(),
+ code =>
+ {
+ Assert.Contains("Property(e => e.HasPrecision).HasPrecision(12)", code.ContextFile.Code);
+ Assert.Contains("Property(e => e.HasPrecisionAndScale).HasPrecision(14, 7)", code.ContextFile.Code);
+ },
+ model =>
+ {
+ var entity = model.FindEntityType("TestNamespace.Entity");
+ Assert.Equal(12, entity.GetProperty("HasPrecision").GetPrecision());
+ Assert.Equal(0, entity.GetProperty("HasPrecision").GetScale());
+ Assert.Equal(14, entity.GetProperty("HasPrecisionAndScale").GetPrecision());
+ Assert.Equal(7, entity.GetProperty("HasPrecisionAndScale").GetScale());
+ });
+ }
+
private class TestCodeGeneratorPlugin : ProviderCodeGeneratorPlugin
{
public override MethodCallCodeFragment GenerateProviderOptions()
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs
index 39cbdbc50b4..e6266371a2c 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingTypeMapperSqlServerTest.cs
@@ -21,7 +21,7 @@ public void Maps_int_column(bool isKeyOrIndex)
{
var mapping = CreateMapper().FindMapping("int", isKeyOrIndex, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalTheory]
@@ -31,17 +31,37 @@ public void Maps_bigint_column(bool isKeyOrIndex)
{
var mapping = CreateMapper().FindMapping("bigint", isKeyOrIndex, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalTheory]
[InlineData(false)]
[InlineData(true)]
- public void Maps_decimal_column(bool isKeyOrIndex)
+ public void Maps_default_decimal_column(bool isKeyOrIndex)
{
- var mapping = CreateMapper().FindMapping("decimal(18, 2)", isKeyOrIndex, rowVersion: false);
+ var mapping = CreateMapper().FindMapping("decimal(18,2)", isKeyOrIndex, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
+ }
+
+ [ConditionalTheory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Maps_non_default_decimal_column(bool isKeyOrIndex)
+ {
+ var mapping = CreateMapper().FindMapping("decimal(14,3)", isKeyOrIndex, rowVersion: false);
+
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: 14, scale: 3);
+ }
+
+ [ConditionalTheory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Maps_numeric_column(bool isKeyOrIndex)
+ {
+ var mapping = CreateMapper().FindMapping("numeric(17,4)", isKeyOrIndex, rowVersion: false);
+
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalTheory]
@@ -51,7 +71,7 @@ public void Maps_bit_column(bool isKeyOrIndex)
{
var mapping = CreateMapper().FindMapping("bit", isKeyOrIndex, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalTheory]
@@ -61,7 +81,7 @@ public void Maps_datetime_column(bool isKeyOrIndex)
{
var mapping = CreateMapper().FindMapping("datetime", isKeyOrIndex, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalTheory]
@@ -71,7 +91,7 @@ public void Maps_datetime2_column(bool isKeyOrIndex)
{
var mapping = CreateMapper().FindMapping("datetime2", isKeyOrIndex, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -79,7 +99,7 @@ public void Maps_normal_varbinary_max_column()
{
var mapping = CreateMapper().FindMapping("varbinary(max)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -87,7 +107,7 @@ public void Maps_normal_varbinary_sized_column()
{
var mapping = CreateMapper().FindMapping("varbinary(200)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -95,7 +115,7 @@ public void Maps_normal_binary_sized_column()
{
var mapping = CreateMapper().FindMapping("binary(200)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -103,7 +123,7 @@ public void Maps_key_varbinary_max_column()
{
var mapping = CreateMapper().FindMapping("varbinary(max)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -111,7 +131,7 @@ public void Maps_key_varbinary_sized_column()
{
var mapping = CreateMapper().FindMapping("varbinary(200)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -119,7 +139,7 @@ public void Maps_key_varbinary_default_sized_column()
{
var mapping = CreateMapper().FindMapping("varbinary(900)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -127,7 +147,7 @@ public void Maps_key_binary_sized_column()
{
var mapping = CreateMapper().FindMapping("binary(200)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -135,7 +155,7 @@ public void Maps_key_binary_default_sized_column()
{
var mapping = CreateMapper().FindMapping("binary(900)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -143,7 +163,7 @@ public void Maps_rowversion_rowversion_column()
{
var mapping = CreateMapper().FindMapping("rowversion", keyOrIndex: false, rowVersion: true);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -151,7 +171,7 @@ public void Maps_rowversion_varbinary_max_column()
{
var mapping = CreateMapper().FindMapping("varbinary(max)", keyOrIndex: false, rowVersion: true);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -159,7 +179,7 @@ public void Maps_rowversion_varbinary_sized_column()
{
var mapping = CreateMapper().FindMapping("varbinary(200)", keyOrIndex: false, rowVersion: true);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -167,7 +187,7 @@ public void Maps_rowversion_varbinary_default_sized_column()
{
var mapping = CreateMapper().FindMapping("varbinary(8)", keyOrIndex: false, rowVersion: true);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -175,7 +195,7 @@ public void Maps_rowversion_binary_max_column()
{
var mapping = CreateMapper().FindMapping("binary(max)", keyOrIndex: false, rowVersion: true);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -183,7 +203,7 @@ public void Maps_rowversion_binary_sized_column()
{
var mapping = CreateMapper().FindMapping("binary(200)", keyOrIndex: false, rowVersion: true);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -191,7 +211,7 @@ public void Maps_rowversion_binary_default_sized_column()
{
var mapping = CreateMapper().FindMapping("binary(8)", keyOrIndex: false, rowVersion: true);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -199,7 +219,7 @@ public void Maps_normal_nvarchar_max_column()
{
var mapping = CreateMapper().FindMapping("nvarchar(max)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -207,7 +227,7 @@ public void Maps_normal_nvarchar_sized_column()
{
var mapping = CreateMapper().FindMapping("nvarchar(200)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -215,7 +235,7 @@ public void Maps_normal_varchar_max_column()
{
var mapping = CreateMapper().FindMapping("varchar(max)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: false, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: false, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -223,7 +243,7 @@ public void Maps_normal_varchar_sized_column()
{
var mapping = CreateMapper().FindMapping("varchar(200)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -231,7 +251,7 @@ public void Maps_key_nvarchar_max_column()
{
var mapping = CreateMapper().FindMapping("nvarchar(max)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -239,7 +259,7 @@ public void Maps_key_nvarchar_sized_column()
{
var mapping = CreateMapper().FindMapping("nvarchar(200)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -247,7 +267,7 @@ public void Maps_key_varchar_max_column()
{
var mapping = CreateMapper().FindMapping("varchar(max)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -255,7 +275,7 @@ public void Maps_key_varchar_sized_column()
{
var mapping = CreateMapper().FindMapping("varchar(200)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -263,7 +283,7 @@ public void Maps_key_nvarchar_default_sized_column()
{
var mapping = CreateMapper().FindMapping("nvarchar(450)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -271,7 +291,7 @@ public void Maps_key_varchar_default_sized_column()
{
var mapping = CreateMapper().FindMapping("varchar(900)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: false, fixedLength: null);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: false, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -279,7 +299,7 @@ public void Maps_normal_nchar_sized_column()
{
var mapping = CreateMapper().FindMapping("nchar(200)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -287,7 +307,7 @@ public void Maps_normal_char_sized_column()
{
var mapping = CreateMapper().FindMapping("char(200)", keyOrIndex: false, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -295,7 +315,7 @@ public void Maps_key_nchar_max_column()
{
var mapping = CreateMapper().FindMapping("nchar(max)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -303,7 +323,7 @@ public void Maps_key_nchar_sized_column()
{
var mapping = CreateMapper().FindMapping("nchar(200)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: null, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -311,7 +331,7 @@ public void Maps_key_char_max_column()
{
var mapping = CreateMapper().FindMapping("char(max)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
[ConditionalFact]
@@ -319,7 +339,7 @@ public void Maps_key_char_sized_column()
{
var mapping = CreateMapper().FindMapping("char(200)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: 200, unicode: false, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -327,7 +347,7 @@ public void Maps_key_nchar_default_sized_column()
{
var mapping = CreateMapper().FindMapping("nchar(450)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: null, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -335,7 +355,7 @@ public void Maps_key_char_default_sized_column()
{
var mapping = CreateMapper().FindMapping("char(900)", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: true, maxLength: null, unicode: false, fixedLength: true);
+ AssertMapping(mapping, inferred: true, maxLength: null, unicode: false, fixedLength: true, precision: null, scale: null);
}
[ConditionalFact]
@@ -343,16 +363,20 @@ public void Maps_text_column()
{
var mapping = CreateMapper().FindMapping("text", keyOrIndex: true, rowVersion: false);
- AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null);
+ AssertMapping(mapping, inferred: false, maxLength: null, unicode: null, fixedLength: null, precision: null, scale: null);
}
- private static void AssertMapping(TypeScaffoldingInfo mapping, bool inferred, int? maxLength, bool? unicode, bool? fixedLength)
+ private static void AssertMapping(
+ TypeScaffoldingInfo mapping, bool inferred, int? maxLength,
+ bool? unicode, bool? fixedLength, int? precision, int? scale)
{
Assert.Same(typeof(T), mapping.ClrType);
Assert.Equal(inferred, mapping.IsInferred);
Assert.Equal(maxLength, mapping.ScaffoldMaxLength);
Assert.Equal(unicode, mapping.ScaffoldUnicode);
Assert.Equal(fixedLength, mapping.ScaffoldFixedLength);
+ Assert.Equal(precision, mapping.ScaffoldPrecision);
+ Assert.Equal(scale, mapping.ScaffoldScale);
}
private static ScaffoldingTypeMapper CreateMapper()
diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs
index 25d28b7688c..cdc9f5f7d8e 100644
--- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs
+++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs
@@ -2057,6 +2057,70 @@ public void Alter_column_max_length()
});
}
+ [ConditionalFact]
+ public void Alter_column_precision()
+ {
+ Execute(
+ source => source.Entity(
+ "Toad",
+ x =>
+ {
+ x.Property("Id");
+ x.Property("Salary");
+ }),
+ target => target.Entity(
+ "Toad",
+ x =>
+ {
+ x.Property("Id");
+ x.Property("Salary")
+ .HasPrecision(10);
+ }),
+ operations =>
+ {
+ Assert.Equal(1, operations.Count);
+
+ var operation = Assert.IsType(operations[0]);
+ Assert.Equal("Toad", operation.Table);
+ Assert.Equal("Salary", operation.Name);
+ Assert.Equal(10, operation.Precision);
+ Assert.Equal(0, operation.Scale);
+ Assert.True(operation.IsDestructiveChange);
+ });
+ }
+
+ [ConditionalFact]
+ public void Alter_column_precision_and_scale()
+ {
+ Execute(
+ source => source.Entity(
+ "Toad",
+ x =>
+ {
+ x.Property("Id");
+ x.Property("Salary");
+ }),
+ target => target.Entity(
+ "Toad",
+ x =>
+ {
+ x.Property("Id");
+ x.Property("Salary")
+ .HasPrecision(17, 5);
+ }),
+ operations =>
+ {
+ Assert.Equal(1, operations.Count);
+
+ var operation = Assert.IsType(operations[0]);
+ Assert.Equal("Toad", operation.Table);
+ Assert.Equal("Salary", operation.Name);
+ Assert.Equal(17, operation.Precision);
+ Assert.Equal(5, operation.Scale);
+ Assert.True(operation.IsDestructiveChange);
+ });
+ }
+
[ConditionalFact]
public void Alter_column_unicode()
{
diff --git a/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs b/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs
index bbd8cc43f5c..1cb744fe88c 100644
--- a/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs
+++ b/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs
@@ -94,6 +94,31 @@ public virtual void AddColumnOperation_with_maxLength_no_model()
IsNullable = true
});
+ [ConditionalFact]
+ public virtual void AddColumnOperation_with_precision_and_scale_overridden()
+ => Generate(
+ modelBuilder => modelBuilder.Entity().Property("Pi").HasPrecision(30, 17),
+ new AddColumnOperation
+ {
+ Table = "Person",
+ Name = "Pi",
+ ClrType = typeof(decimal),
+ Precision = 15,
+ Scale = 10
+ });
+
+ [ConditionalFact]
+ public virtual void AddColumnOperation_with_precision_and_scale_no_model()
+ => Generate(
+ new AddColumnOperation
+ {
+ Table = "Person",
+ Name = "Pi",
+ ClrType = typeof(decimal),
+ Precision = 20,
+ Scale = 7
+ });
+
[ConditionalFact]
public virtual void AddForeignKeyOperation_without_principal_columns()
=> Generate(
@@ -203,6 +228,7 @@ protected class Person
{
public int Id { get; set; }
public string Name { get; set; }
+ public decimal Pi { get; set; }
}
}
}
diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs
index 7be1719f597..396717ed0e5 100644
--- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs
+++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs
@@ -145,6 +145,39 @@ public void Key_with_store_type_is_picked_up_by_FK()
GetMapping(mapper, model.FindEntityType(typeof(MyRelatedType1)).FindProperty("Relationship1Id")).StoreType);
}
+ [ConditionalFact]
+ public void Does_default_type_mapping_from_decimal()
+ {
+ var model = CreateModel();
+ var mapper = CreateTestTypeMapper();
+
+ Assert.Equal(
+ "default_decimal_mapping",
+ GetMapping(mapper, model.FindEntityType(typeof(MyPrecisionType)).FindProperty("Id")).StoreType);
+ }
+
+ [ConditionalFact]
+ public void Does_type_mapping_from_decimal_with_precision_only()
+ {
+ var model = CreateModel();
+ var mapper = CreateTestTypeMapper();
+
+ Assert.Equal(
+ "decimal_mapping(16)",
+ GetMapping(mapper, model.FindEntityType(typeof(MyPrecisionType)).FindProperty("PrecisionOnly")).StoreType);
+ }
+
+ [ConditionalFact]
+ public void Does_type_mapping_from_decimal_with_precision_and_scale()
+ {
+ var model = CreateModel();
+ var mapper = CreateTestTypeMapper();
+
+ Assert.Equal(
+ "decimal_mapping(18,7)",
+ GetMapping(mapper, model.FindEntityType(typeof(MyPrecisionType)).FindProperty("PrecisionAndScale")).StoreType);
+ }
+
private static IRelationalTypeMappingSource CreateTestTypeMapper()
=> new TestRelationalTypeMappingSource(
TestServiceFactory.Instance.Create(),
@@ -214,7 +247,7 @@ public void Key_store_type_is_preferred_if_specified()
GetMapping(mapper, model.FindEntityType(typeof(MyType)).FindProperty("Id")).StoreType);
Assert.Equal(
- "dec(6,1)",
+ "decimal_mapping(6,1)",
GetMapping(mapper, model.FindEntityType(typeof(MyRelatedType1)).FindProperty("Relationship2Id")).StoreType);
}
diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs
index 13827eaee7e..b0021aea033 100644
--- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs
+++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs
@@ -22,6 +22,8 @@ protected IMutableModel CreateModel()
builder.Entity().Property(e => e.Id).IsUnicode(false);
builder.Entity().Property(e => e.Relationship2Id).HasMaxLength(767);
builder.Entity().Property(e => e.Relationship2Id).IsUnicode();
+ builder.Entity().Property(e => e.PrecisionOnly).HasPrecision(16);
+ builder.Entity().Property(e => e.PrecisionAndScale).HasPrecision(18, 7);
return builder.Model;
}
@@ -33,6 +35,13 @@ protected class MyType
public decimal Id { get; set; }
}
+ protected class MyPrecisionType
+ {
+ public decimal Id { get; set; }
+ public decimal PrecisionOnly { get; set; }
+ public decimal PrecisionAndScale { get; set; }
+ }
+
protected class MyRelatedType1
{
public string Id { get; set; }
diff --git a/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs b/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs
index ae0e1bc7de4..db44bfe338c 100644
--- a/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs
+++ b/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs
@@ -196,6 +196,30 @@ protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInf
size);
}
+ if (clrType == typeof(decimal)
+ && !string.Equals("money", storeTypeName, StringComparison.Ordinal))
+ {
+ var precision = mappingInfo.Precision;
+ var scale = mappingInfo.Scale;
+ if (precision == _defaultDecimalMapping.Precision
+ && scale == _defaultDecimalMapping.Scale)
+ {
+ return _defaultDecimalMapping;
+ }
+
+ if (scale == null || scale == 0)
+ {
+ return new DecimalTypeMapping(
+ "decimal_mapping(" + precision + ")",
+ precision: precision);
+ }
+
+ return new DecimalTypeMapping(
+ "decimal_mapping(" + precision + "," + scale + ")",
+ precision: precision,
+ scale: scale);
+ }
+
if (_simpleMappings.TryGetValue(clrType, out var mapping))
{
return storeTypeName != null
@@ -211,5 +235,8 @@ protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInf
? mappingFromName
: null;
}
+
+ protected override bool StoreTypeNameBaseUsesPrecision(string storeTypeNameBase)
+ => "default_decimal_mapping" == storeTypeNameBase;
}
}
diff --git a/test/EFCore.Relational.Tests/Utilities/DbParameterCollectionExtensionsTest.cs b/test/EFCore.Relational.Tests/Utilities/DbParameterCollectionExtensionsTest.cs
index 783876adc54..12eccb3a9be 100644
--- a/test/EFCore.Relational.Tests/Utilities/DbParameterCollectionExtensionsTest.cs
+++ b/test/EFCore.Relational.Tests/Utilities/DbParameterCollectionExtensionsTest.cs
@@ -514,7 +514,7 @@ public void Formats_object_parameter_with_unusual_type()
public void Formats_DateTime_parameter()
{
Assert.Equal(
- "@param='1973-09-03T00:00:00'",
+ "@param='1973-09-03T00:00:00.0000000'",
DbParameterCollectionExtensions.FormatParameter(
"@param", new DateTime(1973, 9, 3), true, ParameterDirection.Input, DbType.DateTime2, false, 0, 0, 0));
}
@@ -523,7 +523,7 @@ public void Formats_DateTime_parameter()
public void Formats_DateTime_parameter_with_unusual_type()
{
Assert.Equal(
- "@param='1973-09-03T00:00:00' (DbType = DateTime)",
+ "@param='1973-09-03T00:00:00.0000000' (DbType = DateTime)",
DbParameterCollectionExtensions.FormatParameter(
"@param", new DateTime(1973, 9, 3), true, ParameterDirection.Input, DbType.DateTime, false, 0, 0, 0));
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs
index 51a920c0d80..09a7de7e2d0 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs
@@ -676,15 +676,15 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types()
@p12='D' (Nullable = false) (Size = 1)
@p13='G' (Nullable = false) (Size = 1) (DbType = AnsiString)
@p14='A' (Nullable = false) (Size = 1) (DbType = AnsiString)
-@p15='2015-01-02T10:11:12' (DbType = Date)
-@p16='2019-01-02T14:11:12' (DbType = DateTime)
-@p17='2017-01-02T12:11:12'
-@p18='2018-01-02T13:11:12' (DbType = DateTime)
-@p19='2016-01-02T11:11:12.0000000+00:00'
-@p20='101.1'
-@p21='102.2'
+@p15='2015-01-02T10:11:12.0000000' (DbType = Date)
+@p16='2019-01-02T14:11:12.0000000' (DbType = DateTime)
+@p17='2017-01-02T12:11:12.1234567'
+@p18='2018-01-02T13:11:12.0000000' (DbType = DateTime)
+@p19='2016-01-02T11:11:12.1234567+00:00'
+@p20='101.1' (Precision = 18) (Scale = 2)
+@p21='102.2' (Precision = 18) (Scale = 2)
@p22='81.1'
-@p23='103.3'
+@p23='103.3' (Precision = 18) (Scale = 2)
@p24='82.2'
@p25='85.5'
@p26='83.3'
@@ -716,7 +716,7 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types()
@p48='4294967295'
@p49='-1'
@p50='-1'
-@p51='18446744073709551615'",
+@p51='18446744073709551615' (Precision = 20)",
parameters,
ignoreLineEndingDifferences: true);
@@ -747,8 +747,8 @@ private static void AssertMappedDataTypes(MappedDataTypes entity, int id)
Assert.Equal(84.4f, entity.FloatAsReal);
Assert.Equal(85.5, entity.DoubleAsDoublePrecision);
Assert.Equal(new DateTime(2015, 1, 2), entity.DateTimeAsDate);
- Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
- Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12), entity.DateTimeAsDatetime2);
+ Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(1234567), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
+ Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(1234567), entity.DateTimeAsDatetime2);
Assert.Equal(new DateTime(2018, 1, 2, 13, 11, 00), entity.DateTimeAsSmalldatetime);
Assert.Equal(new DateTime(2019, 1, 2, 14, 11, 12), entity.DateTimeAsDatetime);
Assert.Equal(new TimeSpan(11, 15, 12), entity.TimeSpanAsTime);
@@ -804,8 +804,8 @@ private static MappedDataTypes CreateMappedDataTypes(int id)
FloatAsReal = 84.4f,
DoubleAsDoublePrecision = 85.5,
DateTimeAsDate = new DateTime(2015, 1, 2, 10, 11, 12),
- DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero),
- DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12),
+ DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(1234567), TimeSpan.Zero),
+ DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(1234567),
DateTimeAsSmalldatetime = new DateTime(2018, 1, 2, 13, 11, 12),
DateTimeAsDatetime = new DateTime(2019, 1, 2, 14, 11, 12),
TimeSpanAsTime = new TimeSpan(11, 15, 12),
@@ -870,15 +870,15 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types()
@p12='D' (Size = 1)
@p13='G' (Size = 1) (DbType = AnsiString)
@p14='A' (Size = 1) (DbType = AnsiString)
-@p15='2015-01-02T10:11:12' (Nullable = true) (DbType = Date)
-@p16='2019-01-02T14:11:12' (Nullable = true) (DbType = DateTime)
-@p17='2017-01-02T12:11:12' (Nullable = true)
-@p18='2018-01-02T13:11:12' (Nullable = true) (DbType = DateTime)
-@p19='2016-01-02T11:11:12.0000000+00:00' (Nullable = true)
-@p20='101.1' (Nullable = true)
-@p21='102.2' (Nullable = true)
+@p15='2015-01-02T10:11:12.0000000' (Nullable = true) (DbType = Date)
+@p16='2019-01-02T14:11:12.0000000' (Nullable = true) (DbType = DateTime)
+@p17='2017-01-02T12:11:12.9876543' (Nullable = true)
+@p18='2018-01-02T13:11:12.0000000' (Nullable = true) (DbType = DateTime)
+@p19='2016-01-02T11:11:12.9876543+00:00' (Nullable = true)
+@p20='101.1' (Nullable = true) (Precision = 18) (Scale = 2)
+@p21='102.2' (Nullable = true) (Precision = 18) (Scale = 2)
@p22='81.1' (Nullable = true)
-@p23='103.3' (Nullable = true)
+@p23='103.3' (Nullable = true) (Precision = 18) (Scale = 2)
@p24='82.2' (Nullable = true)
@p25='85.5' (Nullable = true)
@p26='83.3' (Nullable = true)
@@ -906,7 +906,7 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types()
@p48='4294967295' (Nullable = true)
@p49='-1' (Nullable = true)
@p50='-1' (Nullable = true)
-@p51='18446744073709551615' (Nullable = true)",
+@p51='18446744073709551615' (Nullable = true) (Precision = 20)",
parameters,
ignoreLineEndingDifferences: true);
@@ -933,8 +933,8 @@ private static void AssertMappedNullableDataTypes(MappedNullableDataTypes entity
Assert.Equal(84.4f, entity.FloatAsReal);
Assert.Equal(85.5, entity.DoubleAsDoublePrecision);
Assert.Equal(new DateTime(2015, 1, 2), entity.DateTimeAsDate);
- Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
- Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12), entity.DateTimeAsDatetime2);
+ Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(9876543), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
+ Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(9876543), entity.DateTimeAsDatetime2);
Assert.Equal(new DateTime(2018, 1, 2, 13, 11, 00), entity.DateTimeAsSmalldatetime);
Assert.Equal(new DateTime(2019, 1, 2, 14, 11, 12), entity.DateTimeAsDatetime);
Assert.Equal(new TimeSpan(11, 15, 12), entity.TimeSpanAsTime);
@@ -990,8 +990,8 @@ private static MappedNullableDataTypes CreateMappedNullableDataTypes(int id)
FloatAsReal = 84.4f,
DoubleAsDoublePrecision = 85.5,
DateTimeAsDate = new DateTime(2015, 1, 2, 10, 11, 12),
- DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero),
- DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12),
+ DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(9876543), TimeSpan.Zero),
+ DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(9876543),
DateTimeAsSmalldatetime = new DateTime(2018, 1, 2, 13, 11, 12),
DateTimeAsDatetime = new DateTime(2019, 1, 2, 14, 11, 12),
TimeSpanAsTime = new TimeSpan(11, 15, 12),
@@ -1061,10 +1061,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null()
@p17=NULL (DbType = DateTime2)
@p18=NULL (DbType = DateTime)
@p19=NULL (DbType = DateTimeOffset)
-@p20=NULL
-@p21=NULL
+@p20=NULL (Precision = 18) (Scale = 2)
+@p21=NULL (Precision = 18) (Scale = 2)
@p22=NULL
-@p23=NULL
+@p23=NULL (Precision = 18) (Scale = 2)
@p24=NULL
@p25=NULL
@p26=NULL
@@ -1092,7 +1092,7 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null()
@p48=NULL (DbType = Int64)
@p49=NULL (DbType = Int32)
@p50=NULL (DbType = Int64)
-@p51=NULL",
+@p51=NULL (Precision = 20)",
parameters,
ignoreLineEndingDifferences: true);
@@ -1326,11 +1326,11 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_scale()
var parameters = DumpParameters();
Assert.Equal(
@"@p0='77'
-@p1='2017-01-02T12:11:12' (Size = 3)
-@p2='2016-01-02T11:11:12.0000000+00:00' (Size = 3)
-@p3='102.2' (Size = 3)
-@p4='101.1' (Size = 3)
-@p5='103.3' (Size = 3)
+@p1='2017-01-02T12:11:12.3210000' (Precision = 3)
+@p2='2016-01-02T11:11:12.7650000+00:00' (Precision = 3)
+@p3='102' (Precision = 3)
+@p4='101' (Precision = 3)
+@p5='103' (Precision = 3)
@p6='85.55000305175781' (Size = 25)
@p7='85.5' (Size = 3)
@p8='83.33000183105469' (Size = 25)
@@ -1351,8 +1351,8 @@ private static void AssertMappedScaledDataTypes(MappedScaledDataTypes entity, in
Assert.Equal(85.5f, entity.FloatAsDoublePrecision3);
Assert.Equal(83.33f, entity.FloatAsFloat25);
Assert.Equal(85.55f, entity.FloatAsDoublePrecision25);
- Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset3);
- Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12), entity.DateTimeAsDatetime23);
+ Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12, 765), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset3);
+ Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12, 321), entity.DateTimeAsDatetime23);
Assert.Equal(101m, entity.DecimalAsDecimal3);
Assert.Equal(102m, entity.DecimalAsDec3);
Assert.Equal(103m, entity.DecimalAsNumeric3);
@@ -1366,11 +1366,11 @@ private static MappedScaledDataTypes CreateMappedScaledDataTypes(int id)
FloatAsDoublePrecision3 = 85.5f,
FloatAsFloat25 = 83.33f,
FloatAsDoublePrecision25 = 85.55f,
- DateTimeOffsetAsDatetimeoffset3 = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero),
- DateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12),
- DecimalAsDecimal3 = 101.1m,
- DecimalAsDec3 = 102.2m,
- DecimalAsNumeric3 = 103.3m
+ DateTimeOffsetAsDatetimeoffset3 = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12, 765), TimeSpan.Zero),
+ DateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12, 321),
+ DecimalAsDecimal3 = 101m,
+ DecimalAsDec3 = 102m,
+ DecimalAsNumeric3 = 103m
};
[ConditionalFact]
@@ -1386,9 +1386,9 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_precisio
var parameters = DumpParameters();
Assert.Equal(
@"@p0='77'
-@p1='102.2'
-@p2='101.1'
-@p3='103.3'",
+@p1='102.2' (Precision = 5) (Scale = 2)
+@p2='101.1' (Precision = 5) (Scale = 2)
+@p3='103.3' (Precision = 5) (Scale = 2)",
parameters,
ignoreLineEndingDifferences: true);
@@ -1441,15 +1441,15 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_identity
@p11='D' (Nullable = false) (Size = 1)
@p12='G' (Nullable = false) (Size = 1) (DbType = AnsiString)
@p13='A' (Nullable = false) (Size = 1) (DbType = AnsiString)
-@p14='2015-01-02T10:11:12' (DbType = Date)
-@p15='2019-01-02T14:11:12' (DbType = DateTime)
-@p16='2017-01-02T12:11:12'
-@p17='2018-01-02T13:11:12' (DbType = DateTime)
-@p18='2016-01-02T11:11:12.0000000+00:00'
-@p19='101.1'
-@p20='102.2'
+@p14='2015-01-02T10:11:12.0000000' (DbType = Date)
+@p15='2019-01-02T14:11:12.0000000' (DbType = DateTime)
+@p16='2017-01-02T12:11:12.7654321'
+@p17='2018-01-02T13:11:12.0000000' (DbType = DateTime)
+@p18='2016-01-02T11:11:12.7654321+00:00'
+@p19='101.1' (Precision = 18) (Scale = 2)
+@p20='102.2' (Precision = 18) (Scale = 2)
@p21='81.1'
-@p22='103.3'
+@p22='103.3' (Precision = 18) (Scale = 2)
@p23='82.2'
@p24='85.5'
@p25='83.3'
@@ -1478,7 +1478,7 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_identity
@p48='4294967295'
@p49='-1'
@p50='-1'
-@p51='18446744073709551615'",
+@p51='18446744073709551615' (Precision = 20)",
parameters,
ignoreLineEndingDifferences: true);
@@ -1505,8 +1505,8 @@ private static void AssertMappedDataTypesWithIdentity(MappedDataTypesWithIdentit
Assert.Equal(84.4f, entity.FloatAsReal);
Assert.Equal(85.5, entity.DoubleAsDoublePrecision);
Assert.Equal(new DateTime(2015, 1, 2), entity.DateTimeAsDate);
- Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
- Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12), entity.DateTimeAsDatetime2);
+ Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(7654321), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
+ Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(7654321), entity.DateTimeAsDatetime2);
Assert.Equal(new DateTime(2018, 1, 2, 13, 11, 00), entity.DateTimeAsSmalldatetime);
Assert.Equal(new DateTime(2019, 1, 2, 14, 11, 12), entity.DateTimeAsDatetime);
Assert.Equal(new TimeSpan(11, 15, 12), entity.TimeSpanAsTime);
@@ -1562,8 +1562,8 @@ private static MappedDataTypesWithIdentity CreateMappedDataTypesWithIdentity(int
FloatAsReal = 84.4f,
DoubleAsDoublePrecision = 85.5,
DateTimeAsDate = new DateTime(2015, 1, 2, 10, 11, 12),
- DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero),
- DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12),
+ DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(7654321), TimeSpan.Zero),
+ DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(7654321),
DateTimeAsSmalldatetime = new DateTime(2018, 1, 2, 13, 11, 12),
DateTimeAsDatetime = new DateTime(2019, 1, 2, 14, 11, 12),
TimeSpanAsTime = new TimeSpan(11, 15, 12),
@@ -1627,15 +1627,15 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types_with
@p11='D' (Size = 1)
@p12='G' (Size = 1) (DbType = AnsiString)
@p13='A' (Size = 1) (DbType = AnsiString)
-@p14='2015-01-02T10:11:12' (Nullable = true) (DbType = Date)
-@p15='2019-01-02T14:11:12' (Nullable = true) (DbType = DateTime)
-@p16='2017-01-02T12:11:12' (Nullable = true)
-@p17='2018-01-02T13:11:12' (Nullable = true) (DbType = DateTime)
-@p18='2016-01-02T11:11:12.0000000+00:00' (Nullable = true)
-@p19='101.1' (Nullable = true)
-@p20='102.2' (Nullable = true)
+@p14='2015-01-02T10:11:12.0000000' (Nullable = true) (DbType = Date)
+@p15='2019-01-02T14:11:12.0000000' (Nullable = true) (DbType = DateTime)
+@p16='2017-01-02T12:11:12.2345678' (Nullable = true)
+@p17='2018-01-02T13:11:12.0000000' (Nullable = true) (DbType = DateTime)
+@p18='2016-01-02T11:11:12.2345678+00:00' (Nullable = true)
+@p19='101.1' (Nullable = true) (Precision = 18) (Scale = 2)
+@p20='102.2' (Nullable = true) (Precision = 18) (Scale = 2)
@p21='81.1' (Nullable = true)
-@p22='103.3' (Nullable = true)
+@p22='103.3' (Nullable = true) (Precision = 18) (Scale = 2)
@p23='82.2' (Nullable = true)
@p24='85.5' (Nullable = true)
@p25='83.3' (Nullable = true)
@@ -1663,7 +1663,7 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types_with
@p47='4294967295' (Nullable = true)
@p48='-1' (Nullable = true)
@p49='-1' (Nullable = true)
-@p50='18446744073709551615' (Nullable = true)
+@p50='18446744073709551615' (Nullable = true) (Precision = 20)
@p51='-1' (Nullable = true)",
parameters,
ignoreLineEndingDifferences: true);
@@ -1691,8 +1691,8 @@ private static void AssertMappedNullableDataTypesWithIdentity(MappedNullableData
Assert.Equal(84.4f, entity.FloatAsReal);
Assert.Equal(85.5, entity.DoubleAsDoublePrecision);
Assert.Equal(new DateTime(2015, 1, 2), entity.DateTimeAsDate);
- Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
- Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12), entity.DateTimeAsDatetime2);
+ Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(2345678), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset);
+ Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(2345678), entity.DateTimeAsDatetime2);
Assert.Equal(new DateTime(2018, 1, 2, 13, 11, 00), entity.DateTimeAsSmalldatetime);
Assert.Equal(new DateTime(2019, 1, 2, 14, 11, 12), entity.DateTimeAsDatetime);
Assert.Equal(new TimeSpan(11, 15, 12), entity.TimeSpanAsTime);
@@ -1748,8 +1748,8 @@ private static MappedNullableDataTypesWithIdentity CreateMappedNullableDataTypes
FloatAsReal = 84.4f,
DoubleAsDoublePrecision = 85.5,
DateTimeAsDate = new DateTime(2015, 1, 2, 10, 11, 12),
- DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero),
- DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12),
+ DateTimeOffsetAsDatetimeoffset = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12).AddTicks(2345678), TimeSpan.Zero),
+ DateTimeAsDatetime2 = new DateTime(2017, 1, 2, 12, 11, 12).AddTicks(2345678),
DateTimeAsSmalldatetime = new DateTime(2018, 1, 2, 13, 11, 12),
DateTimeAsDatetime = new DateTime(2019, 1, 2, 14, 11, 12),
TimeSpanAsTime = new TimeSpan(11, 15, 12),
@@ -1818,10 +1818,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null_w
@p16=NULL (DbType = DateTime2)
@p17=NULL (DbType = DateTime)
@p18=NULL (DbType = DateTimeOffset)
-@p19=NULL
-@p20=NULL
+@p19=NULL (Precision = 18) (Scale = 2)
+@p20=NULL (Precision = 18) (Scale = 2)
@p21=NULL
-@p22=NULL
+@p22=NULL (Precision = 18) (Scale = 2)
@p23=NULL
@p24=NULL
@p25=NULL
@@ -1849,7 +1849,7 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null_w
@p47=NULL (DbType = Int64)
@p48=NULL (DbType = Int32)
@p49=NULL (DbType = Int64)
-@p50=NULL
+@p50=NULL (Precision = 20)
@p51=NULL (DbType = Int16)",
parameters,
ignoreLineEndingDifferences: true);
@@ -2085,11 +2085,11 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_scale_wi
var parameters = DumpParameters();
Assert.Equal(
- @"@p0='2017-01-02T12:11:12' (Size = 3)
-@p1='2016-01-02T11:11:12.0000000+00:00' (Size = 3)
-@p2='102.2' (Size = 3)
-@p3='101.1' (Size = 3)
-@p4='103.3' (Size = 3)
+ @"@p0='2017-01-02T12:11:12.1230000' (Precision = 3)
+@p1='2016-01-02T11:11:12.5670000+00:00' (Precision = 3)
+@p2='102' (Precision = 3)
+@p3='101' (Precision = 3)
+@p4='103' (Precision = 3)
@p5='85.55000305175781' (Size = 25)
@p6='85.5' (Size = 3)
@p7='83.33000183105469' (Size = 25)
@@ -2111,8 +2111,8 @@ private static void AssertMappedScaledDataTypesWithIdentity(MappedScaledDataType
Assert.Equal(85.5f, entity.FloatAsDoublePrecision3);
Assert.Equal(83.33f, entity.FloatAsFloat25);
Assert.Equal(85.55f, entity.FloatAsDoublePrecision25);
- Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset3);
- Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12), entity.DateTimeAsDatetime23);
+ Assert.Equal(new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12, 567), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset3);
+ Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12, 123), entity.DateTimeAsDatetime23);
Assert.Equal(101m, entity.DecimalAsDecimal3);
Assert.Equal(102m, entity.DecimalAsDec3);
Assert.Equal(103m, entity.DecimalAsNumeric3);
@@ -2126,11 +2126,11 @@ private static MappedScaledDataTypesWithIdentity CreateMappedScaledDataTypesWith
FloatAsDoublePrecision3 = 85.5f,
FloatAsFloat25 = 83.33f,
FloatAsDoublePrecision25 = 85.55f,
- DateTimeOffsetAsDatetimeoffset3 = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12), TimeSpan.Zero),
- DateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12),
- DecimalAsDecimal3 = 101.1m,
- DecimalAsDec3 = 102.2m,
- DecimalAsNumeric3 = 103.3m
+ DateTimeOffsetAsDatetimeoffset3 = new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12, 567), TimeSpan.Zero),
+ DateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12, 123),
+ DecimalAsDecimal3 = 101m,
+ DecimalAsDec3 = 102m,
+ DecimalAsNumeric3 = 103m
};
[ConditionalFact]
@@ -2146,9 +2146,9 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_precisio
var parameters = DumpParameters();
Assert.Equal(
- @"@p0='102.2'
-@p1='101.1'
-@p2='103.3'
+ @"@p0='102.2' (Precision = 5) (Scale = 2)
+@p1='101.1' (Precision = 5) (Scale = 2)
+@p2='103.3' (Precision = 5) (Scale = 2)
@p3='77'",
parameters,
ignoreLineEndingDifferences: true);
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs
index 4c25fa22175..58e0c3d99b7 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs
@@ -178,8 +178,8 @@ public override void FromSqlRaw_queryable_multiple_composed_with_closure_paramet
base.FromSqlRaw_queryable_multiple_composed_with_closure_parameters();
AssertSql(
- @"p0='1997-01-01T00:00:00'
-p1='1998-01-01T00:00:00'
+ @"p0='1997-01-01T00:00:00.0000000'
+p1='1998-01-01T00:00:00.0000000'
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM (
@@ -197,8 +197,8 @@ public override void FromSqlRaw_queryable_multiple_composed_with_parameters_and_
AssertSql(
@"p0='London' (Size = 4000)
-p1='1997-01-01T00:00:00'
-p2='1998-01-01T00:00:00'
+p1='1997-01-01T00:00:00.0000000'
+p2='1998-01-01T00:00:00.0000000'
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM (
@@ -210,8 +210,8 @@ CROSS JOIN (
WHERE [c].[CustomerID] = [o].[CustomerID]",
//
@"p0='Berlin' (Size = 4000)
-p1='1998-04-01T00:00:00'
-p2='1998-05-01T00:00:00'
+p1='1998-04-01T00:00:00.0000000'
+p2='1998-05-01T00:00:00.0000000'
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM (
@@ -296,8 +296,8 @@ public override void FromSqlInterpolated_queryable_multiple_composed_with_parame
AssertSql(
@"p0='London' (Size = 4000)
-p1='1997-01-01T00:00:00'
-p2='1998-01-01T00:00:00'
+p1='1997-01-01T00:00:00.0000000'
+p2='1998-01-01T00:00:00.0000000'
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM (
@@ -309,8 +309,8 @@ CROSS JOIN (
WHERE [c].[CustomerID] = [o].[CustomerID]",
//
@"p0='Berlin' (Size = 4000)
-p1='1998-04-01T00:00:00'
-p2='1998-05-01T00:00:00'
+p1='1998-04-01T00:00:00.0000000'
+p2='1998-05-01T00:00:00.0000000'
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM (
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
index a7cb439b0d3..bfe3dd71b5a 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
@@ -6630,7 +6630,7 @@ public override async Task DateTimeOffset_Date_returns_datetime(bool async)
await base.DateTimeOffset_Date_returns_datetime(async);
AssertSql(
- @"@__dateTimeOffset_Date_0='0002-03-01T00:00:00'
+ @"@__dateTimeOffset_Date_0='0002-03-01T00:00:00.0000000'
SELECT [m].[Id], [m].[CodeName], [m].[Duration], [m].[Rating], [m].[Timeline]
FROM [Missions] AS [m]
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs
index e5bf93faff7..f8299461b41 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServerTest.cs
@@ -723,7 +723,7 @@ await AssertCount(
c => dateTime > new DateTime(DateTime.Now.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond));
AssertSql(
- @$"@__dateTime_0='1919-12-12T10:20:15' (DbType = DateTime)
+ @$"@__dateTime_0='1919-12-12T10:20:15.0000000' (DbType = DateTime)
@__dateTime_Month_2='12'
@__dateTime_Day_3='12'
@__dateTime_Hour_4='10'
@@ -783,7 +783,7 @@ await AssertCount(
c => date > new DateTime(DateTime.Now.Year, date.Month, date.Day));
AssertSql(
- @$"@__date_0='1919-12-12T00:00:00' (DbType = Date)
+ @$"@__date_0='1919-12-12T00:00:00.0000000' (DbType = Date)
@__date_Month_2='12'
@__date_Day_3='12'
@@ -839,7 +839,7 @@ public virtual void DateTime2FromParts_compare_with_local_variable()
Assert.Equal(0, count);
AssertSql(
- @$"@__dateTime_0='1919-12-12T10:20:15'
+ @$"@__dateTime_0='1919-12-12T10:20:15.0000000'
@__dateTime_Month_2='12'
@__dateTime_Day_3='12'
@__dateTime_Hour_4='10'
@@ -965,7 +965,7 @@ await AssertCount(
c => dateTime > new DateTime(DateTime.Now.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0));
AssertSql(
- @$"@__dateTime_0='1919-12-12T23:20:00' (DbType = DateTime)
+ @$"@__dateTime_0='1919-12-12T23:20:00.0000000' (DbType = DateTime)
@__dateTime_Month_2='12'
@__dateTime_Day_3='12'
@__dateTime_Hour_4='23'
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs
index 47660310fe5..2dbc3e0fd1e 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs
@@ -1395,7 +1395,7 @@ public override async Task Static_equals_nullable_datetime_compared_to_non_nulla
await base.Static_equals_nullable_datetime_compared_to_non_nullable(async);
AssertSql(
- @"@__arg_0='1996-07-04T00:00:00'
+ @"@__arg_0='1996-07-04T00:00:00.0000000'
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs
index 75783a7f824..43ca415c325 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs
@@ -2696,7 +2696,7 @@ public override async Task DateTime_parse_is_parameterized_when_from_closure(boo
await base.DateTime_parse_is_parameterized_when_from_closure(async);
AssertSql(
- @"@__Parse_0='1998-01-01T12:00:00' (Nullable = true) (DbType = DateTime)
+ @"@__Parse_0='1998-01-01T12:00:00.0000000' (Nullable = true) (DbType = DateTime)
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
@@ -2718,13 +2718,13 @@ public override async Task New_DateTime_is_parameterized_when_from_closure(bool
await base.New_DateTime_is_parameterized_when_from_closure(async);
AssertSql(
- @"@__p_0='1998-01-01T12:00:00' (Nullable = true) (DbType = DateTime)
+ @"@__p_0='1998-01-01T12:00:00.0000000' (Nullable = true) (DbType = DateTime)
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE [o].[OrderDate] > @__p_0",
//
- @"@__p_0='1998-01-01T11:00:00' (Nullable = true) (DbType = DateTime)
+ @"@__p_0='1998-01-01T11:00:00.0000000' (Nullable = true) (DbType = DateTime)
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
index fd392709b44..e50cb387860 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
@@ -711,7 +711,7 @@ public override async Task Where_datetime_now(bool async)
await base.Where_datetime_now(async);
AssertSql(
- @"@__myDatetime_0='2015-04-10T00:00:00'
+ @"@__myDatetime_0='2015-04-10T00:00:00.0000000'
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
@@ -723,7 +723,7 @@ public override async Task Where_datetime_utcnow(bool async)
await base.Where_datetime_utcnow(async);
AssertSql(
- @"@__myDatetime_0='2015-04-10T00:00:00'
+ @"@__myDatetime_0='2015-04-10T00:00:00.0000000'
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
@@ -745,7 +745,7 @@ public override async Task Where_datetime_date_component(bool async)
await base.Where_datetime_date_component(async);
AssertSql(
- @"@__myDatetime_0='1998-05-04T00:00:00' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs
index acf87173385..c3cf4752b2a 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs
@@ -114,7 +114,7 @@ public override void Scalar_Function_Where_Not_Correlated_Static()
base.Scalar_Function_Where_Not_Correlated_Static();
AssertSql(
- @"@__startDate_0='2000-04-01T00:00:00' (Nullable = true)
+ @"@__startDate_0='2000-04-01T00:00:00.0000000' (Nullable = true)
SELECT TOP(2) [c].[Id]
FROM [Customers] AS [c]
@@ -336,7 +336,7 @@ public override void Scalar_Function_Where_Not_Correlated_Instance()
base.Scalar_Function_Where_Not_Correlated_Instance();
AssertSql(
- @"@__startDate_1='2000-04-01T00:00:00' (Nullable = true)
+ @"@__startDate_1='2000-04-01T00:00:00.0000000' (Nullable = true)
SELECT TOP(2) [c].[Id]
FROM [Customers] AS [c]
diff --git a/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs b/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs
index db74901434b..c1d0481fc11 100644
--- a/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs
+++ b/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs
@@ -63,6 +63,24 @@ public override void AddColumnOperation_with_maxLength_overridden()
");
}
+ public override void AddColumnOperation_with_precision_and_scale_overridden()
+ {
+ base.AddColumnOperation_with_precision_and_scale_overridden();
+
+ AssertSql(
+ @"ALTER TABLE [Person] ADD [Pi] decimal(15,10) NOT NULL;
+");
+ }
+
+ public override void AddColumnOperation_with_precision_and_scale_no_model()
+ {
+ base.AddColumnOperation_with_precision_and_scale_no_model();
+
+ AssertSql(
+ @"ALTER TABLE [Person] ADD [Pi] decimal(20,7) NOT NULL;
+");
+ }
+
public override void AddColumnOperation_with_unicode_overridden()
{
base.AddColumnOperation_with_unicode_overridden();
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs
index c82a7de6b44..cf0e3f04557 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs
@@ -49,7 +49,7 @@ public override async Task Where_datetime_now(bool async)
await base.Where_datetime_now(async);
AssertSql(
- @"@__myDatetime_0='2015-04-10T00:00:00' (DbType = String)
+ @"@__myDatetime_0='2015-04-10T00:00:00.0000000' (DbType = String)
SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -61,7 +61,7 @@ public override async Task Where_datetime_utcnow(bool async)
await base.Where_datetime_utcnow(async);
AssertSql(
- @"@__myDatetime_0='2015-04-10T00:00:00' (DbType = String)
+ @"@__myDatetime_0='2015-04-10T00:00:00.0000000' (DbType = String)
SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -83,7 +83,7 @@ public override async Task Where_datetime_date_component(bool async)
await base.Where_datetime_date_component(async);
AssertSql(
- @"@__myDatetime_0='1998-05-04T00:00:00' (DbType = String)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = String)
SELECT ""o"".""OrderID"", ""o"".""CustomerID"", ""o"".""EmployeeID"", ""o"".""OrderDate""
FROM ""Orders"" AS ""o""
diff --git a/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs
index 06947228ca5..b3c3b90e18a 100644
--- a/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs
+++ b/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs
@@ -126,6 +126,68 @@ public void Can_only_override_existing_MaxLength_value_explicitly()
Assert.Equal(2, metadata.GetMaxLength().Value);
}
+ [ConditionalFact]
+ public void Can_only_override_lower_or_equal_source_Precision()
+ {
+ var builder = CreateInternalPropertyBuilder();
+ var metadata = builder.Metadata;
+
+ Assert.NotNull(builder.HasPrecision(1, ConfigurationSource.DataAnnotation));
+ Assert.NotNull(builder.HasPrecision(2, ConfigurationSource.DataAnnotation));
+
+ Assert.Equal(2, metadata.GetPrecision().Value);
+
+ Assert.Null(builder.HasPrecision(1, ConfigurationSource.Convention));
+ Assert.Equal(2, metadata.GetPrecision().Value);
+ }
+
+ [ConditionalFact]
+ public void Can_only_override_existing_Precision_value_explicitly()
+ {
+ var metadata = CreateProperty();
+ metadata.SetPrecision(1);
+ var builder = metadata.Builder;
+
+ Assert.NotNull(builder.HasPrecision(1, ConfigurationSource.DataAnnotation));
+ Assert.Null(builder.HasPrecision(2, ConfigurationSource.DataAnnotation));
+
+ Assert.Equal(1, metadata.GetPrecision().Value);
+
+ Assert.NotNull(builder.HasPrecision(2, ConfigurationSource.Explicit));
+ Assert.Equal(2, metadata.GetPrecision().Value);
+ }
+
+ [ConditionalFact]
+ public void Can_only_override_lower_or_equal_source_Scale()
+ {
+ var builder = CreateInternalPropertyBuilder();
+ var metadata = builder.Metadata;
+
+ Assert.NotNull(builder.HasScale(1, ConfigurationSource.DataAnnotation));
+ Assert.NotNull(builder.HasScale(2, ConfigurationSource.DataAnnotation));
+
+ Assert.Equal(2, metadata.GetScale().Value);
+
+ Assert.Null(builder.HasScale(1, ConfigurationSource.Convention));
+ Assert.Equal(2, metadata.GetScale().Value);
+ }
+
+ [ConditionalFact]
+ public void Can_only_override_existing_Scale_value_explicitly()
+ {
+ var metadata = CreateProperty();
+ metadata.SetScale(1);
+ var builder = metadata.Builder;
+
+ Assert.NotNull(builder.HasScale(1, ConfigurationSource.DataAnnotation));
+ Assert.Null(builder.HasScale(2, ConfigurationSource.DataAnnotation));
+
+ Assert.Equal(1, metadata.GetScale().Value);
+
+ Assert.NotNull(builder.HasScale(2, ConfigurationSource.Explicit));
+ Assert.Equal(2, metadata.GetScale().Value);
+ }
+
[ConditionalFact]
public void Can_only_override_lower_or_equal_source_CustomValueGenerator_factory()
{