我正在使用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,如下所示,并添加新的迁移,但我的up
和down
函数是空的。
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; }
如果我这样做并创建新的迁移,我会看到它立即检测到更改,并且我的up
和down
函数如下所示:
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,但都没有工作。有什么建议吗?还是我漏掉了什么?
问题是,那些映射对象配置函数根本没有被调用。
解决办法很简单:
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约定,而且我的对象映射从来没有运行过。