source

소수점 정밀도 및 척도(EF Code First)

nicesource 2023. 6. 3. 08:31
반응형

소수점 정밀도 및 척도(EF Code First)

코드 우선 접근법을 실험하고 있지만, 이제 시스템 유형의 속성을 알게 되었습니다.10진수는 10진수 유형(18, 0)의 SQL 열에 매핑됩니다.

데이터베이스 열의 정밀도를 설정하려면 어떻게 해야 합니까?

Dave Van Den Eynde의 답변은 이제 구식입니다.EF 4.1부터 ModelBuilder 클래스는 이제 DbModelBuilder이고 이제 DecimalPropertyConfiguration이 있습니다.다음과 같은 서명을 가진 정밀도 방법이 있습니다.

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);
   }
}

것에 하고자 하는 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<>정밀도를 지정하는 엔티티의 경우:

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);
        }
    }
}

첫 번째 부분은 모델의 모든 클래스를 가져오는 것입니다(내 사용자 지정 속성은 해당 어셈블리에 정의되어 있으므로 모델과 함께 어셈블리를 가져오는 데 사용했습니다).

각각의 두 번째는 사용자 지정 속성과 속성 자체를 사용하여 해당 클래스의 모든 속성을 가져옵니다. 그래서 정밀도와 척도 데이터를 얻을 수 있습니다.

그 후에 전화를 해야 합니다.

modelBuilder.Entity<MODEL_CLASS>().Property(c=> c.PROPERTY_NAME).HasPrecision(PRECISION,SCALE);

그래서 저는 모델을 Builder라고 부릅니다.반영하여 엔티티()를 entityConfig 변수에 저장한 다음 "c => c.PROPERTY_NAME" 람다 식을 작성합니다.

그런 다음 소수가 null일 경우 다음을 호출합니다.

Property(Expression<Func<TStructuralType, decimal?>> propertyExpression) 

방법 (저는 이것을 배열의 위치로 부릅니다, 이상적이지 않습니다, 저는 압니다, 어떤 도움도 매우 감사할 것입니다)

그리고 무효가 아니라면 전화하겠습니다.

Property(Expression<Func<TStructuralType, decimal>> propertyExpression)

방법.

DecimalPropertyConfiguration이 있으면 HasPrecision 메서드를 호출합니다.

DecimalPrecisonAttribute에서 (UY를 로, EF6에서 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());
}

DbContext를 재정의할 수 있습니다.ModelCreating() 메서드에서 다음과 같이 정밀도를 구성합니다.

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);
    }

기본 구현이 아무 것도 하지 않더라도 메서드를 재정의할 때 기본 메서드를 호출하는 것이 좋습니다.

업데이트: 이 기사도 많은 도움이 되었습니다.

[Column(TypeName = "decimal(18,2)")]

이것은 여기에 설명된 대로 EF Core 코드 첫 번째 마이그레이션에서 작동합니다.

Entity Framework Ver 6(Alpha, rc1)에는 사용자 지정 규칙이라는 이 있습니다.소수점 정밀도를 설정하는 방법

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties<decimal>().Configure(config => config.HasPrecision(18, 4));
}

참조:

편집, .NET 6에서 정밀 속성으로 바꿀 수 있습니다.

[Precision(그림, 축척)]

EF Core의 이전 버전:

[Column(TypeName = "decimal(수평, 축척도")]

정의:

정밀도 = 사용된 총 문자

척도 = 점 다음의 총 숫자입니다. (혼란을 일으키기 쉽습니다)

:

using System.ComponentModel.DataAnnotations; //.Net Core
using Microsoft.EntityFrameworkCore; //.NET 6+

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; }
    [Precision(28, 8)]
    public decimal RatingV6 { get; set; }
}

자세한 내용은 여기에서 확인하십시오. https://learn.microsoft.com/en-us/ef/core/modeling/relational/data-types

이 코드 라인은 동일한 작업을 수행하는 더 간단한 방법이 될 수 있습니다.

 public class ProductConfiguration : EntityTypeConfiguration<Product>
    {
        public ProductConfiguration()
        {
            this.Property(m => m.Price).HasPrecision(10, 2);
        }
    }

.NET EF Core 6 이상에서는 Precision 특성을 사용할 수 있습니다.

    [Precision(18, 2)]
    public decimal Price { get; set; }

하고 EF Core 6을 설치한 후 다음 작업을 .using

using Microsoft.EntityFrameworkCore;

언제든지 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 fii에만 적용되며 DB에 매핑된 모든 소수점 유형에 적용됩니다.

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);
    });

사용.

System.ComponentModel.DataAnnotations;

해당 속성을 모델에 간단히 넣을 수 있습니다.

[DataType("decimal(18,5)")]

MSDN - 엔티티 데이터 모델의 측면에서 자세한 정보를 확인할 수 있습니다.http://msdn.microsoft.com/en-us/library/ee382834.aspx 전체 권장.

엔티티 프레임워크 코어 3.1.3에 대한 실제:

OnModel의 일부 솔루션생성:

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)");

킨슬레이어UY의 커스텀 속성은 나에게 잘 먹혔지만 나는 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;
            }
        }
    }

이것이 제가 찾던 것입니다, 일반 MVC 프로젝트(No.Net Core)에 적합합니다.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<YOUR_CLASS_NAME>().Property(x => x.YOUR_DECIAML_PROP).HasPrecision(18, 6);

        base.OnModelCreating(modelBuilder);

    }
}

패키지 관리자 콘솔

add-migration changeDecimalPrecision

생성된 마이그레이션

    public override void Up()
    {
        AlterColumn("dbo.YOUR_CLASS_NAME", "YOUR_DECIAML_PROP", c => c.Decimal(nullable: false, precision: 18, scale: 6));
    }

언급URL : https://stackoverflow.com/questions/3504660/decimal-precision-and-scale-in-ef-code-first

반응형