概要
Entity Framework Core(EF Core)を使用してエンティティを設計する際、クラス内の特定のプロパティをデータベースのテーブル列として作成したくない場合があります。 本レシピでは、データ注釈(Data Annotations)の [NotMapped] 属性を使用し、一時的な計算結果やUI表示用のフラグなどをデータベース管理の対象外にする方法を解説します。
仕様(入出力)
- 入力: エンティティクラスに定義されたプロパティ。
- 処理: 対象プロパティに
[NotMapped]属性を付与する。 - 出力: データベースへの保存(INSERT/UPDATE)および読み込み(SELECT)時に、該当プロパティが無視されること。
基本の使い方
System.ComponentModel.DataAnnotations.Schema 名前空間を利用し、対象のプロパティに属性を付与します。
[NotMapped]
public string TemporaryMessage { get; set; }
コード全文
以下のコードは、EF Core の InMemory データベースを使用して動作確認を行う完全なコンソールアプリケーションの例です。 データベースに保存しても EstimatedProcessTime の値は永続化されず、再読み込み時には初期値に戻ることを確認できます。
※ 実行には 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 NotMappedSample
{
// 1. エンティティクラスの定義
public class ProductionOrder
{
[Key]
public int OrderId { get; set; }
public string ProductName { get; set; } = string.Empty;
public int Quantity { get; set; }
// このプロパティはデータベース上のテーブルには作成されません
// アプリケーション内での一時的な計算や表示用に使用します
[NotMapped]
public TimeSpan EstimatedProcessTime { get; set; }
}
// 2. DbContextの定義
public class FactoryContext : DbContext
{
public DbSet<ProductionOrder> Orders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 動作確認用にインメモリデータベースを使用
optionsBuilder.UseInMemoryDatabase("FactoryDb");
}
}
class Program
{
static void Main(string[] args)
{
// データの保存
using (var context = new FactoryContext())
{
var newOrder = new ProductionOrder
{
ProductName = "Widget-X",
Quantity = 100,
// メモリ上では値を設定する
EstimatedProcessTime = TimeSpan.FromHours(2.5)
};
context.Orders.Add(newOrder);
context.SaveChanges();
Console.WriteLine($"[保存時] ID: {newOrder.OrderId}, EstimatedProcessTime: {newOrder.EstimatedProcessTime}");
}
// データの読み込み
using (var context = new FactoryContext())
{
var savedOrder = context.Orders.FirstOrDefault();
if (savedOrder != null)
{
Console.WriteLine("--- データベースから再読み込み ---");
Console.WriteLine($"[読込時] ID: {savedOrder.OrderId}, Name: {savedOrder.ProductName}");
// NotMapped属性があるため、DBには保存されておらず初期値(TimeSpan.Zero)に戻る
Console.WriteLine($"[読込時] EstimatedProcessTime: {savedOrder.EstimatedProcessTime}");
}
}
}
}
}
実行結果例
[保存時] ID: 1, EstimatedProcessTime: 02:30:00
--- データベースから再読み込み ---
[読込時] ID: 1, Name: Widget-X
[読込時] EstimatedProcessTime: 00:00:00
カスタムポイント
- 計算プロパティへの利用 Getterのみのプロパティ(
public int Total => Price * Count;)は、EF Coreによって自動的に無視されることが多いですが、Setterが存在する場合や明示的に除外したい場合に[NotMapped]が有効です。 - UI用状態フラグ WPFやBlazorなどのクライアントサイドで、行の選択状態(
IsSelected)や展開状態(IsExpanded)をエンティティに持たせる場合に最適です。
注意点
- LINQクエリでの使用不可
[NotMapped]が付与されたプロパティをWhere句などのLINQ to Entities クエリで使用すると、SQLに変換できず実行時例外が発生します。クエリは必ずデータベースに存在するカラムで行ってください。 - 値の消失 解説通り、コンテキストが破棄され再読み込みされると値は失われます。永続化が必要なデータには絶対に使用しないでください。
- マイグレーションへの影響 既にデータベースにカラムが存在する状態で後からこの属性を追加した場合、マイグレーションを実行するとそのカラムを削除するSQLが生成されます(データが消えるため注意が必要です)。
バリエーション
Fluent APIを使用する場合
エンティティクラスを汚したくない(POCOを保ちたい)場合は、属性ではなく OnModelCreating メソッドで設定可能です。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProductionOrder>()
.Ignore(p => p.EstimatedProcessTime);
}
まとめ
LINQクエリの検索条件には使用できない点に注意して設計を行ってください。
データベーステーブルのカラムと一致させたくないプロパティには [NotMapped] を付与します。
一時的な計算値、UIの状態管理、ログ出力用の一時データなどに適しています。
