【C#】Entity Framework Coreで主キーと自動採番の挙動を明示的に定義する

目次

概要

Entity Framework Core (EF Core) では、通常 Id という名前のプロパティが自動的に主キー(Primary Key)として扱われ、数値型であればデータベース側で自動採番(Identity)される設定になります。 しかし、社員コードや商品型番のような「意味を持つ文字列」を主キーにしたい場合や、自動採番を使わずアプリケーション側でIDを管理したい場合があります。 本記事では、[Key] 属性と [DatabaseGenerated] 属性を使用して、主キーの定義と値の生成ルールを制御する実装例を紹介します。

仕様(入出力)

  • 入力: エンティティクラスと、主キーとしたいプロパティ。
  • 処理:
    • [Key] 属性で主キーを明示。
    • [DatabaseGenerated(DatabaseGeneratedOption.None)] で自動採番を無効化。
  • 出力: 指定したプロパティが主キーとして設定され、INSERT時にデータベースによる自動採番が行われないこと。

基本の使い方

System.ComponentModel.DataAnnotations 名前空間を使用し、対象プロパティに属性を付与します。

public class Student
{
    // 自動採番させず、アプリ側で指定する学籍番号を主キーにする
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public string StudentId { get; set; }
    
    public string Name { get; set; }
}

コード全文

以下のコードは、書籍のISBNコードのような「文字列」を主キーとし、自動採番を行わずにデータを登録・検索する完全なコンソールアプリケーションの例です。

※ 実行には Microsoft.EntityFrameworkCore.InMemory パッケージが必要です。 dotnet add package Microsoft.EntityFrameworkCore.InMemory

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace PrimaryKeySample
{
    // 1. エンティティクラスの定義
    public class BookItem
    {
        // 書籍のISBNコードなどを主キーとして扱う
        // string型はデフォルトで自動採番されませんが、
        // 意図を明確にするためにOption.Noneを指定して自動生成を否定します
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public string IsbnCode { get; set; } = string.Empty;

        public string Title { get; set; } = string.Empty;

        public DateTime PublishedDate { get; set; }
    }

    // 2. DbContextの定義
    public class LibraryContext : DbContext
    {
        public DbSet<BookItem> Books { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // 動作確認用にインメモリデータベースを使用
            optionsBuilder.UseInMemoryDatabase("LibraryDb");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // データの保存(IDを自分で決めて登録)
            using (var context = new LibraryContext())
            {
                var newBook = new BookItem
                {
                    // 主キーの値をアプリケーション側で明示的に指定する
                    IsbnCode = "978-3-16-148410-0",
                    Title = "Advanced C# Programming",
                    PublishedDate = DateTime.Today
                };

                context.Books.Add(newBook);
                context.SaveChanges();

                Console.WriteLine($"保存完了: ISBN={newBook.IsbnCode}, Title={newBook.Title}");
            }

            // データの読み込み
            using (var context = new LibraryContext())
            {
                // Findメソッドは主キーを使って効率的に検索を行います
                var loadedBook = context.Books.Find("978-3-16-148410-0");

                if (loadedBook != null)
                {
                    Console.WriteLine("--- 取得データ ---");
                    Console.WriteLine($"ISBN  : {loadedBook.IsbnCode}");
                    Console.WriteLine($"Title : {loadedBook.Title}");
                }
            }
        }
    }
}

実行結果例

保存完了: ISBN=978-3-16-148410-0, Title=Advanced C# Programming
--- 取得データ ---
ISBN  : 978-3-16-148410-0
Title : Advanced C# Programming

カスタムポイント

  • DatabaseGeneratedOptionの種類
    • None: 自動生成しない。アプリ側で値をセットする(今回の例)。
    • Identity: インサート時に値を自動生成する(デフォルトの整数型PKの挙動)。
    • Computed: 更新時にも再計算される列(更新日時や計算列など)。
  • Guidの利用 Guid 型を主キーにする場合も、DB側で生成させるか(Identity)、C#側で Guid.NewGuid() するか(None)を制御できます。

注意点

  1. 重複エラー DatabaseGeneratedOption.None を使用する場合、主キーの一意性(ユニーク制約)はアプリケーション側の責任になります。既に存在するキーで保存しようとすると、DbUpdateException が発生します。
  2. 主キーの変更禁止 EF Coreでは、一度コンテキストに追跡(Attach)されたエンティティの主キー値を変更することはできません。キーを変更したい場合は、古いレコードを削除して新しいレコードを作成する必要があります。
  3. 複合キーの制限 [Key] 属性は単一カラムの主キーにのみ使用できます(EF Coreの仕様)。複数のプロパティを組み合わせた複合主キーを作りたい場合は、後述の「バリエーション」で解説する Fluent API を使用する必要があります。

バリエーション(任意)

複合主キー(Composite Key)の設定

「会員ID」と「契約年度」の組み合わせで一意になるようなテーブルの場合、DbContextOnModelCreating メソッドで定義します。 ※データ注釈(Attribute)だけでは複合キーを定義できません。

using Microsoft.EntityFrameworkCore;

// エンティティ
public class AnnualContract
{
    public int MemberId { get; set; } // 主キーの一部
    public int Year { get; set; }     // 主キーの一部
    public string PlanName { get; set; }
}

// DbContext
public class ContractContext : DbContext
{
    public DbSet<AnnualContract> Contracts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 2つのプロパティを組み合わせて主キーとする
        modelBuilder.Entity<AnnualContract>()
            .HasKey(c => new { c.MemberId, c.Year });
    }
}

まとめ

複数の列で主キーを構成する「複合キー」の場合は、属性ではなく OnModelCreating 内の HasKey メソッドを使用します。

規約(Idプロパティ)に従わない主キーを設定するには [Key] 属性を使用します。

自動採番を無効化して自分で値を設定するには [DatabaseGenerated(DatabaseGeneratedOption.None)] を併用します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次