生成された値

データベースの列の値は、さまざまな方法で生成できます。たとえば、主キー列は、自動的に増分される整数であるのが一般的です。既定値や計算値などが格納される列もあります。このページでは、EF Core で値の生成を構成するためのさまざまなパターンについて説明します。

既定値

リレーショナル データベースでは、既定値を使用して列を構成できます。行を挿入するときに、その列の値を指定しなかった場合、既定値が使用されます。

既定値は、プロパティに対して構成できます。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Rating)
        .HasDefaultValue(3);
}

既定値の計算に使用される SQL フラグメントを指定することもできます。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Created)
        .HasDefaultValueSql("getdate()");
}

計算列

ほとんどのリレーショナル データベースでは、列の構成によって、その値をデータベースで計算することができます。値の計算には、他の列を参照する式が用いられるのが一般的です。

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 を指定しても期待した効果は得られません (後述の「日付/時刻値の生成」セクションを参照)。

同様に、追加時または更新時に生成されるように構成され、コンカレンシー トークンとしてマークされる byte[] プロパティは、rowversion データ型を使用して設定されます。そうすれば、その値はデータベースで自動的に生成されます。 ただし、ValueGeneratedOnAdd を指定しても効果はありません。

サポートされている特定の値生成手法については、プロバイダーのドキュメントを参照してください。 SQL Server の値の生成に関するドキュメントについては、こちらを参照してください。

日付/時刻値の生成

よく寄せられる要望として、行が最初に挿入されたときの日付/時刻が格納されるデータベース列 (追加時の値生成) や、最後に更新されたときの日付/時刻が格納されるデータベース列 (追加または更新時の値生成) があります。 この点についてはさまざまな方法があるため、日付/時刻列の値生成を EF Core プロバイダーで自動的に設定することは通常ありません。開発者が自分で構成する必要があります。

作成のタイムスタンプ

通常、行の作成時のタイムスタンプを格納するよう日付/時刻列を構成することは、適切な SQL 関数を使用して既定値を構成することに他なりません。 たとえば、SQL Server では、次のようにすることができます。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    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 の既定値 (string なら nullint なら 0Guid なら Guid.Empty など) 以外の値に設定します。

Note

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