概要
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)を制御できます。
注意点
- 重複エラー
DatabaseGeneratedOption.Noneを使用する場合、主キーの一意性(ユニーク制約)はアプリケーション側の責任になります。既に存在するキーで保存しようとすると、DbUpdateExceptionが発生します。 - 主キーの変更禁止 EF Coreでは、一度コンテキストに追跡(Attach)されたエンティティの主キー値を変更することはできません。キーを変更したい場合は、古いレコードを削除して新しいレコードを作成する必要があります。
- 複合キーの制限
[Key]属性は単一カラムの主キーにのみ使用できます(EF Coreの仕様)。複数のプロパティを組み合わせた複合主キーを作りたい場合は、後述の「バリエーション」で解説する Fluent API を使用する必要があります。
バリエーション(任意)
複合主キー(Composite Key)の設定
「会員ID」と「契約年度」の組み合わせで一意になるようなテーブルの場合、DbContext の OnModelCreating メソッドで定義します。 ※データ注釈(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)] を併用します。
