首页 文章

EF Code First中的十进制精度和比例

提问于
浏览
194

我正在尝试这种代码优先方法,但我现在发现System.Decimal类型的属性被映射到decimal(18,0)类型的sql列 .

如何设置数据库列的精度?

15 回答

  • 47

    在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);
        });
    
  • 46

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

    如果要为EF6中的所有 decimals 设置精度,可以替换 DbModelBuilder 中使用的默认 DecimalPropertyConvention 约定:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<DecimalPropertyConvention>();
        modelBuilder.Conventions.Add(new DecimalPropertyConvention(38, 18));
    }
    

    EF6中的默认 DecimalPropertyConventiondecimal 属性映射到 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);
        }
    }
    
  • 69

    我很高兴为此创建自定义属性:

    [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”lambda表达式

    之后,如果小数可以为空,我会调用

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

    方法(我通过数组中的位置调用它,它不是理想我知道,任何帮助将不胜感激)

    如果它不可空,我会打电话给

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

    方法 .

    使用DecimalPropertyConfiguration我调用HasPrecision方法 .

  • 68

    显然,您可以覆盖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);
        }
    

    在覆盖方法时调用基本方法是一种很好的做法,即使基本实现什么也不做 .

    更新:This article也非常有帮助 .

  • 4

    使用来自KinSlayerUY的 DecimalPrecisonAttribute ,在EF6中,您可以创建一个约定来处理具有该属性的各个属性(而不是在this answer中设置 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());
    }
    
  • 1

    实体框架Ver 6(Alpha,rc1)有一个名为 Custom Conventions 的东西 . 要设置小数精度:

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

    参考:

  • 2

    这段代码可能是一种更简单的方法来完成相同的事情:

    public class ProductConfiguration : EntityTypeConfiguration<Product>
        {
            public ProductConfiguration()
            {
                this.Property(m => m.Price).HasPrecision(10, 2);
            }
        }
    
  • 0
    [Column(TypeName = "decimal(18,2)")]
    

    这将适用于代码首次迁移,如here所述 .

  • 0

    - FOR EF CORE -using System.ComponentModel.DataAnnotations;

    使用 [ColumnTypeName = "decimalprecisionscale")]

    精度=使用的总字符数

    比例=点后的总数 . (容易混淆)

    Example

    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

  • 1

    您总是可以告诉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的所有十进制类型 .

  • 12

    您可以在MSDN上找到更多信息 - 实体数据模型的方面 . http://msdn.microsoft.com/en-us/library/ee382834.aspx完全推荐 .

  • 8

    运用

    System.ComponentModel.DataAnnotations;
    

    您可以简单地将该属性放在模型中:

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

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

    @ Mark007,我已经将类型选择标准更改为乘坐DbContext的DbSet <>属性 . 我认为这更安全,因为有时你在给定的命名空间中有不应该是模型定义的一部分的类,或者它们不是实体 . 或者您的实体可以驻留在单独的命名空间或单独的程序集中,并将它们组合到一次Context中 .

    此外,即使不太可能,我认为依靠方法定义的排序是不安全的,所以最好通过参数列表将它们拉出来 . (.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;
                }
            }
        }
    

相关问题