Entity Framework Core: Data Integrity and Patterns
The Story of the Magical Bank Vault đŚ
Imagine you run a magical bank. Every day, wizards come to deposit gold coins, transfer money, and check their balances. But hereâs the tricky partâwhat happens if two wizards try to grab the same gold coin at the exact same moment? Or what if the lights go out in the middle of counting coins?
Thatâs exactly what Entity Framework Coreâs Data Integrity and Patterns helps us solve! Think of it as the rulebook for keeping your database safe, consistent, and reliableâno matter what chaos happens.
Letâs explore this magical world together!
đ The Cast of Characters
| Pattern | Real-World Analogy |
|---|---|
| Transactions | A sealed envelopeâeither everything inside is delivered, or nothing is |
| Concurrency | Two kids grabbing the last cookie at the same time |
| Resiliency | A phone that reconnects to WiFi automatically |
| Seeding | Pre-filling a notebook with example entries |
| Data Patterns | Different ways to organize your toy collection |
| Scaffolding | Building blocks that create a house blueprint from an existing house |
1. Transactions: The All-or-Nothing Promise đŚ
Whatâs the Big Idea?
A transaction is like putting multiple tasks in a sealed envelope. Either ALL tasks succeed together, or NONE of them happen. No half-done work allowed!
Simple Example: Transferring Money
Imagine you want to move $100 from your piggy bank to your friendâs jar:
- Take $100 from your piggy bank
- Put $100 in friendâs jar
What if step 1 works but step 2 fails? Your money vanishes! đą
With transactions, if step 2 fails, step 1 automatically undoes itself.
using var transaction =
await context.Database
.BeginTransactionAsync();
try
{
// Step 1: Remove from your account
myAccount.Balance -= 100;
// Step 2: Add to friend's account
friendAccount.Balance += 100;
await context.SaveChangesAsync();
// Everything worked! Seal the deal.
await transaction.CommitAsync();
}
catch
{
// Something broke! Undo everything.
await transaction.RollbackAsync();
}
The Three Magic Words
| Word | Meaning |
|---|---|
| Begin | Start the sealed envelope |
| Commit | Seal and send it |
| Rollback | Tear it up, pretend nothing happened |
2. Concurrency Handling: Who Gets the Last Cookie? đŞ
Whatâs the Big Idea?
Concurrency happens when two people try to change the same thing at the same time. Itâs like two kids reaching for the last cookie!
EF Core has a smart solution: version stamps. Every row gets a secret number that changes whenever someone edits it.
How It Works
graph TD A["đ§ User A reads product<br/>Price: $10, Version: 1"] --> B["đ§ User B reads same product<br/>Price: $10, Version: 1"] B --> C["User A changes price to $15<br/>Saves with Version 1"] C --> D["Database updates<br/>Version becomes 2"] D --> E["User B tries to save $12<br/>Still thinks Version is 1"] E --> F[â CONFLICT!<br/>Versions don't match]
The Code
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
// This magical timestamp catches conflicts!
[Timestamp]
public byte[] RowVersion { get; set; }
}
When a conflict happens:
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
// Someone else changed it first!
// Decide: keep theirs, keep ours, or merge?
}
Resolution Strategies
| Strategy | When to Use |
|---|---|
| Client Wins | Userâs changes always override |
| Database Wins | Keep whatâs already saved |
| Custom Merge | Combine both changes smartly |
3. Connection Resiliency: The Bounce-Back Power đ
Whatâs the Big Idea?
Sometimes the internet hiccups. Servers get busy. Connections drop for a split second. Resiliency means your app doesnât crashâit automatically tries again!
Simple Analogy
Imagine calling your friend but the call drops. Instead of giving up, your phone redials automatically. Thatâs resiliency!
The Code
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(
connectionString,
sqlOptions => sqlOptions
.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan
.FromSeconds(30),
errorNumbersToAdd: null)
)
);
What Gets Retried?
| Retried Automatically | NOT Retried |
|---|---|
| Network timeouts | Wrong password |
| Server too busy | Invalid SQL |
| Connection dropped | Constraint violations |
Custom Retry Strategy
options.UseSqlServer(
connectionString,
sqlOptions => sqlOptions
.ExecutionStrategy(
context => new MyRetryStrategy(
context,
maxRetryCount: 3,
retryDelay: TimeSpan
.FromMilliseconds(500)
)
)
);
4. Database Seeding: Pre-Filling the Notebook đ
Whatâs the Big Idea?
When you get a new notebook, sometimes it helps to have some example entries already written. Seeding fills your database with starter data!
Two Types of Seeds
| Type | When It Runs | Best For |
|---|---|---|
| HasData | During migrations | Fixed data that rarely changes |
| Custom Seeding | At app startup | Dynamic or environment-specific data |
Method 1: HasData (The Migration Way)
protected override void OnModelCreating(
ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>()
.HasData(
new Category {
Id = 1,
Name = "Electronics"
},
new Category {
Id = 2,
Name = "Books"
},
new Category {
Id = 3,
Name = "Clothing"
}
);
}
Method 2: Custom Seeding (The Startup Way)
public static async Task SeedDatabase(
MyContext context)
{
// Only add if empty
if (!context.Users.Any())
{
context.Users.AddRange(
new User {
Name = "Admin",
Role = "Administrator"
},
new User {
Name = "Guest",
Role = "Viewer"
}
);
await context.SaveChangesAsync();
}
}
Pro Tips
- Always check if data exists before seeding
- Use fixed IDs for HasData (required!)
- Keep seeding logic separate from business logic
5. Data Access Patterns: Organizing Your Toys đ§¸
Whatâs the Big Idea?
There are many ways to organize toysâby color, by size, by type. Similarly, there are different patterns for organizing how your code talks to the database!
The Main Patterns
graph TD A["Your App"] --> B{Which Pattern?} B --> C["Repository Pattern<br/>đď¸ One box per toy type"] B --> D["Unit of Work<br/>đŚ Track all changes together"] B --> E["CQRS<br/>đ Read and Write are separate"] B --> F["Direct DbContext<br/>đŻ Simple and direct"]
Pattern 1: Repository Pattern
Think of it as having a separate toy box for each type of toy.
public interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
Task AddAsync(Product product);
Task UpdateAsync(Product product);
Task DeleteAsync(int id);
}
public class ProductRepository
: IProductRepository
{
private readonly MyContext _context;
public ProductRepository(MyContext context)
{
_context = context;
}
public async Task<Product> GetByIdAsync(
int id)
{
return await _context.Products
.FindAsync(id);
}
}
Pattern 2: Unit of Work
Think of it as a shopping cartâadd many items, then checkout all at once.
public interface IUnitOfWork
{
IProductRepository Products { get; }
IOrderRepository Orders { get; }
Task<int> SaveChangesAsync();
}
Pattern 3: CQRS (Command Query Separation)
Reading and Writing use different pathsâlike having separate doors for entering and exiting.
// QUERY: Just reading, optimized for speed
public class GetProductsQuery
{
public async Task<List<ProductDto>> Execute()
{
return await _context.Products
.AsNoTracking() // Faster reads!
.Select(p => new ProductDto {...})
.ToListAsync();
}
}
// COMMAND: Making changes
public class CreateProductCommand
{
public async Task Execute(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
}
}
Which Pattern to Choose?
| Scenario | Recommended Pattern |
|---|---|
| Small app, few entities | Direct DbContext |
| Medium app, need testing | Repository |
| Large app, complex logic | Repository + Unit of Work |
| High-traffic reads/writes | CQRS |
6. Scaffolding: Building Blueprints from Reality đď¸
Whatâs the Big Idea?
Imagine you see a beautiful house and want to build one just like it. Scaffolding creates the blueprints (code) by looking at an existing building (database)!
This is called Database-First approach.
The Magic Command
dotnet ef dbcontext scaffold \
"Server=.;Database=Store;..." \
Microsoft.EntityFrameworkCore.SqlServer \
--output-dir Models \
--context StoreContext
What Gets Generated?
graph LR A["đď¸ Existing Database"] --> B["Scaffold Command"] B --> C["đ Entity Classes"] B --> D["đ DbContext"] B --> E["đ Configuration"]
Example: Generated Product Class
// Auto-generated from database!
public partial class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public decimal? Price { get; set; }
public int? CategoryId { get; set; }
public virtual Category Category
{ get; set; }
}
Scaffolding Options
| Option | What It Does |
|---|---|
--output-dir |
Where to put the files |
--context |
Name your DbContext |
--tables |
Only scaffold specific tables |
--force |
Overwrite existing files |
--no-pluralize |
Keep table names as-is |
Updating After Database Changes
# Re-scaffold with --force
dotnet ef dbcontext scaffold \
"ConnectionString" \
Provider \
--force
Pro Tip: Partial Classes
Keep your custom code safe from re-scaffolding:
// Generated file: Product.cs
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
// Your custom file: Product.Custom.cs
public partial class Product
{
// Your additions here!
public string DisplayName =>
quot;{Name} (ID: {Id})";
}
đŻ Quick Reference Table
| Concept | One-Line Summary |
|---|---|
| Transactions | All succeed or all fail together |
| Concurrency | Detect when two users edit simultaneously |
| Resiliency | Auto-retry on connection failures |
| Seeding | Pre-populate database with starter data |
| Data Patterns | Organized ways to structure data access |
| Scaffolding | Generate code from existing database |
đ You Did It!
Youâve just learned how Entity Framework Core keeps your data safe, consistent, and reliable! These patterns are like the guardrails on a mountain roadâthey keep everything from going off the cliff.
Remember:
- Transactions = Sealed envelopes (all or nothing)
- Concurrency = Cookie grabbing rules
- Resiliency = Auto-redial on dropped calls
- Seeding = Pre-filled notebooks
- Patterns = Toy organization systems
- Scaffolding = Building blueprints from existing houses
Now go build something amazing! đ
