首页 > 解决方案 > EF Core 3.1 Fluent API 未检测到实体上的更改

问题描述

我正在使用带有流畅 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 中检查我的表时,我看到 Coupon 表值列实际上使用默认值 (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,但它们都没有奏效。有什么建议还是我在这里遗漏了什么?

标签: c#.net-coreentity-framework-corecode-firstef-fluent-api

解决方案


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

解决方案很简单:

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 约定,我的对象映射从未运行过。


推荐阅读