繁体   English   中英

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

[英]EF Core 3.1 Fluent API does not detect the changes on entity

我正在使用 EF Core 3.1 和流利的 API 并在特定的 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()'.

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

优惠券 object 值字段定义如下:

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 object 中设置为HasMaxLength(120)

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

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 object 中的其他属性,但没有检测到这些更改。 例如,我尝试将我的列最大长度从 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 有什么意义?)

我在想也许我的实体 map 对象没用,但是我在实体 map 对象中创建的关系工作得很好。

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

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

解决方案很简单:

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

这意味着 Entity Framework 将为数据库提供默认精度。 默认值通常为 (18, 2)。 这意味着它将存储总共 18 个数字,其中 2 个数字位于小数点右侧。

如果您的记录有超过 2 个小数点,SQL 服务器将截断额外部分。 如果您的记录的总位数超过 18 位,您将收到“超出范围”错误。 最简单的解决方法是使用数据注释在 model 上声明默认值。 数据注释看起来像: [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)");
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM