【问题标题】:EF Core 3.1 Fluent API does not detect the changes on entityEF Core 3.1 Fluent API 未检测到实体上的更改
【发布时间】:2020-04-20 15:26:38
【问题描述】:

我正在使用带有流畅 API 的 EF Core 3.1,并在特定的 Map 对象中配置实体,如下所示:

public class CouponMap: IEntityTypeConfiguration < Coupon > {
    public void Configure(EntityTypeBuilder < Coupon > builder) {
        builder.HasKey(t = >t.Id);
        builder.Property(t = >t.Id).ValueGeneratedOnAdd();
        builder.Property(e = >e.Name).HasMaxLength(120).IsRequired();
        builder.Property(e = >e.Code).HasMaxLength(120).IsRequired();
        builder.Property(e = >e.Value).HasColumnType("decimal(19,4)").IsRequired();

        // Relations...
        builder.HasOne < AppUser > (e = >e.CreatedByUser).WithMany().HasForeignKey(e = >e.DeletedBy).IsRequired();
        builder.HasOne < AppUser > (e = >e.DeletedByUser).WithMany().HasForeignKey(e = >e.CreatedBy).IsRequired();
        builder.HasOne < AppUser > (e = >e.UpdatedByUser).WithMany().HasForeignKey(e = >e.UpdatedBy);

        // Set table name
        builder.ToTable("Coupon", "dbo");
    }
}

然后我在调试控制台上注意到,我的十进制字段有警告,例如:

Microsoft.EntityFrameworkCore.Model.Validation[30000] No type was specified for the decimal column 'Value' on entity type 'Coupon'. This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'.

但我实际上已经在我的地图对象中定义了HasColumnType,如您在上面看到的。首先我认为这可能只是一个警告,因为它无法“以某种方式”检测到更改,但是当我在 db 中检查我的表时,我看到优惠券表值列实际上使用默认值 (18,2) 作为十进制精度刻度而不是 ( 19,4)。

优惠券对象值字段定义如下:

public class Coupon : IHasIdColumn<int>
    {         
        public int Id { get; set; }
        public string Name { get; set; }
        .
        .
        .      
        public decimal Value { get; set; } // <== the problem guy!
        .
        .           
   }

此外,如果您注意到,代码和名称字段甚至被创建为 NVARCHAR(MAX),尽管它们在地图对象中已设置为 HasMaxLength(120)

之后我检查我的模型快照以确保我的实体脚本是如何生成的,只是为了确认它忽略了我的小数(19,4)并确认它实际上在模型快照中创建了表脚本:

modelBuilder.Entity("MyProject.Core.DomainModels.Coupon", b = >{
    b.Property < int > ("Id").ValueGeneratedOnAdd().HasColumnType("int").HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
    .
    .
    b.Property < decimal > ("Value").HasColumnType("decimal(18,2)");
    .  
    .
    . goes with other properties
}

然后这让我明白了,所以我尝试更新我的 CouponMap 对象中的其他属性,但没有检测到这些更改。例如,我尝试将我的列最大长度从 120 更改为 500,如下所示并添加新的迁移,但我的 updown 函数为空。

public class CouponMap: IEntityTypeConfiguration < Coupon > {
    public void Configure(EntityTypeBuilder < Coupon > builder) {
        builder.HasKey(t = >t.Id);
        builder.Property(t = >t.Id).ValueGeneratedOnAdd();
        builder.Property(e = >e.Name).HasMaxLength(500).IsRequired(); // changed this but not detected in migration...
        builder.Property(e = >e.Code).HasMaxLength(500).IsRequired(); // also changed this but not detectedin migration...
        builder.Property(e = >e.Value).HasColumnType("decimal(19,4)").IsRequired(); // this somehow was not being detected anyways so I tried other 2 columns above

        // Relations...
        builder.HasOne < AppUser > (e = >e.CreatedByUser).WithMany().HasForeignKey(e = >e.DeletedBy).IsRequired();
        builder.HasOne < AppUser > (e = >e.DeletedByUser).WithMany().HasForeignKey(e = >e.CreatedBy).IsRequired();
        builder.HasOne < AppUser > (e = >e.UpdatedByUser).WithMany().HasForeignKey(e = >e.UpdatedBy);

        // Set table name
        builder.ToTable("Coupon", "dbo");
    }
}

我知道我可以使用System.ComponentModel.DataAnnotations.Schema 来装饰我的财产,如下所示:

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

如果我这样做并创建新的迁移,我会看到它立即检测到更改,并且我的 updown 函数如下所示:

 protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AlterColumn<decimal>(
        name: "Value",
        table: "Coupon",
        type: "decimal(19,4)",
        nullable: false,
        oldClrType: typeof(decimal),
        oldType: "decimal(18,2)");
}

protected override void Down(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AlterColumn<decimal>(
        name: "Value",
        table: "Coupon",
        type: "decimal(18,2)",
        nullable: false,
        oldClrType: typeof(decimal),
        oldType: "decimal(19,4)");
}

但我想保持我的域对象尽可能干净,并且只使用EntityMap 类来定义与数据库相关的任何实体类型配置(否则使用 FluentAPI 有什么意义?)

我在想我的实体映射对象可能没用,但是我在实体映射对象中创建的关系工作得很好。

我尝试清理解决方案、重建、重新启动 VS,但都没有奏效。有什么建议还是我在这里遗漏了什么?

【问题讨论】:

  • CouponMap.Configure 中的代码很可能没有被执行。
  • @IvanStoev - 我实际上是这么想的,但我所有的关系都工作正常(也在 CouponMap.Configure 中定义)。
  • 好吧,KeyValueGeneratedOnAdd、关系 - 所有这些都可以在没有流畅配置的情况下工作,因为它们符合 EF Core 约定。为了测试,把Configure方法的内容注释掉。或者在里面添加类似throw new Exception("Blah");的东西。
  • @IvanStoev - 你是对的。非常感谢。它根本没有抛出那个异常,因为我没有在 DbContext 的 OnModelCreating 函数中调用 builder.ApplyConfiguration ......呃......

标签: c# .net-core entity-framework-core code-first ef-fluent-api


【解决方案1】:

问题是,那些映射对象的配置函数根本没有被调用。

解决方法很简单:

protected override void OnModelCreating(ModelBuilder builder)
        { 
            // configuring mappings one by one like below..
            //  builder.ApplyConfiguration(new CouponMap());
            //  builder.ApplyConfiguration(new WebOrderMap());

            // or configuring all the mappings by using assembly..
            builder.ApplyConfigurationsFromAssembly(typeof(CouponMap).Assembly);

            base.OnModelCreating(builder);
        } 

感谢@Ivan Stoev,他指出我在这些映射对象中抛出异常以查看它们是否会触发......它愚弄了我,因为我所有的关系都非常好,所以我认为这是因为我的映射对象有效美好的。但实际上只是因为 EF 约定,我的对象映射从未运行过。

【讨论】:

    【解决方案2】:

    这意味着 Entity Framework 将为 数据库。默认值通常为 (18, 2)。这意味着它将存储 总共 18 个数字,其中 2 个数字在右边 小数点。

    如果您的记录有超过 2 个小数点,SQL Server 将 截断额外内容。如果您的记录总位数超过 18 位,您 将出现“超出范围”错误。最简单的解决方法是使用数据 在模型上声明默认值的注释。数据标注 看起来像:[Column(TypeName = "decimal(18,2)")]

    public class Product {
        public int ID { get; set; }
        public string Title { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal Price { get; set; }
    }
    

    您也可以将其添加到 DbContext 的 OnModelCreating 方法中 实现而不是上述方法。它看起来类似于 以下:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Product>()
            .Property(p => p.Price)
            .HasColumnType("decimal(18,2)");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-22
      • 2015-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多