37

別名コードファーストで複数のID列を作成するにはどうすればよいですか?

クラスタリングのパフォーマンスのため、一般的な推奨事項は、で作成されたGUIDの代わりに、自動インクリメントされた整数列を使用することnewid()です。

列を自動インクリメントとして宣言するには、アノテーションを使用して列を指定する必要があります[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

ただし、テーブルに含めることができるIDは1つだけです。

したがって、次のような基本モデルから始めます。

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」に設定されているテーブルへの変更はサポートされていません。代わりに「Identity」パターンを使用してください。

参考までに、コードで自動的に生成する場合は、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

これは私にとって、EntityFramework5で機能することになりました。

  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; }
    
    • ボーナス:EFIdは主キーであると想定しているようですので、必要ありません[Key, Required]
  6. 次のような移行コードを作成しますadd-migration TrickEfIntoAutogeneratingMultipleColumns

  7. Up()メソッドのステートメントAlterColumnで、データベースにGUIDを自動生成するように指示します。defaultSqlValue
    • AlterColumn(theTable, "Id", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newid()"));
  8. 移行する

これは、両方の列がIDであると想定し、それに応じて反応するという意味で、EFを「だます」ように見えます。移行中に、別の列をIDにしようとしますが、それがサイレントに失敗しても気にしないようです。一方がIdentityとしてマークされ、もう一方がデフォルト値でマークされます。

通常のコード操作中に、EFがSaveChanges / ChangeTrackingステップを実行すると、IdプロパティがIDと見なされるため、 「一時キーの割り当て」全体が実行され、デフォルトの0000000...値を使用しようとはしません。代わりに、指定したデフォルト値関数を使用してデータベースに生成させます。

Computed(私は同じことを達成したのと同じようにこのフィールドに注釈を付けることを考えていたでしょうが...質問で述べたエラー...ブー...)

また、ClusterIdフィールドはコード内のIDでもあり、実際にはデータベース内のIDであるため、自動インクリメントも行われます。

于 2012-08-17T20:27:47.130 に答える