提问者:小点点

EF Core 3.1 Fluent API不检测实体上的更改


我正在使用EF Core3.1和fluent API,并在一个特定的地图对象中配置实体,如下所示:

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]未为实体类型“Coupon”上的十进制列“Value”指定类型。如果值不适合默认精度和小数位数,这将导致无人值守截断值。使用“HasColumnType()”显式指定可容纳所有值的SQL server列类型。

但是我实际上已经在我的map对象中定义了hasColumnType,如上面所示。首先,我认为这可能只是一个警告,因为它“不知何故”检测不到更改,但当我检查db中的表时,我发现Coupon表值列实际上使用了default(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),尽管它们在map对象中被设置为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,但都没有工作。有什么建议吗?还是我漏掉了什么?


共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约定,而且我的对象映射从来没有运行过。