EF Code First 中的小数精度和小数位数

发布于 2024-09-15 02:43:06 字数 100 浏览 6 评论 0原文

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

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

I'm experimenting with this code-first approach, but I'm find out now that a property of type System.Decimal gets mapped to a sql column of type decimal(18, 0).

How do I set the precision of the database column?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

黎夕旧梦 2024-09-22 02:43:06

戴夫·范·登·艾德 (Dave Van den Eynde) 的答案现已过时。有 2 个重要更改,从 EF 4.1 开始,ModelBuilder 类现在为 DbModelBuilder 现在有一个 DecimalPropertyConfiguration.HasPrecision 方法,其签名为:

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

其中 precision 是数据库将存储的总位数,无论小数点所在的位置和比例是它将存储的小数位数。

因此,不需要迭代所示的属性,但可以从

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

The answer from Dave Van den Eynde is now out of date. There are 2 important changes, from EF 4.1 onwards the ModelBuilder class is now DbModelBuilder and there is now a DecimalPropertyConfiguration.HasPrecision Method which has a signature of:

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

where precision is the total number of digits the db will store, regardless of where the decimal point falls and scale is the number of decimal places it will store.

Therefore there is no need to iterate through properties as shown but the can just be called from

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);
   }
}
烦人精 2024-09-22 02:43:06

如果您想设置 EF6 中所有小数的精度,您可以替换 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);
    }
}

If you want to set the precision for all decimals in EF6 you could replace the default DecimalPropertyConvention convention used in the DbModelBuilder:

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

The default DecimalPropertyConvention in EF6 maps decimal properties to decimal(18,2) columns.

If you only want individual properties to have a specified precision then you can set the precision for the entity's property on the DbModelBuilder:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<MyEntity>().Property(e => e.Value).HasPrecision(38, 18);
}

Or, add an EntityTypeConfiguration<> for the entity which specifies the precision:

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);
    }
}
随风而去 2024-09-22 02:43:06

我为此创建了一个自定义属性,度过了愉快的时光:

[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 方法。

I had a nice time creating an Custom Attribute for this:

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

}

using it like this

[DecimalPrecision(20,10)]
public Nullable<decimal> DeliveryPrice { get; set; }

and the magic happens at model creation with some reflection

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

the first part is to get all classes in the model (my custom attribute is defined in that assembly so i used that to get the assembly with the model)

the second foreach gets all properties in that class with the custom attribute, and the attribute itself so i can get the precision and scale data

after that i have to call

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

so i call the modelBuilder.Entity() by reflection and store it in the entityConfig variable
then i build the "c => c.PROPERTY_NAME" lambda expression

After that, if the decimal is nullable i call the

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

method (i call this by the position in the array, it's not ideal i know, any help will be much appreciated)

and if it's not nullable i call the

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

method.

Having the DecimalPropertyConfiguration i call the HasPrecision method.

静谧 2024-09-22 02:43:06

使用 KinSlayerUY 中的 DecimalPrecisonAttribute,在 EF6 中,您可以创建一个约定来处理具有该属性的各个属性(而不是像 这个答案将影响所有小数属性)。

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

Using the DecimalPrecisonAttribute from KinSlayerUY, in EF6 you can create a convention which will handle individual properties which have the attribute (as opposed to setting the DecimalPropertyConvention like in this answer which will affect all decimal properties).

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

Then in your DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}
妥活 2024-09-22 02:43:06
[Column(TypeName = "decimal(18,2)")]

这将与 EF Core 代码优先迁移一起使用,如所述 在这里

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

this will work with EF Core code first migrations as described here.

别念他 2024-09-22 02:43:06

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

这是一个很好的做法当您重写方法时,您会调用基本方法,即使基本实现不执行任何操作。

更新:本文 也很有帮助。

Apparently, you can override the DbContext.OnModelCreating() method and configure the precision like this:

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

But this is pretty tedious code when you have to do it with all your price-related properties, so I came up with this:

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

It's good practice that you call the base method when you override a method, even though the base implementation does nothing.

Update: This article was also very helpful.

北城挽邺 2024-09-22 02:43:06

实体框架版本 6(Alpha、rc1)有一个称为自定义约定的东西。设置小数精度:

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

参考:

Entity Framework Ver 6 (Alpha, rc1) has something called Custom Conventions. To set decimal precision:

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

Reference:

毁梦 2024-09-22 02:43:06

从 .NET EF Core 6 开始,您可以使用 Precision 属性。

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

确保您需要安装 EF Core 6 并执行以下 using

using Microsoft.EntityFrameworkCore;

From .NET EF Core 6 onwards you can use the Precision attribute.

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

make sure that you need to install EF Core 6 and do following using line

using Microsoft.EntityFrameworkCore;
羁客 2024-09-22 02:43:06

替换

编辑,从 .NET 6 开始,可以用精度属性[Precision( precision, scale)]

以前版本的 EF Core:

[Column(TypeName = "decimal( precision, scale)")]

定义:

精度 = 使用的字符总数

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

示例

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

Edit, From .NET 6, this can been replaced with precision attribute

[Precision(precision, scale)]

Previous version of EF Core:

[Column(TypeName = "decimal(precision, scale)")]

Definitions:

Precision = Total number of characters used

Scale = Total number after the dot. (easy to get confused)

Example:

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

More details here: https://learn.microsoft.com/en-us/ef/core/modeling/relational/data-types

独﹏钓一江月 2024-09-22 02:43:06

此代码行可能是完成相同任务的更简单方法:

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

this code line could be a simpler way to acomplish the same:

 public class ProductConfiguration : EntityTypeConfiguration<Product>
    {
        public ProductConfiguration()
        {
            this.Property(m => m.Price).HasPrecision(10, 2);
        }
    }
怼怹恏 2024-09-22 02:43:06

您始终可以通过 OnModelCreating 函数中 Context 类中的约定告诉 EF 执行此操作,如下所示:

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 并适用于映射到数据库的所有十进制类型。

You can always tell EF to do this with conventions in the Context class in the OnModelCreating function as follows:

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

This only applies to Code First EF fyi and applies to all decimal types mapped to the db.

爱情眠于流年 2024-09-22 02:43:06

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

In 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);
    });
放肆 2024-09-22 02:43:06

使用

System.ComponentModel.DataAnnotations;

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

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

Using

System.ComponentModel.DataAnnotations;

You can simply put that attribute in your model :

[DataType("decimal(18,5)")]
停滞 2024-09-22 02:43:06

这就是我一直在寻找的,适用于普通 MVC 项目(无 .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));
    }

This is what I was looking for, works for ordinary MVC project (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);

    }
}

Package manager console

add-migration changeDecimalPrecision

Generated migration

    public override void Up()
    {
        AlterColumn("dbo.YOUR_CLASS_NAME", "YOUR_DECIAML_PROP", c => c.Decimal(nullable: false, precision: 18, scale: 6));
    }
半岛未凉 2024-09-22 02:43:06

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

You can found more information on MSDN - facet of Entity Data Model.
http://msdn.microsoft.com/en-us/library/ee382834.aspx
Full recommended.

ま昔日黯然 2024-09-22 02:43:06

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

Actual for EntityFrameworkCore 3.1.3:

some solution in 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)");
淡水深流 2024-09-22 02:43:06

您可以使用列属性精度属性简单地从模型设计类中设置小数精度和比例,如下所示:

  // Option # 1
         [Column(TypeName ="decimal(10,3)")]
            public decimal Price { get; set; } = 5.000;
        
     //Option # 2:  you can also use the ***Precision Attribute*** like the following sample code :
         [Precision(10, 3)]
         public decimal Price { get; set; }=5.000;

you can simply set decimal pricision and scale from Model Design class by using Column Attribute or Precision Attribute like following :

  // Option # 1
         [Column(TypeName ="decimal(10,3)")]
            public decimal Price { get; set; } = 5.000;
        
     //Option # 2:  you can also use the ***Precision Attribute*** like the following sample code :
         [Precision(10, 3)]
         public decimal Price { get; set; }=5.000;
临风闻羌笛 2024-09-22 02:43:06

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

KinSlayerUY's custom attribute worked nicely for me but I had issues with ComplexTypes. They were being mapped as entities in the attribute code so couldn't then be mapped as a ComplexType.

I therefore extended the code to allow for this:

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);
            }
        }
    }
↘人皮目录ツ 2024-09-22 02:43:06

@Mark007,我已经更改了类型选择标准以乘坐 DbSet<> DbContext 的属性。我认为这更安全,因为有时给定名称空间中的类不应该是模型定义的一部分,或者它们是但不是实体。或者您的实体可以驻留在单独的命名空间或单独的程序集中,并被拉到一个上下文中。

另外,尽管不太可能,但我认为依赖方法定义的顺序并不安全,因此最好通过参数列表将它们拉出来。 (.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;
            }
        }
    }

@Mark007, I have changed the type selection criteria to ride of the DbSet<> properties of the DbContext. I think this is safer because there are times when you have classes in the given namespace that shouldn't be part of the model definition or they are but are not entities. Or your entities could reside in separate namespaces or separate assemblies and be pulled together into once Context.

Also, even though unlikely, I do not think it's safe to rely on ordering of method definitions, so it's better to pull them out with by Parameter list. (.GetTypeMethods() is an extension method I built to work with the new TypeInfo paradigm and can flatten class hierarchies when looking for methods).

Do note that OnModelCreating delegates to this method:

    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;
            }
        }
    }
耀眼的星火 2024-09-22 02:43:06

我也有同样的问题,但我太懒了,找不到解决方案。

我只是使用一直存在的 double 类型,并在图形用户界面中格式化十进制数字......

我只是想在这里提到这一点,因为有时你不会想出最简单的解决方案。

I had the same question, but I'm too lazy for the solutions.

I just use the type double that has been around forever and format the decimal digits in the graphical user interface....

I just wanted to mention this here because sometimes you don't come up with the simplest solutions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文