Entity Framework Core
Entity Framework Core(EF Core)は「.NET向けの軽量・拡張可能・クロスプラットフォーム対応のオブジェクト関係マッピング(ORM)フレームワーク」として開発された、Microsoft公式のデータアクセステクノロジーです。.NET 5/6/7/8との完全統合により、LINQベースの直感的なクエリ記述、Code Firstアプローチによる自動スキーマ生成、包括的なマイグレーション機能を提供。SQL Server、MySQL、PostgreSQL、SQLite等の主要データベースをサポートし、現代的な.NET開発における包括的なデータ永続化ソリューションとして確固たる地位を築いています。
GitHub概要
dotnet/efcore
EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
トピックス
スター履歴
ライブラリ
Entity Framework Core
概要
Entity Framework Core(EF Core)は「.NET向けの軽量・拡張可能・クロスプラットフォーム対応のオブジェクト関係マッピング(ORM)フレームワーク」として開発された、Microsoft公式のデータアクセステクノロジーです。.NET 5/6/7/8との完全統合により、LINQベースの直感的なクエリ記述、Code Firstアプローチによる自動スキーマ生成、包括的なマイグレーション機能を提供。SQL Server、MySQL、PostgreSQL、SQLite等の主要データベースをサポートし、現代的な.NET開発における包括的なデータ永続化ソリューションとして確固たる地位を築いています。
詳細
Entity Framework Core 2025年版は、10年以上の進化により.NET エコシステムの中核データアクセス技術として成熟し続けています。LINQと完全統合された型安全なクエリシステムにより、SQLを直接記述することなく複雑なデータ操作を実現。Code Firstアプローチによる直感的なモデル定義から自動的なデータベーススキーマ生成、充実したマイグレーション機能により開発効率を大幅向上。ASP.NET Core、Blazor、.NET MAUIなどのモダンフレームワークとのシームレスな統合により、Web、デスクトップ、モバイルアプリケーション開発を包括的にサポートします。
主な特徴
- LINQ統合: C#のLINQを使用した型安全で直感的なクエリ記述
- Code Firstアプローチ: C#クラスからの自動データベーススキーマ生成
- 包括的マイグレーション: 自動・手動両対応の本格的スキーマ変更管理
- マルチDB対応: SQL Server、MySQL、PostgreSQL、SQLite等への統一API
- .NET統合: ASP.NET Core、Blazor等との完全統合とクロスプラットフォーム対応
- 高度な機能: 変更追跡、遅延読み込み、楽観的並行性制御サポート
メリット・デメリット
メリット
- Microsoft公式サポートによる高い信頼性と長期保守体制
- LINQとの完全統合による型安全で直感的なデータ操作
- Code Firstアプローチによる高い開発効率とモデル中心設計
- 包括的なマイグレーション機能による本格的なスキーマ管理
- .NETエコシステム全体との優れた統合性とクロスプラットフォーム対応
- 豊富なドキュメントと学習リソース、活発なコミュニティサポート
デメリット
- .NET環境に特化しており他プラットフォームでの利用不可
- 複雑なクエリでパフォーマンスが生SQLより劣る場合がある
- 抽象化層により詳細なSQL制御が制限される場合
- 大規模データ処理において他の専用ORMより重い場合
- LINQへの依存によりSQL知識なしでの高度な最適化が困難
- 自動生成コードへの依存によるブラックボックス化
参考ページ
書き方の例
基本セットアップ
# 新しい.NETプロジェクト作成
dotnet new web -n BlogApp
cd BlogApp
# Entity Framework Core パッケージ追加
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
# SQLiteを使用する場合
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
// Program.cs - .NET 6以降のスタイル
using Microsoft.EntityFrameworkCore;
using BlogApp.Data;
var builder = WebApplication.CreateBuilder(args);
// Entity Framework Coreの設定
builder.Services.AddDbContext<BlogContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// 開発時のサービス追加
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
// 開発環境でのマイグレーション自動適用
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<BlogContext>();
await context.Database.MigrateAsync();
}
app.Run();
// appsettings.json - 接続文字列設定
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=BlogAppDb;Trusted_Connection=true;MultipleActiveResultSets=true",
"SQLiteConnection": "Data Source=blog.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Information"
}
}
}
モデル定義と基本操作
// Models/Blog.cs - ブログエンティティ
using System.ComponentModel.DataAnnotations;
namespace BlogApp.Models;
public class Blog
{
public int BlogId { get; set; }
[Required]
[StringLength(200)]
public string Title { get; set; } = string.Empty;
[StringLength(1000)]
public string? Description { get; set; }
public string Url { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedDate { get; set; }
// ナビゲーションプロパティ
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}
// Models/Post.cs - 投稿エンティティ
public class Post
{
public int PostId { get; set; }
[Required]
[StringLength(300)]
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public DateTime PublishedDate { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedDate { get; set; }
public bool IsPublished { get; set; } = false;
// 外部キー
public int BlogId { get; set; }
// ナビゲーションプロパティ
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Comment> Comments { get; set; } = new List<Comment>();
public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}
// Models/Comment.cs - コメントエンティティ
public class Comment
{
public int CommentId { get; set; }
[Required]
public string Author { get; set; } = string.Empty;
[Required]
[EmailAddress]
public string Email { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
public bool IsApproved { get; set; } = false;
// 外部キー
public int PostId { get; set; }
// ナビゲーションプロパティ
public virtual Post Post { get; set; } = null!;
}
// Models/Tag.cs - タグエンティティ
public class Tag
{
public int TagId { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; } = string.Empty;
[StringLength(200)]
public string? Description { get; set; }
// 多対多のナビゲーションプロパティ
public virtual ICollection<Blog> Blogs { get; set; } = new List<Blog>();
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}
// Data/BlogContext.cs - データベースコンテキスト
using Microsoft.EntityFrameworkCore;
using BlogApp.Models;
namespace BlogApp.Data;
public class BlogContext : DbContext
{
public BlogContext(DbContextOptions<BlogContext> options) : base(options)
{
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// ブログ設定
modelBuilder.Entity<Blog>(entity =>
{
entity.HasKey(e => e.BlogId);
entity.Property(e => e.Title).IsRequired().HasMaxLength(200);
entity.Property(e => e.Url).IsRequired();
entity.HasIndex(e => e.Url).IsUnique();
});
// 投稿設定
modelBuilder.Entity<Post>(entity =>
{
entity.HasKey(e => e.PostId);
entity.Property(e => e.Title).IsRequired().HasMaxLength(300);
entity.Property(e => e.Content).HasColumnType("text");
// ブログとの一対多リレーション
entity.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
});
// コメント設定
modelBuilder.Entity<Comment>(entity =>
{
entity.HasKey(e => e.CommentId);
entity.Property(e => e.Author).IsRequired();
entity.Property(e => e.Email).IsRequired();
// 投稿との一対多リレーション
entity.HasOne(c => c.Post)
.WithMany(p => p.Comments)
.HasForeignKey(c => c.PostId)
.OnDelete(DeleteBehavior.Cascade);
});
// タグ設定
modelBuilder.Entity<Tag>(entity =>
{
entity.HasKey(e => e.TagId);
entity.Property(e => e.Name).IsRequired().HasMaxLength(50);
entity.HasIndex(e => e.Name).IsUnique();
});
// 多対多リレーション設定(Blog-Tag)
modelBuilder.Entity<Blog>()
.HasMany(b => b.Tags)
.WithMany(t => t.Blogs)
.UsingEntity<Dictionary<string, object>>(
"BlogTag",
j => j.HasOne<Tag>().WithMany().HasForeignKey("TagId"),
j => j.HasOne<Blog>().WithMany().HasForeignKey("BlogId"));
// 多対多リレーション設定(Post-Tag)
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(t => t.Posts)
.UsingEntity<Dictionary<string, object>>(
"PostTag",
j => j.HasOne<Tag>().WithMany().HasForeignKey("TagId"),
j => j.HasOne<Post>().WithMany().HasForeignKey("PostId"));
}
}
// Services/BlogService.cs - ビジネスロジック
public class BlogService
{
private readonly BlogContext _context;
public BlogService(BlogContext context)
{
_context = context;
}
// ブログ作成
public async Task<Blog> CreateBlogAsync(string title, string description, string url)
{
var blog = new Blog
{
Title = title,
Description = description,
Url = url,
CreatedDate = DateTime.UtcNow
};
_context.Blogs.Add(blog);
await _context.SaveChangesAsync();
return blog;
}
// ブログ取得(投稿含む)
public async Task<Blog?> GetBlogWithPostsAsync(int blogId)
{
return await _context.Blogs
.Include(b => b.Posts)
.ThenInclude(p => p.Comments)
.Include(b => b.Tags)
.FirstOrDefaultAsync(b => b.BlogId == blogId);
}
// 全ブログ取得
public async Task<List<Blog>> GetAllBlogsAsync()
{
return await _context.Blogs
.Include(b => b.Posts)
.OrderBy(b => b.Title)
.ToListAsync();
}
// ブログ更新
public async Task<Blog?> UpdateBlogAsync(int blogId, string title, string description)
{
var blog = await _context.Blogs.FindAsync(blogId);
if (blog == null) return null;
blog.Title = title;
blog.Description = description;
blog.UpdatedDate = DateTime.UtcNow;
await _context.SaveChangesAsync();
return blog;
}
// ブログ削除
public async Task<bool> DeleteBlogAsync(int blogId)
{
var blog = await _context.Blogs.FindAsync(blogId);
if (blog == null) return false;
_context.Blogs.Remove(blog);
await _context.SaveChangesAsync();
return true;
}
}
高度なクエリ操作
// Services/PostQueryService.cs - 高度なクエリ例
public class PostQueryService
{
private readonly BlogContext _context;
public PostQueryService(BlogContext context)
{
_context = context;
}
// 複雑な条件検索
public async Task<List<Post>> SearchPostsAsync(string? keyword, int? blogId, bool? isPublished, DateTime? fromDate, DateTime? toDate)
{
var query = _context.Posts
.Include(p => p.Blog)
.Include(p => p.Tags)
.AsQueryable();
// 動的条件追加
if (!string.IsNullOrWhiteSpace(keyword))
{
query = query.Where(p => p.Title.Contains(keyword) || p.Content.Contains(keyword));
}
if (blogId.HasValue)
{
query = query.Where(p => p.BlogId == blogId.Value);
}
if (isPublished.HasValue)
{
query = query.Where(p => p.IsPublished == isPublished.Value);
}
if (fromDate.HasValue)
{
query = query.Where(p => p.PublishedDate >= fromDate.Value);
}
if (toDate.HasValue)
{
query = query.Where(p => p.PublishedDate <= toDate.Value);
}
return await query
.OrderByDescending(p => p.PublishedDate)
.ToListAsync();
}
// 集計クエリ
public async Task<PostStatistics> GetPostStatisticsAsync()
{
var stats = await _context.Posts
.GroupBy(p => 1) // 全体をグループ化
.Select(g => new PostStatistics
{
TotalPosts = g.Count(),
PublishedPosts = g.Count(p => p.IsPublished),
DraftPosts = g.Count(p => !p.IsPublished),
TotalComments = g.Sum(p => p.Comments.Count),
AverageCommentsPerPost = g.Average(p => p.Comments.Count),
LastPostDate = g.Max(p => p.PublishedDate)
})
.FirstOrDefaultAsync();
return stats ?? new PostStatistics();
}
// 人気投稿取得(コメント数順)
public async Task<List<Post>> GetPopularPostsAsync(int count = 10)
{
return await _context.Posts
.Include(p => p.Blog)
.Include(p => p.Comments)
.Where(p => p.IsPublished)
.OrderByDescending(p => p.Comments.Count)
.ThenByDescending(p => p.PublishedDate)
.Take(count)
.ToListAsync();
}
// タグ別投稿取得
public async Task<List<Post>> GetPostsByTagAsync(string tagName)
{
return await _context.Posts
.Include(p => p.Blog)
.Include(p => p.Tags)
.Where(p => p.Tags.Any(t => t.Name == tagName) && p.IsPublished)
.OrderByDescending(p => p.PublishedDate)
.ToListAsync();
}
// ページング対応の投稿取得
public async Task<PagedResult<Post>> GetPagedPostsAsync(int pageNumber, int pageSize, int? blogId = null)
{
var query = _context.Posts
.Include(p => p.Blog)
.Include(p => p.Tags)
.AsQueryable();
if (blogId.HasValue)
{
query = query.Where(p => p.BlogId == blogId.Value);
}
var totalCount = await query.CountAsync();
var posts = await query
.Where(p => p.IsPublished)
.OrderByDescending(p => p.PublishedDate)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
return new PagedResult<Post>
{
Items = posts,
TotalCount = totalCount,
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
};
}
// 生SQLクエリ使用例
public async Task<List<BlogStatistics>> GetBlogStatisticsRawSqlAsync()
{
return await _context.Database
.SqlQueryRaw<BlogStatistics>(@"
SELECT
b.BlogId,
b.Title,
COUNT(p.PostId) as PostCount,
COUNT(CASE WHEN p.IsPublished = 1 THEN 1 END) as PublishedPostCount,
COALESCE(SUM(c.CommentCount), 0) as TotalComments
FROM Blogs b
LEFT JOIN Posts p ON b.BlogId = p.BlogId
LEFT JOIN (
SELECT PostId, COUNT(*) as CommentCount
FROM Comments
GROUP BY PostId
) c ON p.PostId = c.PostId
GROUP BY b.BlogId, b.Title
ORDER BY PostCount DESC")
.ToListAsync();
}
}
// DTOs/PagedResult.cs - ページング結果
public class PagedResult<T>
{
public List<T> Items { get; set; } = new();
public int TotalCount { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
public int TotalPages { get; set; }
public bool HasPreviousPage => PageNumber > 1;
public bool HasNextPage => PageNumber < TotalPages;
}
// DTOs/PostStatistics.cs - 投稿統計
public class PostStatistics
{
public int TotalPosts { get; set; }
public int PublishedPosts { get; set; }
public int DraftPosts { get; set; }
public int TotalComments { get; set; }
public double AverageCommentsPerPost { get; set; }
public DateTime LastPostDate { get; set; }
}
// DTOs/BlogStatistics.cs - ブログ統計
public class BlogStatistics
{
public int BlogId { get; set; }
public string Title { get; set; } = string.Empty;
public int PostCount { get; set; }
public int PublishedPostCount { get; set; }
public int TotalComments { get; set; }
}
リレーション操作
// Services/RelationshipService.cs - リレーション操作の例
public class RelationshipService
{
private readonly BlogContext _context;
public RelationshipService(BlogContext context)
{
_context = context;
}
// 投稿作成とタグ関連付け
public async Task<Post> CreatePostWithTagsAsync(int blogId, string title, string content, List<string> tagNames)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// ブログ存在確認
var blog = await _context.Blogs.FindAsync(blogId);
if (blog == null)
throw new ArgumentException("Blog not found", nameof(blogId));
// 投稿作成
var post = new Post
{
Title = title,
Content = content,
BlogId = blogId,
PublishedDate = DateTime.UtcNow,
IsPublished = true
};
_context.Posts.Add(post);
await _context.SaveChangesAsync(); // IDを取得するために保存
// タグ処理
foreach (var tagName in tagNames.Distinct())
{
var tag = await _context.Tags
.FirstOrDefaultAsync(t => t.Name == tagName);
if (tag == null)
{
// 新しいタグ作成
tag = new Tag { Name = tagName };
_context.Tags.Add(tag);
await _context.SaveChangesAsync();
}
// 投稿とタグを関連付け
post.Tags.Add(tag);
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return post;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
// コメント追加
public async Task<Comment> AddCommentToPostAsync(int postId, string author, string email, string content)
{
var post = await _context.Posts.FindAsync(postId);
if (post == null)
throw new ArgumentException("Post not found", nameof(postId));
var comment = new Comment
{
PostId = postId,
Author = author,
Email = email,
Content = content,
CreatedDate = DateTime.UtcNow,
IsApproved = false // 管理者承認待ち
};
_context.Comments.Add(comment);
await _context.SaveChangesAsync();
return comment;
}
// ブログ間での投稿移動
public async Task<bool> MovePostToBlogAsync(int postId, int newBlogId)
{
var post = await _context.Posts.FindAsync(postId);
var newBlog = await _context.Blogs.FindAsync(newBlogId);
if (post == null || newBlog == null)
return false;
post.BlogId = newBlogId;
post.UpdatedDate = DateTime.UtcNow;
await _context.SaveChangesAsync();
return true;
}
// 投稿からタグ削除
public async Task<bool> RemoveTagFromPostAsync(int postId, int tagId)
{
var post = await _context.Posts
.Include(p => p.Tags)
.FirstOrDefaultAsync(p => p.PostId == postId);
if (post == null) return false;
var tag = post.Tags.FirstOrDefault(t => t.TagId == tagId);
if (tag == null) return false;
post.Tags.Remove(tag);
await _context.SaveChangesAsync();
return true;
}
// ブログに複数投稿一括追加
public async Task<List<Post>> AddMultiplePostsToBlogAsync(int blogId, List<(string Title, string Content)> posts)
{
var blog = await _context.Blogs.FindAsync(blogId);
if (blog == null)
throw new ArgumentException("Blog not found", nameof(blogId));
var newPosts = posts.Select(p => new Post
{
Title = p.Title,
Content = p.Content,
BlogId = blogId,
PublishedDate = DateTime.UtcNow,
IsPublished = false // ドラフトとして作成
}).ToList();
_context.Posts.AddRange(newPosts);
await _context.SaveChangesAsync();
return newPosts;
}
// 関連データの一括削除
public async Task<bool> DeleteBlogWithAllRelatedDataAsync(int blogId)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
var blog = await _context.Blogs
.Include(b => b.Posts)
.ThenInclude(p => p.Comments)
.Include(b => b.Posts)
.ThenInclude(p => p.Tags)
.FirstOrDefaultAsync(b => b.BlogId == blogId);
if (blog == null) return false;
// カスケード削除が設定されているため、ブログを削除すれば
// 関連する投稿とコメントも自動削除される
_context.Blogs.Remove(blog);
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
実用例
// Controllers/BlogController.cs - ASP.NET Core コントローラー例
using Microsoft.AspNetCore.Mvc;
using BlogApp.Services;
using BlogApp.Models;
[ApiController]
[Route("api/[controller]")]
public class BlogController : ControllerBase
{
private readonly BlogService _blogService;
private readonly PostQueryService _postQueryService;
private readonly RelationshipService _relationshipService;
public BlogController(
BlogService blogService,
PostQueryService postQueryService,
RelationshipService relationshipService)
{
_blogService = blogService;
_postQueryService = postQueryService;
_relationshipService = relationshipService;
}
// 全ブログ取得
[HttpGet]
public async Task<ActionResult<List<Blog>>> GetBlogs()
{
var blogs = await _blogService.GetAllBlogsAsync();
return Ok(blogs);
}
// ブログ詳細取得
[HttpGet("{id}")]
public async Task<ActionResult<Blog>> GetBlog(int id)
{
var blog = await _blogService.GetBlogWithPostsAsync(id);
if (blog == null)
return NotFound();
return Ok(blog);
}
// ブログ作成
[HttpPost]
public async Task<ActionResult<Blog>> CreateBlog([FromBody] CreateBlogRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
var blog = await _blogService.CreateBlogAsync(
request.Title,
request.Description,
request.Url);
return CreatedAtAction(nameof(GetBlog), new { id = blog.BlogId }, blog);
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
// ブログ更新
[HttpPut("{id}")]
public async Task<ActionResult<Blog>> UpdateBlog(int id, [FromBody] UpdateBlogRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var blog = await _blogService.UpdateBlogAsync(id, request.Title, request.Description);
if (blog == null)
return NotFound();
return Ok(blog);
}
// ブログ削除
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteBlog(int id)
{
var result = await _relationshipService.DeleteBlogWithAllRelatedDataAsync(id);
if (!result)
return NotFound();
return NoContent();
}
// ブログの投稿検索
[HttpGet("{id}/posts")]
public async Task<ActionResult<PagedResult<Post>>> GetBlogPosts(
int id,
[FromQuery] int page = 1,
[FromQuery] int size = 10,
[FromQuery] string? keyword = null)
{
var result = await _postQueryService.GetPagedPostsAsync(page, size, id);
return Ok(result);
}
// 投稿作成
[HttpPost("{id}/posts")]
public async Task<ActionResult<Post>> CreatePost(int id, [FromBody] CreatePostRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
var post = await _relationshipService.CreatePostWithTagsAsync(
id,
request.Title,
request.Content,
request.Tags ?? new List<string>());
return CreatedAtAction("GetPost", "Post", new { id = post.PostId }, post);
}
catch (ArgumentException ex)
{
return BadRequest(new { message = ex.Message });
}
}
}
// DTOs/Requests.cs - リクエストDTO
public class CreateBlogRequest
{
[Required]
[StringLength(200)]
public string Title { get; set; } = string.Empty;
[StringLength(1000)]
public string? Description { get; set; }
[Required]
[Url]
public string Url { get; set; } = string.Empty;
}
public class UpdateBlogRequest
{
[Required]
[StringLength(200)]
public string Title { get; set; } = string.Empty;
[StringLength(1000)]
public string? Description { get; set; }
}
public class CreatePostRequest
{
[Required]
[StringLength(300)]
public string Title { get; set; } = string.Empty;
[Required]
public string Content { get; set; } = string.Empty;
public List<string>? Tags { get; set; }
}
// マイグレーション管理コマンド例
/*
# 初期マイグレーション作成
dotnet ef migrations add InitialCreate
# データベース更新
dotnet ef database update
# 新しいマイグレーション追加
dotnet ef migrations add AddTagsAndComments
# マイグレーション一覧表示
dotnet ef migrations list
# マイグレーション削除(最新のみ)
dotnet ef migrations remove
# 特定マイグレーションまで戻す
dotnet ef database update AddTagsAndComments
# スクリプト生成(本番用)
dotnet ef migrations script --output migration.sql
# バンドル作成(実行可能ファイル)
dotnet ef migrations bundle --output efbundle.exe
*/
// Startup.cs または Program.cs での高度な設定例
public void ConfigureServices(IServiceCollection services)
{
// Entity Framework Core 設定
services.AddDbContext<BlogContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlOptions =>
{
// 接続タイムアウト設定
sqlOptions.CommandTimeout(30);
// マイグレーションアセンブリ指定
sqlOptions.MigrationsAssembly("BlogApp.Migrations");
// リトライ設定
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(5),
errorNumbersToAdd: null);
});
// 開発環境での詳細ログ
if (Environment.IsDevelopment())
{
options.EnableSensitiveDataLogging();
options.EnableDetailedErrors();
}
// 変更追跡の最適化
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
// サービス登録
services.AddScoped<BlogService>();
services.AddScoped<PostQueryService>();
services.AddScoped<RelationshipService>();
}