Phase 1 Project: EF Core Fundamentals Repository
Project Goal
Build a comprehensive repository demonstrating all EF Core fundamentals concepts with practical examples.
Project Structure
EFCoreFundamentals/
├── src/
│ ├── Domain/
│ │ └── Entities/
│ ├── Infrastructure/
│ │ ├── Data/
│ │ │ ├── Configurations/
│ │ │ ├── Migrations/
│ │ │ └── AppDbContext.cs
│ │ └── Repositories/
│ └── ConsoleApp/
│ └── Demonstrations/
└── tests/
└── Integration Tests/
Requirements
1. Entity Models
Create entities demonstrating:
- One-to-Many: Blog → Posts
- One-to-One: User → UserProfile
- Many-to-Many: Students ↔ Courses (with enrollment data)
- Self-Referencing: Category → SubCategories
- TPH Inheritance: Payment → CreditCardPayment, BankTransferPayment
2. Configurations
Use IEntityTypeConfiguration for all entities with:
- Primary keys
- Relationships with appropriate delete behaviors
- Indexes (single, composite, unique, filtered)
- Default values
- Value conversions
- Computed columns
3. Migrations
Create migrations for:
- Initial schema
- Seed data
- Schema changes (add column, rename column)
- Custom SQL (stored procedure, function)
- Index creation
4. Demonstrations
Create console app demos for:
DbContext Lifecycle:
- Create, use, dispose pattern
- DbContext pooling comparison
- IDbContextFactory usage
Change Tracking:
- View entity states
- Track vs NoTrack comparison
- Attach/Update scenarios
- DetectChanges performance
Relationships:
- Loading related data
- Adding related entities
- Cascade delete behavior
- Circular reference handling
Migrations:
- Apply migrations
- Rollback migrations
- Generate SQL scripts
- Seed data
5. Integration Tests
Test:
- Entity configurations
- Relationships
- Cascade deletes
- Unique constraints
- Default values
- Migrations
Implementation Guide
Step 1: Setup Project
dotnet new console -n EFCoreFundamentals
cd EFCoreFundamentals
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Logging.Console
Step 2: Create Entities
Start with Blog/Post example:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public DateTime CreatedDate { get; set; }
public bool IsActive { get; set; }
public List<Post> Posts { get; set; } = new();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PublishedDate { get; set; }
public int ViewCount { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
Step 3: Create Configurations
public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder.HasKey(b => b.Id);
builder.Property(b => b.Name)
.IsRequired()
.HasMaxLength(100);
builder.Property(b => b.Url)
.HasMaxLength(200);
builder.Property(b => b.CreatedDate)
.HasDefaultValueSql("GETUTCDATE()");
builder.Property(b => b.IsActive)
.HasDefaultValue(true);
builder.HasIndex(b => b.Name);
builder.HasMany(b => b.Posts)
.WithOne(p => p.Blog)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
// Seed data
builder.HasData(
new Blog { Id = 1, Name = "Tech Blog", Url = "https://tech.blog" },
new Blog { Id = 2, Name = "Travel Blog", Url = "https://travel.blog" }
);
}
}
Step 4: Create DbContext
public class AppDbContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Author> Authors { get; set; }
// ... other DbSets
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
Step 5: Create Demonstrations
public class ChangeTrackingDemo
{
public static async Task Run()
{
await using var context = new AppDbContext();
// Demo 1: Entity States
Console.WriteLine("=== Entity States Demo ===");
var blog = new Blog { Name = "New Blog" };
Console.WriteLine($"Detached: {context.Entry(blog).State}");
context.Blogs.Add(blog);
Console.WriteLine($"Added: {context.Entry(blog).State}");
await context.SaveChangesAsync();
Console.WriteLine($"Unchanged: {context.Entry(blog).State}");
blog.Name = "Updated Blog";
Console.WriteLine($"Modified: {context.Entry(blog).State}");
// Demo 2: Tracking vs NoTracking
var sw = Stopwatch.StartNew();
var tracked = await context.Blogs.ToListAsync();
sw.Stop();
Console.WriteLine($"With tracking: {sw.ElapsedMilliseconds}ms");
sw.Restart();
var noTrack = await context.Blogs.AsNoTracking().ToListAsync();
sw.Stop();
Console.WriteLine($"No tracking: {sw.ElapsedMilliseconds}ms");
}
}
Step 6: Create Tests
[Fact]
public async Task Blog_CascadeDelete_DeletesPosts()
{
// Arrange
await using var context = CreateContext();
var blog = new Blog { Name = "Test Blog" };
blog.Posts.Add(new Post { Title = "Test Post" });
context.Blogs.Add(blog);
await context.SaveChangesAsync();
// Act
context.Blogs.Remove(blog);
await context.SaveChangesAsync();
// Assert
var posts = await context.Posts.ToListAsync();
Assert.Empty(posts);
}
Deliverables
- Complete source code on GitHub
- README with:
- Project overview
- Setup instructions
- How to run demonstrations
- Migrations folder with all migrations
- Console app with all demonstrations
- Test project with integration tests
Success Criteria
- All relationships configured correctly
- Migrations apply successfully
- Seed data loads
- All demonstrations run without errors
- Tests pass
- Code is well-organized and documented
- No N+1 query problems
- Proper use of IEntityTypeConfiguration
Time Estimate
~20 hours