web-dev-qa-db-ja.com

別のID列を持つIDとしてGuidを最初に使用するEntity Frameworkコード

a.k.a Code Firstで複数のID列を作成するにはどうすればよいですか?

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

列を自動インクリメントとして宣言するには、注釈[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'パターンを使用してください。

[〜#〜] 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();
  }

}
36
drzaus

これは、Entity Framework 5で機能しました。

  1. 自動移行をオフにする
  2. 移行して初期テーブルを作成します。フリルはありません
  3. ClusterIdをIDとして宣言します(注釈)

    _[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public override int ClusterId { get; set; }
    _
  4. 移行する

  5. PkプロパティIdをIdentityとして宣言します、もう1つが更新された後

    _[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public override Guid Id { get; set; }
    _
    • bonus: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. 移行する

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

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

(このフィールドにComputedとして注釈を付けると同じことを成し遂げると思っていましたが、...質問で言及したエラー... boo ...)

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

36
drzaus