目次
概要
Entity Framework Core (EF Core) はデフォルトでクラス名とプロパティ名をそのままデータベースのテーブル名やカラム名として扱いますが、既存のデータベース(レガシーシステム等)と連携する場合、名前が一致しないことが多々あります。 本レシピでは、データ注釈(Data Annotations)を使用して、C#のクラス定義とデータベース側の物理名を明示的にマッピングする方法を解説します。
仕様(入出力)
- 入力: エンティティクラスおよびそのプロパティ。
- 処理:
[Table]属性および[Column]属性を付与してマッピング先を指定する。 - 出力: 指定したテーブル名・カラム名に対してCRUD操作が行われること。
基本の使い方
System.ComponentModel.DataAnnotations.Schema 名前空間を使用し、クラスに [Table]、プロパティに [Column] 属性を付与します。
[Table("tbl_employee_master")]
public class Employee
{
[Column("emp_id")]
public int Id { get; set; }
}
コード全文
以下のコードは、C#のクラス(PascalCase)とデータベース(スネークケース等の独自規約)をマッピングし、データの保存と読み出しを行う完全なコンソールアプリケーションです。
※ 実行には 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 TableMappingSample
{
// 1. エンティティクラスの定義
// データベース上のテーブル名 "tbl_products_mst" にマッピング
[Table("tbl_products_mst")]
public class Product
{
[Key]
[Column("prd_id")] // 物理カラム名 "prd_id" にマッピング
public int Id { get; set; }
[Column("prd_name_vc")] // 物理カラム名 "prd_name_vc" にマッピング
public string Name { get; set; } = string.Empty;
[Column("prd_unit_price")] // 物理カラム名 "prd_unit_price" にマッピング
public decimal Price { get; set; }
// マッピング指定がない場合はプロパティ名がそのままカラム名になります
public DateTime CreatedAt { get; set; }
}
// 2. DbContextの定義
public class WarehouseContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 動作確認用にインメモリデータベースを使用
// ※実際のリレーショナルDB接続時にマッピング効果が発揮されます
optionsBuilder.UseInMemoryDatabase("WarehouseDb");
}
}
class Program
{
static void Main(string[] args)
{
// データの作成と保存
using (var context = new WarehouseContext())
{
var newProduct = new Product
{
Name = "High-Performance Gear",
Price = 2980m,
CreatedAt = DateTime.Now
};
context.Products.Add(newProduct);
context.SaveChanges();
Console.WriteLine("データを保存しました。");
}
// データの読み込み
using (var context = new WarehouseContext())
{
var product = context.Products.FirstOrDefault();
if (product != null)
{
Console.WriteLine("--- 取得データ ---");
// C#側では自然なプロパティ名でアクセスできます
Console.WriteLine($"ID : {product.Id}");
Console.WriteLine($"Name : {product.Name}");
Console.WriteLine($"Price : {product.Price:C}");
}
}
}
}
}
実行結果例
データを保存しました。
--- 取得データ ---
ID : 1
Name : High-Performance Gear
Price : \2,980
カスタムポイント
- スキーマの指定 OracleやSQL Serverなどでスキーマ(所有者)が分かれている場合、
[Table("TableName", Schema = "dbo")]のようにスキーマ名を指定できます。 - データ型の明示
[Column("price", TypeName = "decimal(18, 2)")]のように、データベース側の詳細な型定義を指定することも可能です。
注意点
- 既存DBとの整合性 Database First(既存DBからコード生成)ではなくCode Firstで開発する場合、指定したテーブル名・カラム名が実際のデータベースと完全に一致している必要があります。1文字でも異なると実行時エラーになります。
- 大文字・小文字の区別 使用するデータベース製品(PostgreSQLなど)によっては、引用符で囲まない限り大文字小文字を区別する、あるいは全て小文字として扱うなどの挙動が異なります。指定した文字列がそのままSQLに反映される点に注意してください。
- 予約語の回避 カラム名にSQLの予約語(
Order,User,Keyなど)を使用せざるを得ない場合、このマッピング機能を使って安全なプロパティ名に逃がすテクニックとしても有効です。
バリエーション
Fluent APIを使用する場合
クラス定義をシンプルに保ちたい場合や、外部ライブラリのクラスをエンティティとして使いたい場合は、DbContext の OnModelCreating メソッドで設定します。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.ToTable("tbl_products_mst", schema: "dbo");
modelBuilder.Entity<Product>()
.Property(p => p.Id)
.HasColumnName("prd_id");
}
まとめ
- クラス名・プロパティ名と、DBのテーブル名・カラム名が異なる場合は
[Table][Column]属性を使用します。 - レガシーデータベースの命名規約(スネークケースや短縮形)を、C#のモダンな命名規約(PascalCase)に変換する際に必須の技術です。
- データ型やスキーマも同時に指定できるため、厳密なデータベース設計に対応できます。
