实体框架代码首先使用Guid作为身份与另一个标识列 [英] Entity Framework Code First Using Guid as Identity with another Identity Column

查看:75
本文介绍了实体框架代码首先使用Guid作为身份与另一个标识列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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



但是,你只能有一个表格中的一个身份。



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

  public abstract class ModelBase {
//主键
public virtual Guid Id {get;组; }

//一个唯一的自动增量键
public virtual int ClusterId {get;组;
}

我们如何设置,以便:


  1. Guid由数据库自动生成,而不是代码

  2. ClusterId 自动增量

  3. 实体框架代码首先不会抛出各种错误,如:

    • 不支持将主键列的属性StoreGeneratedPattern设置为Computed的表的修改。使用身份模式。







FYI ,如果您想要在代码中自动生成,您可以跳过Id字段上的注释,并执行以下操作:

  public abstract class AbstractContext:DbContext {

///< summary>
///在changetracker中保存实体时的自定义处理
///< / summary>
///< returns>< / returns>
public override int SaveChanges()
{
//建议为适当的实体显式设置New Guid - http://msdn.microsoft.com/en-us/library/dd283139。 aspx
foreach(ChangeTracker.Entries< ModelBase>()中的var项)其中(e => e.State == EntityState.Added)){

//只生成属性不是身份...
类型t = entry.Entity.GetType();
var info = t.GetProperty(Id)GetCustomAttributes(
typeof(DatabaseGeneratedAttribute),true).Cast< DatabaseGeneratedAttribute>()。

if(info.DatabaseGeneratedOption!= DatabaseGeneratedOption.Identity){
entry.Entity.Id = Guid.NewGuid(); //现在我们让它
}
}
return base.SaveChanges();
}

}


解决方案

最终为我工作,实体框架5。


  1. 关闭自动迁移

  2. 迁移以创建初始表,没有褶边

  3. ClusterId 声明为身份(注释)

      [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public override int ClusterId {get;组;


  4. 迁移


  5. p>在另一个更新后,将pk属性 Id 声明为身份

      [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public override Guid Id {get;组;




    • 奖金:/ EF:EF似乎假设 Id 是主键,所以你不需要 [Key,Required]


  6. 创建迁移代码,如 add-migration TrickEfIntoAutogeneratingMultipleColumns


  7. Up()方法中,在 AlterColumn 语句中,告诉数据库自动生成GUID通过声明 defaultSqlValue


    • AlterColumn(theTable,Id,c => c.Guid(nullable:false,identity:true,defaultValueSql:newid()));


  8. 迁移

这似乎是欺骗EF,在这个意义上,它假定两列都是身份,并相应地作出反应。在迁移期间,它尝试使另一列成为一个身份,但似乎并不在意,当这个静默失败的时候,最终会有一个被标记为身份,另一个是默认值。



在正常代码操作期间,当EF经历SaveChanges / ChangeTracking步骤时,因为它将 Id 属性看作一个标识,它的整体是分配临时密钥的东西,所以不尝试使用默认的0000000 ...值,而是让数据库使用您指定的默认值函数生成它。



(我会想到将这个字段注释为 Computed 将会完成同样的事情,但是。 ..我在问题中提到的错误... boo ...)



而且,因为 ClusterId 字段也是代码中的标识,真正是数据库中的标识,它也自动增加。


a.k.a How can we create multiple identity columns in Code First?

Because of clustering performance, a common recommendation is to use an autoincremented integer column instead of a GUID created with newid().

In order to declare a column as autoincrement, you have to specify it with the Annotation [DatabaseGenerated(DatabaseGeneratedOption.Identity)].

But, you can only have one identity in a table.

So starting with a base model like:

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

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

how do we set it up so that:

  1. Guid is automatically generated by the database, not code
  2. ClusterId is autoincremented
  3. Entity Framework Code First doesn't throw all sorts of errors like:
    • Modifications to tables where a primary key column has property 'StoreGeneratedPattern' set to 'Computed' are not supported. Use 'Identity' pattern instead.


FYI, if you do want to automatically generate it in code, you could skip the annotation on the Id field and do something like:

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();
  }

}

解决方案

This ended up working for me, Entity Framework 5.

  1. Turn off automatic migrations
  2. Migrate to create the initial table, no frills
  3. Declare the ClusterId as Identity (annotation)

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

  4. Migrate

  5. Declare the pk property Id as Identity after the other one has been updated

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public override Guid Id { get; set; }
    

    • bonus: EF seems to assume Id is primary key, so you don't need [Key, Required]
  6. Create the migration code like add-migration TrickEfIntoAutogeneratingMultipleColumns

  7. In the Up() method, in the AlterColumn statement, tell the database to autogenerate the GUID by declaring the defaultSqlValue
    • AlterColumn(theTable, "Id", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newid()"));
  8. Migrate

This seems to "trick" EF, in the sense that it assumes both columns are identities and reacts accordingly. During migration, it tries to make another column an identity, but seemingly doesn't care when that silently fails -- you end up with one marked as Identity and the other with a default value.

During normal code operation, when EF goes through the SaveChanges/ChangeTracking steps, because it sees the Id property as an Identity it does it's whole "assign temporary key" thing, so that it's not trying to use the default 0000000... value, and instead lets the database generate it using the default value function you specified.

(I would have thought annotating this field as Computed would have accomplished the same thing, but...the errors I mentioned in the question...boo...)

And, because the ClusterId field is also an Identity in code, and really is an Identity in the database, it autoincrements as well.

这篇关于实体框架代码首先使用Guid作为身份与另一个标识列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆