实体框架代码首先使用Guid作为另一个标识列的标识

时间:2012-08-17 20:27:47

标签: entity-framework ef-code-first entity-framework-5 ef-migrations

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

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

为了将列声明为自动增量,您必须使用注释[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. 实体框架代码首先不会抛出各种错误,例如:
    • 修改主键列具有属性' StoreGeneratedPattern'设置为' Computed'不受支持。使用'身份'而不是模式。

  4. FYI ,如果你想在代码中自动生成它,你可以跳过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();
      }
    
    }
    

1 个答案:

答案 0 :(得分:36)

这最终为我工作,实体框架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告诉数据库自动生成GUID
    • AlterColumn(theTable, "Id", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newid()"));
  8. 迁移
  9. 这似乎是“欺骗”EF,因为它假定两列都是同一性并做出相应的反应。在迁移过程中,它会尝试将另一列作为标识,但看起来并不关心何时静默失败 - 最终会标记为Identity而另一个标记为默认值。

    在正常的代码操作期间,当EF通过SaveChanges / ChangeTracking步骤时,因为它将Id属性视为一个标识它完成"assign temporary key" thing,所以它不会尝试使用默认值0000000 ... value,而是让数据库使用您指定的默认值函数生成它。

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

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