循环/反映所有EF模型中的所有属性以设置列类型

时间:2017-01-04 16:27:30

标签: entity-framework asp.net-core entity-framework-core

我的客户端有一个标准,用于存储带有十进制(13,4)规范的SQL Server小数。因此,在一个非常庞大且仍在增长的架构中,我有近百个这样的陈述:

builder.Entity<MyObject>()
    .Property(x => x.MyField1)
    .ForSqlServerHasColumnType("decimal(13,4)");
builder.Entity<MyObject>()
    .Property(x => x.MyField2)
    .ForSqlServerHasColumnType("decimal(13,4)");
builder.Entity<MyObject2>()
    .Property(x => x.MyField1)
    .ForSqlServerHasColumnType("decimal(13,4)");

如果有一个功能,我可以告诉EF直接所有小数都应该是十进制(13,4)默认情况下,我想使用它。如果没有,我可以使用反射循环遍历模型中的每个对象/属性,所以我可以在几个语句中执行此操作吗?

类似的东西:

foreach(var efObj in EntityFrameWorkObjects)
{
    foreach (var objProperty in efObj)
    {
        if (objProperty is decimal || objProperty is decimal?)
        {
            builder.Entity<efObj>()
                .Property(x => x.efObj)
                .ForSqlServerHasColumnType("decimal(13,4)");
        }
    }
}

反射似乎是一个很好的方法,因为那时我可以实现一些其他约定,其中,如果一个对象具有名称和描述,则名称是必需的并且限制为256个字符。

更新 我按照伊万评论中的链接进行了调整并对其进行了调整,这对我有用:

foreach (var p in builder.Model
    .GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => 
        p.ClrType == typeof(decimal) ||
        p.ClrType == typeof(decimal?)))
{
    p.SqlServer().ColumnType = "decimal(13,4)";
}

不久之后,他提供了一个完整的答案,我稍微改了一下,以使用十进制和可空的十进制:

foreach (var pb in builder.Model
    .GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => 
        p.ClrType == typeof(decimal) ||
        p.ClrType == typeof(decimal?))
    .Select(p => 
        builder.Entity(p.DeclaringEntityType.ClrType)
            .Property(p.Name)))
{
    pb.ForSqlServerHasColumnType("decimal(13,4)");
}

这两种方法都有效!

更新2:我必须将我的对象声明为DbSet&lt;&gt;在上下文工作的上下文中。当我逐行设置属性时,似乎不需要这样做。

3 个答案:

答案 0 :(得分:26)

在EF Core v1.1.0中,您可以使用以下内容:

foreach (var pb in modelBuilder.Model
    .GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?))
    .Select(p => modelBuilder.Entity(p.DeclaringEntityType.ClrType).Property(p.Name)))
{
    pb.ForSqlServerHasColumnType("decimal(13,4)");
}

更新:从EF Core 2.0开始,为每个数据库提供程序单独构建模型,因此HasAbcXyz方法将替换为公共HasXyz。更新的代码(也跳过显式配置的属性)如下所示:

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?)))
{
    if (property.Relational().ColumnType == null)
        property.Relational().ColumnType = "decimal(13,4)";
}

答案 1 :(得分:1)

当我使用 EFCore 5.0.1 DB-First 时,上述方法不起作用。 MS document 上的以下方法有效:

[Column(TypeName = "decimal(18, 4)")]
public decimal Numeric { get; set; }

答案 2 :(得分:0)

新功能将在EF Core 5.0中引入

modelBuilder
    .Entity<Blog>()
    .Property(b => b.Numeric)
    .HasPrecision(16, 4);

参考:https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#preview-4