Entity Framework Core
Entity Framework Core (EF Core) is developed as "a lightweight, extensible, and cross-platform Object-Relational Mapping (ORM) framework for .NET" and serves as Microsoft's official data access technology. Through complete integration with .NET 5/6/7/8, it provides intuitive query writing based on LINQ, automatic schema generation through Code First approach, and comprehensive migration functionality. Supporting major databases including SQL Server, MySQL, PostgreSQL, SQLite, it has established itself as a comprehensive data persistence solution for modern .NET development.
GitHub Overview
dotnet/efcore
EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
Topics
Star History
Library
Entity Framework Core
Overview
Entity Framework Core (EF Core) is developed as "a lightweight, extensible, and cross-platform Object-Relational Mapping (ORM) framework for .NET" and serves as Microsoft's official data access technology. Through complete integration with .NET 5/6/7/8, it provides intuitive query writing based on LINQ, automatic schema generation through Code First approach, and comprehensive migration functionality. Supporting major databases including SQL Server, MySQL, PostgreSQL, SQLite, it has established itself as a comprehensive data persistence solution for modern .NET development.
Details
Entity Framework Core 2025 edition continues to mature as the core data access technology in the .NET ecosystem through over 10 years of evolution. Through a type-safe query system fully integrated with LINQ, it enables complex data operations without directly writing SQL. Automatic database schema generation from intuitive model definitions through Code First approach and rich migration functionality significantly improve development efficiency. Seamless integration with modern frameworks like ASP.NET Core, Blazor, and .NET MAUI provides comprehensive support for web, desktop, and mobile application development.
Key Features
- LINQ Integration: Type-safe and intuitive query writing using C# LINQ
- Code First Approach: Automatic database schema generation from C# classes
- Comprehensive Migration: Professional schema change management supporting both automatic and manual approaches
- Multi-Database Support: Unified API for SQL Server, MySQL, PostgreSQL, SQLite, etc.
- .NET Integration: Complete integration with ASP.NET Core, Blazor, and cross-platform support
- Advanced Features: Change tracking, lazy loading, optimistic concurrency control support
Pros and Cons
Pros
- High reliability and long-term maintenance system through official Microsoft support
- Type-safe and intuitive data operations through complete LINQ integration
- High development efficiency and model-centric design through Code First approach
- Professional schema management through comprehensive migration functionality
- Excellent integration with the entire .NET ecosystem and cross-platform support
- Rich documentation and learning resources, active community support
Cons
- Specialized for .NET environment, cannot be used on other platforms
- Performance may be inferior to raw SQL for complex queries
- Abstraction layer may limit detailed SQL control in some cases
- May be heavier than other specialized ORMs for large-scale data processing
- Difficulty in advanced optimization without SQL knowledge due to LINQ dependency
- Black-boxing due to dependency on auto-generated code
Reference Pages
- Entity Framework Core Official Site
- Entity Framework Core Documentation
- Entity Framework Core GitHub Repository
Code Examples
Basic Setup
# Create new .NET project
dotnet new web -n BlogApp
cd BlogApp
# Add Entity Framework Core packages
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
# For SQLite usage
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
// Program.cs - .NET 6+ style
using Microsoft.EntityFrameworkCore;
using BlogApp.Data;
var builder = WebApplication.CreateBuilder(args);
// Entity Framework Core configuration
builder.Services.AddDbContext<BlogContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Add development services
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
// Auto-apply migrations in development environment
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<BlogContext>();
await context.Database.MigrateAsync();
}
app.Run();
// appsettings.json - Connection string configuration
{
"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"
}
}
}
Model Definition and Basic Operations
// Models/Blog.cs - Blog entity
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; }
// Navigation properties
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}
// Models/Post.cs - Post entity
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;
// Foreign key
public int BlogId { get; set; }
// Navigation properties
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 - Comment entity
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;
// Foreign key
public int PostId { get; set; }
// Navigation property
public virtual Post Post { get; set; } = null!;
}
// Models/Tag.cs - Tag entity
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; }
// Many-to-many navigation properties
public virtual ICollection<Blog> Blogs { get; set; } = new List<Blog>();
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}
// Data/BlogContext.cs - Database context
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);
// Blog configuration
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();
});
// Post configuration
modelBuilder.Entity<Post>(entity =>
{
entity.HasKey(e => e.PostId);
entity.Property(e => e.Title).IsRequired().HasMaxLength(300);
entity.Property(e => e.Content).HasColumnType("text");
// One-to-many relationship with Blog
entity.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
});
// Comment configuration
modelBuilder.Entity<Comment>(entity =>
{
entity.HasKey(e => e.CommentId);
entity.Property(e => e.Author).IsRequired();
entity.Property(e => e.Email).IsRequired();
// One-to-many relationship with Post
entity.HasOne(c => c.Post)
.WithMany(p => p.Comments)
.HasForeignKey(c => c.PostId)
.OnDelete(DeleteBehavior.Cascade);
});
// Tag configuration
modelBuilder.Entity<Tag>(entity =>
{
entity.HasKey(e => e.TagId);
entity.Property(e => e.Name).IsRequired().HasMaxLength(50);
entity.HasIndex(e => e.Name).IsUnique();
});
// Many-to-many relationship configuration (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"));
// Many-to-many relationship configuration (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 - Business logic
public class BlogService
{
private readonly BlogContext _context;
public BlogService(BlogContext context)
{
_context = context;
}
// Create blog
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;
}
// Get blog with posts
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);
}
// Get all blogs
public async Task<List<Blog>> GetAllBlogsAsync()
{
return await _context.Blogs
.Include(b => b.Posts)
.OrderBy(b => b.Title)
.ToListAsync();
}
// Update blog
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;
}
// Delete 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;
}
}
Advanced Query Operations
// Services/PostQueryService.cs - Advanced query examples
public class PostQueryService
{
private readonly BlogContext _context;
public PostQueryService(BlogContext context)
{
_context = context;
}
// Complex conditional search
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();
// Add dynamic conditions
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();
}
// Aggregation queries
public async Task<PostStatistics> GetPostStatisticsAsync()
{
var stats = await _context.Posts
.GroupBy(p => 1) // Group entire dataset
.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();
}
// Get popular posts (ordered by comment count)
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();
}
// Get posts by tag
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();
}
// Get posts with pagination
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)
};
}
// Raw SQL query example
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 - Pagination result
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 - Post statistics
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 - Blog statistics
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; }
}
Relationship Operations
// Services/RelationshipService.cs - Relationship operation examples
public class RelationshipService
{
private readonly BlogContext _context;
public RelationshipService(BlogContext context)
{
_context = context;
}
// Create post with tag associations
public async Task<Post> CreatePostWithTagsAsync(int blogId, string title, string content, List<string> tagNames)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// Check blog existence
var blog = await _context.Blogs.FindAsync(blogId);
if (blog == null)
throw new ArgumentException("Blog not found", nameof(blogId));
// Create post
var post = new Post
{
Title = title,
Content = content,
BlogId = blogId,
PublishedDate = DateTime.UtcNow,
IsPublished = true
};
_context.Posts.Add(post);
await _context.SaveChangesAsync(); // Save to get ID
// Process tags
foreach (var tagName in tagNames.Distinct())
{
var tag = await _context.Tags
.FirstOrDefaultAsync(t => t.Name == tagName);
if (tag == null)
{
// Create new tag
tag = new Tag { Name = tagName };
_context.Tags.Add(tag);
await _context.SaveChangesAsync();
}
// Associate post with tag
post.Tags.Add(tag);
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return post;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
// Add comment
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 // Awaiting admin approval
};
_context.Comments.Add(comment);
await _context.SaveChangesAsync();
return comment;
}
// Move post between blogs
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;
}
// Remove tag from post
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;
}
// Add multiple posts to blog in batch
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 // Create as draft
}).ToList();
_context.Posts.AddRange(newPosts);
await _context.SaveChangesAsync();
return newPosts;
}
// Delete blog with all related data
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;
// Cascade delete is configured, so deleting blog
// will automatically delete related posts and comments
_context.Blogs.Remove(blog);
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
Practical Examples
// Controllers/BlogController.cs - ASP.NET Core controller example
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;
}
// Get all blogs
[HttpGet]
public async Task<ActionResult<List<Blog>>> GetBlogs()
{
var blogs = await _blogService.GetAllBlogsAsync();
return Ok(blogs);
}
// Get blog details
[HttpGet("{id}")]
public async Task<ActionResult<Blog>> GetBlog(int id)
{
var blog = await _blogService.GetBlogWithPostsAsync(id);
if (blog == null)
return NotFound();
return Ok(blog);
}
// Create 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 });
}
}
// Update blog
[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);
}
// Delete blog
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteBlog(int id)
{
var result = await _relationshipService.DeleteBlogWithAllRelatedDataAsync(id);
if (!result)
return NotFound();
return NoContent();
}
// Search blog posts
[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);
}
// Create post
[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 - Request DTOs
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; }
}
// Migration management command examples
/*
# Create initial migration
dotnet ef migrations add InitialCreate
# Update database
dotnet ef database update
# Add new migration
dotnet ef migrations add AddTagsAndComments
# List migrations
dotnet ef migrations list
# Remove migration (latest only)
dotnet ef migrations remove
# Revert to specific migration
dotnet ef database update AddTagsAndComments
# Generate script (for production)
dotnet ef migrations script --output migration.sql
# Create bundle (executable file)
dotnet ef migrations bundle --output efbundle.exe
*/
// Advanced configuration example in Startup.cs or Program.cs
public void ConfigureServices(IServiceCollection services)
{
// Entity Framework Core configuration
services.AddDbContext<BlogContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlOptions =>
{
// Connection timeout setting
sqlOptions.CommandTimeout(30);
// Specify migrations assembly
sqlOptions.MigrationsAssembly("BlogApp.Migrations");
// Retry configuration
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(5),
errorNumbersToAdd: null);
});
// Detailed logging in development environment
if (Environment.IsDevelopment())
{
options.EnableSensitiveDataLogging();
options.EnableDetailedErrors();
}
// Change tracking optimization
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
// Service registration
services.AddScoped<BlogService>();
services.AddScoped<PostQueryService>();
services.AddScoped<RelationshipService>();
}