【环境】:.NET Core 2.2 + EF Core 2.2,使用Code First模式,实体Entity的映射没有写在Entity里,分离了出来单独配置,通过实现IEntityTypeConfiguration接口来实现映射的,具体代码如下:
实体类(EntityBase):
/// <summary>实体基类</summary>
public class EntityBase
{
/// <summary>主键</summary>
/// <value>The identifier.</value>
public long Id { get; set; }
/// <summary>创建人姓名</summary>
/// <value>The name of the create.</value>
public string CreateName { get; set; } = string.Empty;
/// <summary>创建人Id</summary>
/// <value>The create identifier.</value>
public long? CreateId { get; set; }
/// <summary>创建时间</summary>
/// <value>The create time.</value>
public DateTime? CreateTime { get; set; }
/// <summary>更新人姓名</summary>
/// <value>The name of the update.</value>
public string UpdateName { get; set; } = string.Empty;
/// <summary>更新人Id</summary>
/// <value>The update identifier.</value>
public long? UpdateId { get; set; }
/// <summary>更新时间</summary>
/// <value>The update time.</value>
public DateTime? UpdateTime { get; set; }
/// <summary>数据状态</summary>
/// <value>The state of the data.</value>
public bool IsDeleted { get; set; }
/// <summary>基础字段设置方法</summary>
/// <value>The automatic setter.</value>
public IEntityBaseAutoSetter AutoSetter { get; set; }
}
映射类:
/// <summary>Map实体基类的基础字段</summary>
/// <typeparam name="T"></typeparam>
public abstract class EntityBaseTypeConfig<T> : IEntityTypeConfiguration<T> where T : EntityBase
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.HasKey(it => it.Id);
builder.Property(x => x.CreateId).IsRequired(false);
builder.Property(x => x.CreateName).HasMaxLength(128).IsRequired(false);
builder.Property(x => x.CreateTime).IsRequired(false);
builder.Property(x => x.UpdateId).IsRequired(false);
builder.Property(x => x.UpdateName).HasMaxLength(128).IsRequired(false);
builder.Property(x => x.UpdateTime).IsRequired(false);
builder.Property(x => x.IsDeleted).IsRequired();
builder.Ignore(x => x.AutoSetter);
}
}
【问题】
其中Id设置了主键,在Code First生成数据库时,主键自动设置了自增长,但是框架在向表添加数据时会生成Guid赋值给主键Id,导致保存到数据库是报错,报错内容如下:
[ System.Data.SqlClient.SqlException (0x80131904): 当 IDENTITY_INSERT 设置为 OFF 时,不能为表 'Student' 中的标识列插入显式值。]
【期望】
映射的时候,如何设置主键不增长,或者说如何设置主键可赋值?
框架可以使用自己的规则生成Id作为主键存入数据库。
【补充】
在EF 6.x的版本中,可以通过继承EntityTypeConfiguration类,然后设置如下方式实现
this.Property<long>((Expression<Func<T, long>>) (x => x.Id)).IsRequired().HasDatabaseGeneratedOption(new DatabaseGeneratedOption?(DatabaseGeneratedOption.None));
但是在EF Core中我好像没有找到类似的方法
试试 builder.Property(x => x.Id).ValueGeneratedNever();
感谢,已解决。
但是同时又发现了新的问题:在修改主键字段属性的时候,EF Core会报错【更改列的IDENTITY属性,需要删除并重新创建列】,只能删掉再重新创建。
搜索后发现,在EF Core的源码下已经有人提过issue了(https://github.com/aspnet/EntityFrameworkCore/issues/7444)。
这是EF Core当前版本还没有解决的Bug?
@在7楼: 为什么不直接在数据库中修改?
@dudu: 没有针对这个问题和解决方案,主要是好奇这个是属于EF Core遗留的未解决Bug,还是以后版本也不打算解决(设计就是一旦Code First初始化主键之后就不可以修改主键属性了)?
@在7楼: 等 EF Core 3.0
这是设计思维的理解不一样, 主键是一个对象标识自身不同于其它对象的方式。
如果修改主键,那么就有可能把主键的值修改成与其它对象的主键相同,那么主键本身的【唯一性】功能就尚是了,接下来还会引发更严重的事情,造成一个表中存在相同主键的多个对象。
—— 当然题主肯定是不会做这种操作的,但从编程的绝对可靠原则上,为了避免此类事件发生,所以不允许修改主键,个人观点。