データベース列には、さまざまな方法で値を生成できます。主キー列は頻繁に自動インクリメントされる整数、他の列には既定値や計算値があります。このページでは、EF Core を使用した構成値の生成のさまざまなパターンについて詳しく説明します。
既定値
リレーショナル データベースでは、列を既定値で構成できます。その列の値なしで行が挿入された場合、既定値が使用されます。
プロパティに既定値を構成できます。
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3);
既定値の計算に使用する SQL フラグメントを指定することもできます。
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
EF 10 以降では、SQL Server では既定値制約の名前を明示的に指定できるため、データベース スキーマをより詳細に制御できます。
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3, "DF_Blog_IsActive");
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()" , "DF_Blog_IsActive");
UseNamedDefaultConstraints
を呼び出して、すべての既定の制約の自動名前付けを有効にすることもできます。 既存の移行がある場合、次に追加する移行では、モデル内のすべての既定の制約の名前が変更されることに注意してください。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseNamedDefaultConstraints();
}
計算列
ほとんどのリレーショナル データベースでは、列の値がデータベースで計算されるように構成できます。通常は、他の列を参照する式を使用します。
modelBuilder.Entity<Person>()
.Property(p => p.DisplayName)
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
上記では 、仮想 計算列が作成されます。この列の値は、データベースからフェッチされるたびに計算されます。 計算列を 格納 する ( 永続化と呼ばれることもあります) ことも指定できます。つまり、行の更新ごとに計算され、通常の列と共にディスクに格納されます。
modelBuilder.Entity<Person>()
.Property(p => p.NameLength)
.HasComputedColumnSql("LEN([LastName]) + LEN([FirstName])", stored: true);
主キー
慣例により、short、int、long、または Guid 型の非複合主キーは、値がアプリケーションによって提供されない場合に、挿入されたエンティティに対して生成される値を持つよう設定されます。 通常、データベース プロバイダーは必要な構成を処理します。たとえば、SQL Server の数値主キーは自動的に IDENTITY 列に設定されます。
詳細については、特定の継承マッピング戦略のキーとガイダンスに関するドキュメントを参照してください。
値の生成を明示的に構成する
上記では、EF Core によって主キーの値の生成が自動的に設定されることを確認しましたが、キー以外のプロパティでも同じ操作を行う必要があります。 次のように、挿入されたエンティティに対して生成される値を持つ任意のプロパティを構成できます。
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime Inserted { get; set; }
}
同様に、プロパティは、追加または更新時にその値を生成するように構成できます。
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime LastUpdated { get; set; }
}
既定値や計算列とは異なり、値の生成 方法 は指定していません。使用されているデータベース プロバイダーによって異なります。 データベース プロバイダーは、一部のプロパティ型に対して値の生成を自動的に設定する場合がありますが、値の生成方法を手動で設定する必要がある場合もあります。
たとえば、SQL Server では、GUID プロパティが主キーとして構成されている場合、プロバイダーは、最適なシーケンシャル GUID 値を生成するアルゴリズムを使用して、値生成クライアント側を自動的に実行します。 ただし、DateTime プロパティに ValueGeneratedOnAdd を指定しても効果はありません (DateTime 値の生成については、以下のセクションを参照してください)。
同様に、追加または更新時に生成され、コンカレンシー トークンとしてマークされた byte[] プロパティは、rowversion データ型で設定されるため、値はデータベースに自動的に生成されます。 ただし、 ValueGeneratedOnAdd を指定しても効果はありません。
サポートされている特定の値生成手法については、プロバイダーのドキュメントを参照してください。 SQL Server の値の生成に関するドキュメント については、こちらをご覧ください。
日付/時刻値の生成
一般的な要求は、行が最初に挿入されたとき (追加時に生成された値)、または最後に更新された日時 (追加または更新時に生成された値) の日付/時刻を含むデータベース列を持つことです。 これを行うにはさまざまな方法があるため、EF Core プロバイダーでは、通常、日付/時刻列に対して値の生成が自動的に設定されることはありません。これを自分で構成する必要があります。
作成タイムスタンプ
通常、行の作成タイムスタンプを持つ日付/時刻列を構成することは、適切な SQL 関数を使用して既定値を構成する必要があります。 たとえば、SQL Server では次を使用できます。
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
複数の関数が存在する可能性があるため、必ず適切な関数を選択してください (例: GETDATE()
と GETUTCDATE()
)。
タイムスタンプの更新
格納された計算列は、最後に更新されたタイムスタンプを管理するための適切なソリューションのように見えますが、データベースでは通常、計算列に GETDATE()
などの関数を指定することはできません。 別の方法として、同じ効果を得るためにデータベース トリガーを設定できます。
CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
UPDATE B
SET LastUpdated = GETDATE()
FROM dbo.Blogs AS B
INNER JOIN INSERTED AS I
ON B.BlogId = I.BlogId
END
トリガーの作成については、 移行での生 SQL の使用に関するドキュメントを参照してください。
値生成をオーバーライドすること
プロパティは値の生成用に構成されていますが、多くの場合、値を明示的に指定することもできます。 これが実際に機能するかどうかは、構成されている特定の値生成メカニズムによって異なります。列の既定値を使用する代わりに明示的な値を指定できますが、計算列でも同じことはできません。
値の生成を明示的な値でオーバーライドするには、そのプロパティの型の CLR 既定値ではない値 (null
のstring
、0
のint
、Guid.Empty
のGuid
など) にプロパティを設定するだけです。
注
SQL Server IDENTITY に明示的な値を挿入しようとすると、既定で失敗します。 回避策については、次のドキュメントを参照してください。
追加または更新時に生成される値として構成されているプロパティに明示的な値を指定するには、次のようにプロパティを構成する必要もあります。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property(b => b.LastUpdated)
.ValueGeneratedOnAddOrUpdate()
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
}
値の生成なし
上記のような特定のシナリオとは別に、プロパティには通常、値の生成が構成されません。つまり、データベースに保存する値を常に指定するのはアプリケーションの側です。 この値は、コンテキストに追加する前に、新しいエンティティに割り当てる必要があります。
ただし、規則によって設定された値の生成を無効にしたい場合もあります。 たとえば、int 型の主キーは、通常、値が生成されたアドオン (SQL Server の ID 列など) として暗黙的に構成されます。 これを無効にするには、次のようにします。
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BlogId { get; set; }
public string Url { get; set; }
}
.NET