Skip to main content

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

  1. Complete source code on GitHub
  2. README with:
    • Project overview
    • Setup instructions
    • How to run demonstrations
  3. Migrations folder with all migrations
  4. Console app with all demonstrations
  5. 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