Tôi đang thử nghiệm phương pháp tiếp cận mã đầu tiên này, nhưng bây giờ tôi phát hiện ra rằng một thuộc tính thuộc loại System.Decimal được ánh xạ tới cột sql có kiểu thập phân (18, 0).
Làm cách nào để đặt độ chính xác của cột cơ sở dữ liệu?
Tôi đang thử nghiệm phương pháp tiếp cận mã đầu tiên này, nhưng bây giờ tôi phát hiện ra rằng một thuộc tính thuộc loại System.Decimal được ánh xạ tới cột sql có kiểu thập phân (18, 0).
Làm cách nào để đặt độ chính xác của cột cơ sở dữ liệu?
Câu trả lời:
Câu trả lời từ Dave Van den Eynde hiện đã hết hạn. Có 2 thay đổi quan trọng, từ EF 4.1 trở đi, lớp ModelBuilder hiện là DbModelBuilder và giờ đây có Phương thức DecimalPropertyConfiguration.HasPrecision có chữ ký:
public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )
trong đó độ chính xác là tổng số chữ số mà db sẽ lưu trữ, bất kể điểm thập phân rơi ở đâu và tỷ lệ là số vị trí thập phân mà nó sẽ lưu trữ.
Do đó, không cần phải lặp qua các thuộc tính như được hiển thị nhưng chỉ có thể được gọi từ
public class EFDbContext : DbContext
{
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Class>().Property(object => object.property).HasPrecision(12, 10);
base.OnModelCreating(modelBuilder);
}
}
System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder
base.OnModelCreating(modelBuilder);
. Đó có phải là cố ý hay chỉ là nạn nhân của việc gõ mã trực tuyến thay vì trong IDE?
Nếu bạn muốn đặt độ chính xác cho tất cả decimals
trong EF6, bạn có thể thay thế DecimalPropertyConvention
quy ước mặc định được sử dụng trong DbModelBuilder
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<DecimalPropertyConvention>();
modelBuilder.Conventions.Add(new DecimalPropertyConvention(38, 18));
}
Mặc định DecimalPropertyConvention
trong EF6 ánh xạ decimal
các thuộc tính vào decimal(18,2)
các cột.
Nếu bạn chỉ muốn các thuộc tính riêng lẻ có độ chính xác được chỉ định thì bạn có thể đặt độ chính xác cho thuộc tính của thực thể trên DbModelBuilder
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>().Property(e => e.Value).HasPrecision(38, 18);
}
Hoặc, thêm một EntityTypeConfiguration<>
thực thể xác định độ chính xác:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MyEntityConfiguration());
}
internal class MyEntityConfiguration : EntityTypeConfiguration<MyEntity>
{
internal MyEntityConfiguration()
{
this.Property(e => e.Value).HasPrecision(38, 18);
}
}
Tôi đã có một thời gian tuyệt vời để tạo Thuộc tính tùy chỉnh cho việc này:
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class DecimalPrecisionAttribute : Attribute
{
public DecimalPrecisionAttribute(byte precision, byte scale)
{
Precision = precision;
Scale = scale;
}
public byte Precision { get; set; }
public byte Scale { get; set; }
}
sử dụng nó như thế này
[DecimalPrecision(20,10)]
public Nullable<decimal> DeliveryPrice { get; set; }
và điều kỳ diệu xảy ra khi tạo mô hình với một số phản chiếu
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
foreach (Type classType in from t in Assembly.GetAssembly(typeof(DecimalPrecisionAttribute)).GetTypes()
where t.IsClass && t.Namespace == "YOURMODELNAMESPACE"
select t)
{
foreach (var propAttr in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute<DecimalPrecisionAttribute>() != null).Select(
p => new { prop = p, attr = p.GetCustomAttribute<DecimalPrecisionAttribute>(true) }))
{
var entityConfig = modelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(classType).Invoke(modelBuilder, null);
ParameterExpression param = ParameterExpression.Parameter(classType, "c");
Expression property = Expression.Property(param, propAttr.prop.Name);
LambdaExpression lambdaExpression = Expression.Lambda(property, true,
new ParameterExpression[]
{param});
DecimalPropertyConfiguration decimalConfig;
if (propAttr.prop.PropertyType.IsGenericType && propAttr.prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
MethodInfo methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[7];
decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
}
else
{
MethodInfo methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[6];
decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
}
decimalConfig.HasPrecision(propAttr.attr.Precision, propAttr.attr.Scale);
}
}
}
phần đầu tiên là để có được tất cả các lớp trong mô hình (thuộc tính tùy chỉnh của tôi được xác định trong cụm đó vì vậy tôi đã sử dụng nó để lấy cụm với mô hình)
foreach thứ hai có được tất cả các thuộc tính trong lớp đó với thuộc tính tùy chỉnh và chính thuộc tính đó để tôi có thể có được dữ liệu chính xác và tỷ lệ
sau đó tôi phải gọi
modelBuilder.Entity<MODEL_CLASS>().Property(c=> c.PROPERTY_NAME).HasPrecision(PRECISION,SCALE);
vì vậy tôi gọi modelBuilder.Entity () bằng cách phản chiếu và lưu trữ nó trong biến entityConfig sau đó tôi xây dựng biểu thức lambda "c => c.PROPERTY_NAME"
Sau đó, nếu số thập phân là null, tôi gọi
Property(Expression<Func<TStructuralType, decimal?>> propertyExpression)
phương pháp (tôi gọi điều này theo vị trí trong mảng, nó không lý tưởng tôi biết, bất kỳ trợ giúp sẽ được đánh giá cao)
và nếu nó không rỗng, tôi gọi
Property(Expression<Func<TStructuralType, decimal>> propertyExpression)
phương pháp.
Có DecimalPropertyConfiguration tôi gọi phương thức HasPrecision.
MethodInfo methodInfo = entityConfig.GetType().GetMethod("Property", new[] { lambdaExpression.GetType() });
để có được quá tải chính xác. dường như làm việc cho đến nay.
Sử dụng DecimalPrecisonAttribute
từ KinSlayerUY, trong EF6, bạn có thể tạo một quy ước sẽ xử lý các thuộc tính riêng lẻ có thuộc tính (trái ngược với cài đặtDecimalPropertyConvention
giống như trong câu trả lời này sẽ ảnh hưởng đến tất cả các thuộc tính thập phân).
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class DecimalPrecisionAttribute : Attribute
{
public DecimalPrecisionAttribute(byte precision, byte scale)
{
Precision = precision;
Scale = scale;
}
public byte Precision { get; set; }
public byte Scale { get; set; }
}
public class DecimalPrecisionAttributeConvention
: PrimitivePropertyAttributeConfigurationConvention<DecimalPrecisionAttribute>
{
public override void Apply(ConventionPrimitivePropertyConfiguration configuration, DecimalPrecisionAttribute attribute)
{
if (attribute.Precision < 1 || attribute.Precision > 38)
{
throw new InvalidOperationException("Precision must be between 1 and 38.");
}
if (attribute.Scale > attribute.Precision)
{
throw new InvalidOperationException("Scale must be between 0 and the Precision value.");
}
configuration.HasPrecision(attribute.Precision, attribute.Scale);
}
}
Sau đó, trong của bạn DbContext
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}
Precision
, thì tôi khuyên bạn nên đặt giới hạn trên thành 28 (vì vậy > 28
trong điều kiện của bạn). Theo tài liệu MSDN, System.Decimal
chỉ có thể biểu thị tối đa 28-29 chữ số chính xác ( msdn.microsoft.com/en-us/l Library / 44x0z75.aspx ). Ngoài ra, thuộc tính khai báo Scale
là byte
, điều đó có nghĩa là điều kiện tiên quyết của bạn attribute.Scale < 0
là không cần thiết.
System.Decimal
thì không. Do đó, không có ý nghĩa gì khi đặt điều kiện tiên quyết trên cho bất cứ điều gì lớn hơn 28; System.Decimal
Không thể đại diện cho số lượng lớn, rõ ràng. Ngoài ra, hãy lưu ý rằng thuộc tính này hữu ích cho các nhà cung cấp dữ liệu ngoài SQL Server. Ví dụ, numeric
loại của PostgreQuery hỗ trợ độ chính xác lên tới 131072 chữ số.
decimal(38,9)
cột sẽ giữ hạnh phúc System.Decimal.MaxValue
nhưng một decimal(28,9)
cột thì không. Không có lý do gì để giới hạn độ chính xác chỉ còn 28.
Rõ ràng, bạn có thể ghi đè phương thức DbContext.OnModelCreating () và định cấu hình độ chính xác như sau:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().Property(product => product.Price).Precision = 10;
modelBuilder.Entity<Product>().Property(product => product.Price).Scale = 2;
}
Nhưng đây là mã khá tẻ nhạt khi bạn phải làm điều đó với tất cả các thuộc tính liên quan đến giá của bạn, vì vậy tôi đã đưa ra điều này:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
var properties = new[]
{
modelBuilder.Entity<Product>().Property(product => product.Price),
modelBuilder.Entity<Order>().Property(order => order.OrderTotal),
modelBuilder.Entity<OrderDetail>().Property(detail => detail.Total),
modelBuilder.Entity<Option>().Property(option => option.Price)
};
properties.ToList().ForEach(property =>
{
property.Precision = 10;
property.Scale = 2;
});
base.OnModelCreating(modelBuilder);
}
Đó là một thực hành tốt mà bạn gọi phương thức cơ sở khi bạn ghi đè một phương thức, mặc dù việc triển khai cơ sở không làm gì cả.
Cập nhật: Bài viết này cũng rất hữu ích.
base.OnModelCreating(modelBuilder);
là cần thiết. Từ siêu dữ liệu DbContext trong VS: The default implementation of this method does nothing, but it can be overridden in a derived class such that the model can be further configured before it is locked down.
Entity Framework Ver 6 (Alpha, rc1) có một thứ gọi là Quy ước tùy chỉnh . Để đặt độ chính xác thập phân:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<decimal>().Configure(config => config.HasPrecision(18, 4));
}
Tài liệu tham khảo:
[Column(TypeName = "decimal(18,2)")]
điều này sẽ hoạt động với các lần di chuyển đầu tiên của mã EF Core như được mô tả ở đây .
The store type 'decimal(18,2)' could not be found in the SqlServer provider manifest
dòng mã này có thể là một cách đơn giản hơn để hoàn thành tương tự:
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
public ProductConfiguration()
{
this.Property(m => m.Price).HasPrecision(10, 2);
}
}
- CHO EF CORE - với việc sử dụng System.ComponentModel.DataAnnotations;
sử dụng [Column
( TypeName
= "decimal
( độ chính xác , tỷ lệ )")]
Chính xác = Tổng số ký tự được sử dụng
Tỷ lệ = Tổng số sau dấu chấm. (dễ bị nhầm lẫn)
Ví dụ :
public class Blog
{
public int BlogId { get; set; }
[Column(TypeName = "varchar(200)")]
public string Url { get; set; }
[Column(TypeName = "decimal(5, 2)")]
public decimal Rating { get; set; }
}
Thêm chi tiết tại đây: https://docs.microsoft.com/en-us/ef/core/modeling/relational/data-types
Trong EF6
modelBuilder.Properties()
.Where(x => x.GetCustomAttributes(false).OfType<DecimalPrecisionAttribute>().Any())
.Configure(c => {
var attr = (DecimalPrecisionAttribute)c.ClrPropertyInfo.GetCustomAttributes(typeof (DecimalPrecisionAttribute), true).FirstOrDefault();
c.HasPrecision(attr.Precision, attr.Scale);
});
Bạn luôn có thể yêu cầu EF thực hiện điều này với các quy ước trong lớp Ngữ cảnh trong hàm OnModelCreating như sau:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// <... other configurations ...>
// modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
// modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
// modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Configure Decimal to always have a precision of 18 and a scale of 4
modelBuilder.Conventions.Remove<DecimalPropertyConvention>();
modelBuilder.Conventions.Add(new DecimalPropertyConvention(18, 4));
base.OnModelCreating(modelBuilder);
}
Điều này chỉ áp dụng cho Code First EF fyi và áp dụng cho tất cả các loại thập phân được ánh xạ tới db.
Remove<DecimalPropertyConvention>();
đến trước Add(new DecimalPropertyConvention(18, 4));
. Tôi nghĩ thật lạ khi nó không bị ghi đè tự động.
Sử dụng
System.ComponentModel.DataAnnotations;
Bạn chỉ có thể đặt thuộc tính đó trong mô hình của mình:
[DataType("decimal(18,5)")]
Bạn có thể tìm thấy thêm thông tin về MSDN - khía cạnh của Mô hình dữ liệu thực thể. http://msdn.microsoft.com/en-us/l Library / ee382834.aspx Được đề xuất đầy đủ.
Thực tế cho EntityFrameworkCore 3.1.3:
Một số giải pháp trong OnModelCreating:
var fixDecimalDatas = new List<Tuple<Type, Type, string>>();
foreach (var entityType in builder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (Type.GetTypeCode(property.ClrType) == TypeCode.Decimal)
{
fixDecimalDatas.Add(new Tuple<Type, Type, string>(entityType.ClrType, property.ClrType, property.GetColumnName()));
}
}
}
foreach (var item in fixDecimalDatas)
{
builder.Entity(item.Item1).Property(item.Item2, item.Item3).HasColumnType("decimal(18,4)");
}
//custom decimal nullable:
builder.Entity<SomePerfectEntity>().Property(x => x.IsBeautiful).HasColumnType("decimal(18,4)");
Thuộc tính tùy chỉnh của KinSlayerUY hoạt động tốt đối với tôi nhưng tôi gặp vấn đề với ComplexTypes. Chúng được ánh xạ như các thực thể trong mã thuộc tính, do đó không thể ánh xạ thành ComplexType.
Do đó tôi đã mở rộng mã để cho phép điều này:
public static void OnModelCreating(DbModelBuilder modelBuilder)
{
foreach (Type classType in from t in Assembly.GetAssembly(typeof(DecimalPrecisionAttribute)).GetTypes()
where t.IsClass && t.Namespace == "FA.f1rstval.Data"
select t)
{
foreach (var propAttr in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute<DecimalPrecisionAttribute>() != null).Select(
p => new { prop = p, attr = p.GetCustomAttribute<DecimalPrecisionAttribute>(true) }))
{
ParameterExpression param = ParameterExpression.Parameter(classType, "c");
Expression property = Expression.Property(param, propAttr.prop.Name);
LambdaExpression lambdaExpression = Expression.Lambda(property, true,
new ParameterExpression[] { param });
DecimalPropertyConfiguration decimalConfig;
int MethodNum;
if (propAttr.prop.PropertyType.IsGenericType && propAttr.prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
MethodNum = 7;
}
else
{
MethodNum = 6;
}
//check if complextype
if (classType.GetCustomAttribute<ComplexTypeAttribute>() != null)
{
var complexConfig = modelBuilder.GetType().GetMethod("ComplexType").MakeGenericMethod(classType).Invoke(modelBuilder, null);
MethodInfo methodInfo = complexConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[MethodNum];
decimalConfig = methodInfo.Invoke(complexConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
}
else
{
var entityConfig = modelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(classType).Invoke(modelBuilder, null);
MethodInfo methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[MethodNum];
decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
}
decimalConfig.HasPrecision(propAttr.attr.Precision, propAttr.attr.Scale);
}
}
}
@ Mark007, tôi đã thay đổi tiêu chí lựa chọn loại để sử dụng các thuộc tính Dbset <> của DbContext. Tôi nghĩ rằng điều này an toàn hơn vì có những lúc bạn có các lớp trong không gian tên đã cho mà không phải là một phần của định nghĩa mô hình hoặc chúng là nhưng không phải là các thực thể. Hoặc các thực thể của bạn có thể cư trú trong các không gian tên riêng biệt hoặc các cụm riêng biệt và được kéo lại với nhau thành một bối cảnh.
Ngoài ra, mặc dù không có khả năng, tôi không nghĩ sẽ an toàn khi dựa vào thứ tự các định nghĩa phương thức, vì vậy tốt hơn hết là lấy chúng ra bằng danh sách Thông số. (.GetTypeMethods () là một phương thức mở rộng mà tôi đã xây dựng để làm việc với mô hình TypeInfo mới và có thể làm phẳng hệ thống phân cấp lớp khi tìm kiếm các phương thức).
Xin lưu ý rằng các đại biểu OnModelCreating cho phương thức này:
private void OnModelCreatingSetDecimalPrecisionFromAttribute(DbModelBuilder modelBuilder)
{
foreach (var iSetProp in this.GetType().GetTypeProperties(true))
{
if (iSetProp.PropertyType.IsGenericType
&& (iSetProp.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>) || iSetProp.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)))
{
var entityType = iSetProp.PropertyType.GetGenericArguments()[0];
foreach (var propAttr in entityType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(p => new { prop = p, attr = p.GetCustomAttribute<DecimalPrecisionAttribute>(true) })
.Where(propAttr => propAttr.attr != null))
{
var entityTypeConfigMethod = modelBuilder.GetType().GetTypeInfo().DeclaredMethods.First(m => m.Name == "Entity");
var entityTypeConfig = entityTypeConfigMethod.MakeGenericMethod(entityType).Invoke(modelBuilder, null);
var param = ParameterExpression.Parameter(entityType, "c");
var lambdaExpression = Expression.Lambda(Expression.Property(param, propAttr.prop.Name), true, new ParameterExpression[] { param });
var propertyConfigMethod =
entityTypeConfig.GetType()
.GetTypeMethods(true, false)
.First(m =>
{
if (m.Name != "Property")
return false;
var methodParams = m.GetParameters();
return methodParams.Length == 1 && methodParams[0].ParameterType == lambdaExpression.GetType();
}
);
var decimalConfig = propertyConfigMethod.Invoke(entityTypeConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
decimalConfig.HasPrecision(propAttr.attr.Precision, propAttr.attr.Scale);
}
}
}
}
public static IEnumerable<MethodInfo> GetTypeMethods(this Type typeToQuery, bool flattenHierarchy, bool? staticMembers)
{
var typeInfo = typeToQuery.GetTypeInfo();
foreach (var iField in typeInfo.DeclaredMethods.Where(fi => staticMembers == null || fi.IsStatic == staticMembers))
yield return iField;
//this bit is just for StaticFields so we pass flag to flattenHierarchy and for the purpose of recursion, restrictStatic = false
if (flattenHierarchy == true)
{
var baseType = typeInfo.BaseType;
if ((baseType != null) && (baseType != typeof(object)))
{
foreach (var iField in baseType.GetTypeMethods(true, staticMembers))
yield return iField;
}
}
}
[Column(TypeName = "decimal(18,4)")]
thuộc tính cho các thuộc tính thập phân của bạn