エンティティ フレームワーク コア#
1. ナビゲーション プロパティ#
// A |->* B
public class A{
//...
public List<B> Bs{get;set;}
}
public class B{
// ...
public B B{set;get;}
}
ナビゲーション プロパティは、任意のリスト型であることができます。
2. データベース コンテキスト ⚠️#
EF とデータベースを調整する管理クラス。#
- コンテキストは、DbContext を継承した非抽象クラスです。このクラスの機能は、まずすべてのエンティティのコレクションを公開することであり、これらのコレクションを操作することでストレージからデータを読み取ったり保存したりできます。さらに、コンテキストはデータを保存するモデルを保持します。使用する前に、各エンティティのモデルとその他の必要な設定を正しく構成する必要があります。
- コンテキストのもう一つの重要な機能は、エンティティの変更を追跡することです。これにより、変更を保存する際に、どのように処理すべきかを知ることができます。
- コンテキストが追跡する各エンティティは、次のいずれかの状態にあります:未変更 /added、変更済み /modified、追加済み /added、削除済み /deletedまたは切り離し /detached。コンテキストはサンドボックスとして考えることができ、エンティティ コレクションに変更を加えた後、1 回の保存操作でこれらの変更を適用できます。
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
// エンティティセット DbSet はデータベース テーブルに対応
// Courses テーブルには下記の 2 つのテーブルが含まれているため、下方の 2 つのテーブルを記述しなくても、EF は自動的に含めます
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
// デフォルトの動作をオーバーライドし、テーブル名を設定
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
DbContext フック メソッド#
- OnConfiguring メソッド:コンテキストがプロバイダーと接続文字列を構成する必要があるときに、このメソッドが呼び出され、開発者に介入の入口を提供します。
- OnModelCreating メソッド:EF Core がデータ モデルを組み立てる際に自動的に呼び出され、このメソッド内でモデルを構築するコードを追加します。
- SaveChanges メソッド:変更をデータベースに保存したいときに呼び出します。保存操作の影響を受けたレコード数を返します。
データベース プロバイダー / データベース提供プログラム#
- EF Core はデータベースに依存しません。したがって、異なるデータベースには特定のインターフェイスを提供する必要があります。
- 関連するデータベース プロバイダー パッケージを導入し、コンテキストの OnConfiguring に対応する構成メソッド(UseSqlServer)を追加します。
3. サービス登録#
// Startup.cs
/* Using */
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
/* Start up */
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(
options=>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews();
// 開発環境の例外フィルタ機能
services.AddDatabaseDeveloperPageExceptionFilter();
}
// appsetting.json
{
"ConnectionStrings": {
"DefaultConnection": "データベース接続文字列"
},
}
4. データ モデルの作成#
-
データ モデルを作成するには、エンティティ Entity タイプが必要です。エンティティ タイプは、一連の C# クラスであり、通常はデータベースに対応します。
-
エンティティ タイプ と データベース エンティティ との関係を構成します。2 つの方法があります:「フルエント API」 と 「データ注釈」
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; // 対応するテーブル名と対応するデータベース スキーマ名を構成 [Table("blogs",Schema="dbo")] public class Blog{ [Key] // 主キー、通常 efcore は自動的に識別します public int BlogId{set;get;} [MaxLength(400)] // 最大長 public string Url{set;get;} [NotMapped] // マッピングを除外 public DataTime LoadedFromDatabase {set;get;} [Column("rating",TypeName="decimal(5,2)")] // 列名と型をマッピング public decimal Rating {set;get;} } // フルエント API を使用してデフォルト スキーマ名を構成 protected override void OnModelCreating(ModelBuilder modelBuilder){ modelBuilder.HasDefaultSchema("dbo"); }
「エンティティ参照 / 参照ナビゲーション」構成 一対一#
// 一つの Blog に対して一つの Customer
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<BlogImage> BlogImages { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Blog と BlogImage の一対一関係を設定
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(b => b.BlogForeignKey);
}
}
public class Blog
{
public Blog()
{
Posts = new HashSet<Post>();
}
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public int? ImageId { get; set; }
// 参照ナビゲーション efcore は自動的に構成できます。書き方は公式ドキュメントを参照
public BlogImage BlogImage { get; set; }
public HashSet<Post> Posts { get; set; }
}
public class BlogImage
{
public int BlogImageId { get; set; }
public string ImageUrl { get; set; }
public string Caption { get; set; }
}
「エンティティコレクション / コレクションナビゲーション」構成 :一対多#
// エンティティコレクションプロパティの構成
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Blog の一対多関係を構成
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne(p => p.Blog)
.HasForeignKey(p => p.BlogId);
}
}
public class Blog
{
public Blog()
{
// HashSet ナビゲーションがあるため、コンストラクターにこの内容を追加する必要があります
Posts = new HashSet<Post>();
}
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public int? ImageId { get; set; }
public BlogImage BlogImage { get; set; }
// コレクションナビゲーション、一対多関係を示し、HashSet が必要です
public HashSet<Post> Posts { get; set; }
}
public class Post
{
[Key]
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
まとめ#
データ モデルの役割は、データ構造を説明するだけでなく、データ関係を説明することにもあります。関係はデータベースの核心でもあります。
EF Core は、モデル関係を記述するためのさまざまな方法をサポートしており、注釈を使用することが最も直感的で簡単な方法です。特殊な関係にはフルエント API を使用する必要があります。
ただし、どちらも ModelBuilder 内で関連する動作を構成します。
5. モデルからデータベースへのマイグレーション#
マイグレーションは、EF Core マイグレーション ツールを使用してエンティティ モデルからデータベースを作成および管理します。
マイグレーションプロセス#
-
上記の手順に従って、エンティティ モデルと関係を構築し、 のクラスを作成します。通常、これは意図するデータベース名 + Context です。このクラスは DbContext を継承します。EF Core は、このファイル内のエンティティ モデルと関係の説明に基づいてデータベースを生成します。
-
ターミナルを開きます。
-
まず、.NET Core CLI ツールがインストールされていることを確認します。
# グローバルに dotnet-ef ツールをインストール dotnet tool install --global dotnet-ef # 更新 dotnet tool update --global dotnet-ef # プロジェクトに EFCoreDesign パッケージを追加 dotnet add package Microsoft.EntityFrameworkCore.Design # Nuget マネージャーを使用することもできます # ツールのインストール状況を確認 dotnet ef
⚠️ EF Core ツールのリファレンス (.NET CLI) - EF Core | Microsoft Learn ツールの詳細な使用方法はここにあります。
-
プロジェクト ディレクトリでターミナルを開きます:
# マイグレーションを作成 dotnet ef migrations add InitialCreate ## このコマンドは、マイグレーションを記録するためのマイグレーション フォルダーを生成します。また、その内容を直接変更することもできます。 # データベース スキーマを作成 dotnet ef database update
⚠️ 検証の問題については、接続文字列に
TrustServerCertificate=true;Encrypt=true
を追加できます。 -
完了。
モデルの進化#
エンティティ モデルが変更された場合、データベースを再度更新する必要があります。
まず、マイグレーション ツールが生成する migrations/ シリーズのファイルは、マイグレーション記録であり、データベース構造の進化の完全な説明です。それには、データベース構造のスナップショットとマイグレーションファイルが含まれています。
したがって、モデルが変更された場合、migrations/ ファイルを手動で変更し、再度 dotnet ef database update
を実行してマイグレーションを適用できます。
データベースの進化の記録を保持するために、次のプロセスを使用します:
-
新しいマイグレーションを作成します。
# ここでは意味のあるマイグレーション名を使用することをお勧めします dotnet ef migrations add AddSomeTable
-
マイグレーションを適用
dotnet ef database update
説明#
migrations/ フォルダーを読むと、各マイグレーションの実行アクションを示すタイムスタンプとマイグレーション名を持つ一連の .cs
ファイルがあります。エンティティ モデルが変更された場合、EF Core は自動的に比較して増分更新を行い、データベースを再生成することはありません。
もう一つのファイルは、データベーススナップショットの .cs
ファイルであり、そこには実際のデータベース テーブル エンティティ モデルが含まれています。これは、前回の更新時に存在したデータベーススナップショットです。
これらのファイルはすべて C# ファイルであり、バージョン管理に追加して、プロジェクト開発プロセスにおけるデータベースの進化を記録できます。
6. その他#
除外タイプ#
現在のコンテキストが他のコンテキスト内の型を参照している場合、マイグレーション時に衝突が発生します(参照されているオブジェクトが現在のコンテキストに存在しないため、エラーが発生するのは正常です)。この場合、コンテキストからその型を除外する必要があります。
// これは参照コンテキスト クラスです
// モデル作成フック内で、存在しない型を除外します。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUser>()
.ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}
列の名前変更#
EF Core は列の名前変更や削除を区別できないため、列の名前を変更すると、EF Core は 削除 - 作成 の操作を生成します。これにより、データが失われる可能性があります。手動で変更する必要があります。
// EF Core が生成したマイグレーション アクション
migrationBuilder.DropColumn(
name: "Name",
table: "Customers");
migrationBuilder.AddColumn<string>(
name: "FullName",
table: "Customers",
nullable: true);
// これを次のように変更する必要があります
migrationBuilder.RenameColumn(
name: "Name",
table: "Customers",
newName: "FullName");
マイグレーションに SQL アクションを導入#
エンティティ内の元の Firstname と LastName を結合して FullName に再保存し、最初の 2 つの列を削除する場合を考えます。
EF Core はこのアクションを自動的に認識しないため、マイグレーション用に SQL 文を手動で導入する必要があります。
migrationBuilder.AddColumn<string>(
name: "FullName",
table: "Customer",
nullable: true);
migrationBuilder.Sql(
@"
UPDATE Customer
SET FullName = FirstName + ' ' + LastName;
");
migrationBuilder.DropColumn(
name: "FirstName",
table: "Customer");
migrationBuilder.DropColumn(
name: "LastName",
table: "Customer");
⚠️ このプロセスは順序があります。
古いデータを使用してマイグレーションを行う場合は、SQL を使用できます。
カスタム SQL マイグレーション#
モデルを変更せずに空のマイグレーションを生成し、手動で SQL 文を埋め込むことができます。
SQL は、モデルが知らないデータベース オブジェクトを操作できます。
migrationBuilder.Sql(
@"
EXEC ('CREATE PROCEDURE getFullName
@LastName nvarchar(50),
@FirstName nvarchar(50)
AS
RETURN @LastName + @FirstName;')");
⚠️ ある文が SQL バッチ内の最初または唯一の文である必要がある場合は、EXEC
を使用してください。これにより、幂等マイグレーション スクリプト内の解析エラーを解決できます。現在、テーブルに参照されている列が存在しない場合、このようなエラーが発生する可能性があります。
⚠️ 通常、EF Core はトランザクションを使用してマイグレーション操作を実行します。特定のデータベースがこの操作をサポートしていない場合は、migrationBuilder.Sql
に suppressTransaction: true
パラメータを渡してトランザクションを使用しないことを選択できます。
7. マイグレーション管理#
マイグレーションの削除#
dotnet ef migrations remove
を使用して 最後のマイグレーション を削除します。
⚠️ 適用されたマイグレーションを削除しないでください!
マイグレーションの一覧表示#
dotnet ef migrations list
で、すべてのマイグレーションを一覧表示します。
すべてをリセット#
マイグレーション フォルダーを直接削除することで、すべてのマイグレーション記録を削除できます。詳細は次のとおりです:
マイグレーションの管理 - EF Core | Microsoft Learn
マイグレーションの適用#
-
SQL スクリプトを生成し、ゼロからデータベースを生成します。これは、プロジェクト モデルを本番環境にデプロイするためのベスト プラクティスです。
dotnet ef migrations script [from] [to]
From と to はそれぞれマイグレーション バージョン名です。to はデフォルトで最新です。上記のコードを使用してロールバックできます。
-
SQL スクリプトを自動的に比較します。既存のデータベースにマイグレーションを適用するが、現在のバージョンが不明な場合、EF Core はマイグレーション履歴を取得し、自動的に更新します。
dotnet ef migrations script --idempotent
-
コマンドライン ツールを使用してデータベースをアップグレードします。これは、開発環境やテスト環境のベスト プラクティスです。
dotnet ef database update [to]
-
プログラムの実行時にマイグレーションを実行します。これは、ローカル開発テストのベスト プラクティスであり、毎回手動でマイグレーションを行う手間を省くことができます。
// .net のメイン関数内に追加 var host = CreateHostBuilder(args).Build(); using(var scope = host.Services.CreateScope()){ var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); younameContext.Database.Migrate(); // これが重要です。 } host.Run();
-
バンドルを作成します。
チーム管理#
⏰