이 코드 우선 접근 방식을 실험하고 있지만 System.Decimal 유형의 속성이 decimal (18, 0) 유형의 SQL 열에 매핑된다는 것을 알게되었습니다.
데이터베이스 열의 정밀도를 어떻게 설정합니까?
이 코드 우선 접근 방식을 실험하고 있지만 System.Decimal 유형의 속성이 decimal (18, 0) 유형의 SQL 열에 매핑된다는 것을 알게되었습니다.
데이터베이스 열의 정밀도를 어떻게 설정합니까?
답변:
Dave Van den Eynde의 답변이 구식입니다. EF 4.1부터 ModelBuilder 클래스는 이제 DbModelBuilder 이고 다음과 같은 서명을 갖는 DecimalPropertyConfiguration.HasPrecision 메소드가 있습니다.
public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )
여기서 precision은 소수점이 떨어지는 위치에 관계없이 db가 저장할 총 자릿수이고 scale은 저장할 소수점 이하 자릿수입니다.
따라서 표시된대로 속성을 반복 할 필요는 없지만에서 호출 할 수 있습니다
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);
. IDE가 아닌 온라인에서 코드를 입력하는 것이 의도적이거나 단순한 희생자입니까?
모두의 정밀도를 설정하려면 decimals
EF6에서 에서 DecimalPropertyConvention
사용되는 기본 규칙을 대체 하십시오 DbModelBuilder
.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<DecimalPropertyConvention>();
modelBuilder.Conventions.Add(new DecimalPropertyConvention(38, 18));
}
DecimalPropertyConvention
EF6 의 기본값 은 decimal
속성을 decimal(18,2)
열에 매핑 합니다 .
개별 속성 만 지정된 정밀도를 가지려면 개체 속성의 정밀도를 DbModelBuilder
.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>().Property(e => e.Value).HasPrecision(38, 18);
}
또는 EntityTypeConfiguration<>
정밀도를 지정하는 엔티티에 for를 추가하십시오 .
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);
}
}
나는 이것을 위해 커스텀 속성을 만드는 것이 즐거웠다.
[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; }
}
이런 식으로 사용
[DecimalPrecision(20,10)]
public Nullable<decimal> DeliveryPrice { get; set; }
마법은 약간의 반성과 함께 모델을 만들 때 발생합니다
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);
}
}
}
첫 번째 부분은 모델의 모든 클래스를 얻는 것입니다 (사용자 정의 속성은 해당 어셈블리에 정의되어 있으므로 모델과 함께 어셈블리를 얻는 데 사용했습니다)
두 번째 foreach는 사용자 정의 속성을 사용하여 해당 클래스의 모든 속성과 속성 자체를 가져 오기 때문에 정밀도 및 스케일 데이터를 얻을 수 있습니다
그 후 나는 전화해야
modelBuilder.Entity<MODEL_CLASS>().Property(c=> c.PROPERTY_NAME).HasPrecision(PRECISION,SCALE);
리플렉션을 통해 modelBuilder.Entity ()를 호출하고 entityConfig 변수에 저장 한 다음 "c => c.PROPERTY_NAME"람다 식을 빌드합니다.
그 후, 10 진수가 nullable이면
Property(Expression<Func<TStructuralType, decimal?>> propertyExpression)
방법 (배열의 위치로 이것을 호출합니다. 이상적이지 않습니다. 어떤 도움을 주시면 감사하겠습니다)
그리고 nullable이 아닌 경우
Property(Expression<Func<TStructuralType, decimal>> propertyExpression)
방법.
DecimalPropertyConfiguration을 사용하면 HasPrecision 메서드를 호출합니다.
MethodInfo methodInfo = entityConfig.GetType().GetMethod("Property", new[] { lambdaExpression.GetType() });
올바른 과부하를 얻는 데 사용 합니다. 지금까지 작동하는 것 같습니다.
DecimalPrecisonAttribute
from KinSlayerUY를 사용하여 EF6에서 속성을 갖는 개별 속성을 처리하는 규칙을 만들 수 있습니다 ( 이 답변DecimalPropertyConvention
에서 이와 같은 설정은 모든 십진수 속성에 영향을 미침).
[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);
}
}
그런 다음에 DbContext
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}
Precision
하려는 경우 상한을 28로 설정하는 것이 좋습니다 (따라서 > 28
조건에 따라). MSDN 설명서에 따르면 System.Decimal
최대 28-29 자리의 정밀도 ( msdn.microsoft.com/en-us/library/364x0z75.aspx ) 만 나타낼 수 있습니다 . 또한이 속성은 Scale
로 선언 되므로 byte
전제 조건 attribute.Scale < 0
이 필요하지 않습니다.
System.Decimal
그렇지 않습니다. 따라서 상한 전제 조건을 28보다 큰 것으로 설정하는 것은 의미가 없습니다. System.Decimal
분명히 큰 숫자를 나타낼 수 없습니다. 또한이 특성은 SQL Server 이외의 데이터 공급자에 유용합니다. 예를 들어 PostgreSQL numeric
유형은 최대 131072 자릿수의 정밀도를 지원합니다.
분명히 DbContext.OnModelCreating () 메서드를 재정의하고 다음과 같이 정밀도를 구성 할 수 있습니다.
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;
}
그러나 이것은 모든 가격 관련 속성으로해야 할 때 매우 지루한 코드이므로 다음과 같이 생각해 냈습니다.
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);
}
기본 구현이 아무 것도 수행하지 않더라도 메서드를 재정의 할 때 기본 메서드를 호출하는 것이 좋습니다.
업데이트 : 이 기사 는 매우 도움이되었습니다.
base.OnModelCreating(modelBuilder);
가 필요 하다고 생각하지 않습니다 . VS의 DbContext 메타 데이터에서 : 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)에는 Custom Conventions 라는 것이 있습니다. 소수 자릿수를 설정하려면
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<decimal>().Configure(config => config.HasPrecision(18, 4));
}
참고:
The store type 'decimal(18,2)' could not be found in the SqlServer provider manifest
이 코드 라인은 동일한 것을 달성하는 더 간단한 방법이 될 수 있습니다.
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
public ProductConfiguration()
{
this.Property(m => m.Price).HasPrecision(10, 2);
}
}
- EF 코어 - 와 System.ComponentModel.DataAnnotations 사용합니다.
사용 [Column
( TypeName
= "decimal
( 정밀도 , 스케일 )")]
정밀도 = 사용 된 총 문자 수
스케일 = 점 뒤의 총 수입니다. (혼란하기 쉬움)
예 :
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; }
}
자세한 내용은 https://docs.microsoft.com/en-us/ef/core/modeling/relational/data-types를 참조하십시오.
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);
});
다음과 같이 EF에게 OnModelCreating 함수의 Context 클래스에서 규칙을 사용하여이를 수행하도록 항상 지시 할 수 있습니다.
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);
}
이는 Code First EF fyi에만 적용되며 db에 매핑 된 모든 10 진수 유형에 적용됩니다.
Remove<DecimalPropertyConvention>();
앞에 올 때까지 작동하지 않았습니다 Add(new DecimalPropertyConvention(18, 4));
. 자동으로 재정의되지 않는 것이 이상하다고 생각합니다.
사용
System.ComponentModel.DataAnnotations;
모델에 해당 속성을 넣을 수 있습니다.
[DataType("decimal(18,5)")]
Entity Data Model의 MSDN-facet에 대한 자세한 정보를 찾을 수 있습니다. http://msdn.microsoft.com/en-us/library/ee382834.aspx 전체 권장.
EntityFrameworkCore 3.1.3의 경우 :
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)");
KinSlayerUY의 커스텀 속성은 훌륭하게 작동했지만 ComplexTypes에 문제가있었습니다. 속성 코드에서 엔티티로 맵핑되었으므로 ComplexType으로 맵핑 할 수 없었습니다.
따라서이를 위해 코드를 확장했습니다.
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, 유형 선택 기준을 DbContext의 DbSet <> 속성을 사용하도록 변경했습니다. 주어진 네임 스페이스에 모델 정의의 일부가 아니거나 엔티티가 아닌 클래스가있는 시간이 있기 때문에 이것이 더 안전하다고 생각합니다. 또는 엔터티가 별도의 네임 스페이스 또는 별도의 어셈블리에있을 수 있으며 한 컨텍스트로 결합 될 수 있습니다.
또한 가능하지는 않지만 메소드 정의의 순서에 의존하는 것이 안전하지 않다고 생각하므로 매개 변수 목록으로 가져 오는 것이 좋습니다. (.GetTypeMethods ()는 새로운 TypeInfo 패러다임과 함께 작동하도록 개발 된 확장 메서드이며 메서드를 찾을 때 클래스 계층 구조를 병합 할 수 있습니다).
OnModelCreating은이 메소드에 위임합니다.
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)")]
10 진수 속성에 속성 을 사용하는 것입니다