生成された値

データベースの列の値は、さまざまな方法で生成できます。たとえば、主キー列は、自動的に増分される整数であるのが一般的です。既定値や計算値などが格納される列もあります。このページでは、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);

注意

格納される計算列の作成は、EF Core 5.0. で新たにサポートされました。

主キー

規約上、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 しても効果はありません。

注意

ご使用のデータベース プロバイダーによっては、クライアント側で EF によって、またはデータベースで値が生成されます。 データベースによって値が生成される場合、エンティティをコンテキストに追加したときに一時的な値が EF によって割り当てられることがあります。この一時的な値が、その後、SaveChanges() の過程で、データベースによって生成された値に置き換えられます。 詳細については、一時的な値に関するドキュメントを参照してください。

日付/時刻値の生成

よく寄せられる要望として、列が最初に挿入されたときの日付/時刻が格納されるデータベース列 (追加時の値生成) や、最後に更新されたときの日付/時刻が格納されるデータベース列 (追加または更新時の値生成) があります。 この点についてはさまざまな方法があるため、日付/時刻列の値生成を 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;

    DECLARE @Id INT

    SELECT @Id = INSERTED.BlogId
    FROM INSERTED

    UPDATE dbo.Blogs
    SET LastUpdated = GETDATE()
    WHERE BlogId = @Id
END

トリガーの作成については、移行における生 SQL の使用に関するドキュメントを参照してください。

値生成のオーバーライド

値を生成するようにプロパティが構成されていても、その値は、多くの場合、開発者が明示的に指定できます。 これが実際にうまく行くかどうかは、具体的に構成されている値生成メカニズムによって異なります。列の既定値を使用せずに明示的に値を指定することはできますが、同じことを計算列で行うことはできません。

明示的な値で値生成をオーバーライドしたければ、単にプロパティを、その型に使用される CLR の既定値 (string なら nullint なら 0Guid なら Guid.Empty など) 以外の値に設定します。

注意

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