37

aka我们如何在 Code First 中创建多个标识列?

由于集群性能,一个常见的建议是使用自动递增的整数列,而不是使用创建的 GUID newid()

为了将列声明为自动增量,您必须使用 Annotation 指定它[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

但是,您在一个表中只能有一个身份。

所以从一个基本模型开始,比如:

public abstract class ModelBase {
    // the primary key
    public virtual Guid Id { get; set; }

    // a unique autoincrementing key
    public virtual int ClusterId { get; set; }
}

我们如何设置它以便:

  1. Guid由数据库自动生成,而不是代码
  2. ClusterId是自动递增的
  3. Entity Framework Code First 不会引发各种错误,例如:
    • 不支持对主键列的属性“StoreGeneratedPattern”设置为“Computed”的表进行修改。请改用“身份”模式。

仅供参考,如果您确实想在代码中自动生成它,您可以跳过 Id 字段上的注释并执行以下操作:

public abstract class AbstractContext : DbContext {

  /// <summary>
  /// Custom processing when saving entities in changetracker
  /// </summary>
  /// <returns></returns>
  public override int SaveChanges()
  {
      // recommended to explicitly set New Guid for appropriate entities -- http://msdn.microsoft.com/en-us/library/dd283139.aspx
      foreach (var entry in ChangeTracker.Entries<ModelBase>().Where(e => e.State == EntityState.Added) ) {

          // only generate if property isn't identity...
          Type t = entry.Entity.GetType();
          var info = t.GetProperty("Id").GetCustomAttributes(
              typeof(DatabaseGeneratedAttribute), true).Cast<DatabaseGeneratedAttribute>().Single();

          if (info.DatabaseGeneratedOption != DatabaseGeneratedOption.Identity) {
              entry.Entity.Id = Guid.NewGuid(); // now we make it
          }
      }
      return base.SaveChanges();
  }

}
4

1 回答 1

36

这最终对我有用,Entity Framework 5。

  1. 关闭自动迁移
  2. 迁移以创建初始表,没有多余的装饰
  3. 声明ClusterId为身份(注解)

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public override int ClusterId { get; set; }
    
  4. 迁移

  5. 在另一个更新后将pk 属性声明Id为 Identity

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public override Guid Id { get; set; }
    
    • 奖金:EF 似乎假设Id是主键,所以你不需要[Key, Required]
  6. 创建迁移代码,如add-migration TrickEfIntoAutogeneratingMultipleColumns

  7. Up()方法中,在AlterColumn语句中,通过声明defaultSqlValue
    • AlterColumn(theTable, "Id", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newid()"));
  8. 迁移

这似乎“欺骗”了 EF,因为它假设两列都是身份并做出相应的反应。在迁移过程中,它尝试使另一列成为身份,但似乎并不关心何时静默失败——您最终将一个标记为身份,另一个标记为默认值。

在正常代码操作期间,当 EF 执行 SaveChanges/ChangeTracking 步骤时,因为它将Id属性视为身份,所以它是整个“分配临时密钥”的事情,因此它不会尝试使用默认的 0000000... 值,并且而是让数据库使用您指定的默认值函数生成它。

(我本来以为注释这个字段Computed会完成同样的事情,但是......我在问题中提到的错误......嘘......)

而且,因为该ClusterId字段在代码中也是一个身份,并且在数据库中确实是一个身份,所以它也会自动递增。

于 2012-08-17T20:27:47.130 回答