Entity Framework Core in LINQPad: Let the Code Work For You
Entity Framework Core (EF Core) is a powerful object-database mapper that lets you work with a database using .NET objects. Instead of writing complex SQL queries, you can focus on building your application’s business logic while EF Core handles the database operations behind the scenes.
Why Entity Framework Core?
EF Core transforms your database into code, making it more maintainable and type-safe. By using classes, properties, and attributes, you can represent your database structure directly in your C# code. This approach offers several benefits:
- Type safety and IntelliSense support
- Reduced SQL boilerplate code
- Automated database schema management
- Simplified data access patterns
Download LINQPad Today
Get started with the most powerful .NET Rapid Progress Tool (RPT) available.
Get LINQPad PremiumPowerful .NET acceleration. Code at the speed of thought.
The Power of DbContext
At the heart of EF Core is the DbContext
class. Think of it as the glue that binds your database and code together. It manages database connections, tracks changes, and handles the translation between your C# objects and database records.
Getting Started
To follow along with these examples, you’ll need:
- LINQPad (I offer a course on mastering LINQPad - check it out!)
- .NET SDK
- SQLite (or your preferred database)
Two Ways to Use DbContext
1. AddDbContext: Let .NET Handle the Lifecycle
When you use AddDbContext
, you’re letting .NET’s dependency injection system manage the creation and disposal of your DbContext. This is perfect for most web applications where you want the framework to handle the lifecycle.
Here’s a complete example showing how to set up a DbContext with a one-to-many relationship between Batch
and BatchSchedule
:
void Main()
{
ServiceCollection services = new();
services.AddScoped<BatchesService>();
services.AddDbContext<BatchesDbContext>();
using ServiceProvider sp1 = services.BuildServiceProvider();
using IServiceScope scope1 = sp1.CreateScope();
var service = scope1.ServiceProvider.GetService<BatchesService>();
service.BatchesGetAll().Dump();
}
public class BatchesDbContext : DbContext
{
public DbSet<Batch> Batches { get; set; }
public string DbPath { get; } = @"./jobscraper.db";
public BatchesDbContext() { }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}")
.LogTo((msg) => msg.Dump(), LogLevel.Information);
}
public class BatchesService
{
public readonly BatchesDbContext Ctx;
public BatchesService(BatchesDbContext ctx) { Ctx = ctx; }
public List<Batch> BatchesGetAll() =>
Ctx.Batches.Include(x => x.Schedules).ToList();
}
public class Batch
{
public int BatchId { get; set; }
public string BatchName { get; set; }
public List<BatchSchedule> Schedules { get; } = new();
}
[Table("batches_scheduler")]
public class BatchSchedule
{
public int Id { get; set; }
public string BatchDate { get; set; }
public int BatchId { get; set; }
public Batch Batch { get; set; }
}
Key points about this approach:
- The DbContext is configured using
OnConfiguring
- We use LINQPad’s
Dump()
method as a logger to see the generated SQL - The
Include()
method performs the join betweenBatch
andBatchSchedule
- Dependency injection provides the DbContext to our service
2. AddDbContextFactory: Take Control
The AddDbContextFactory
pattern gives you more control over the DbContext lifecycle. This is particularly useful when you need to manage multiple DbContext instances or want to control when they’re created and disposed.
void Main()
{
ServiceCollection services = new();
services.AddScoped<BatchesService>();
services.AddDbContextFactory<BatchesDbContext>(options =>
options.UseSqlite($"Data Source={@"./jobscraper.db"}")
.LogTo((msg) => msg.Dump("EF Core Logging"), LogLevel.Information));
using ServiceProvider sp1 = services.BuildServiceProvider();
using IServiceScope scope1 = sp1.CreateScope();
var service = scope1.ServiceProvider.GetService<BatchesService>();
service.BatchesGetAll().Dump();
}
public class BatchesDbContext : DbContext
{
public DbSet<Batch> Batches { get; set; }
public BatchesDbContext(DbContextOptions<BatchesDbContext> options) : base(options) { }
}
public class BatchesService
{
public readonly IDbContextFactory<BatchesDbContext> CtxFactory;
public BatchesService(IDbContextFactory<BatchesDbContext> ctx) { CtxFactory = ctx; }
public List<Batch> BatchesGetAll()
{
using var ctx = CtxFactory.CreateDbContext();
var batches = ctx.Batches.Include(x => x.Schedules).ToList();
return batches;
}
}
And the output in LINQPad looks like this
Key differences in this approach:
- The connection string is configured in the service registration
- The DbContext doesn’t know about the connection details
- We explicitly manage the DbContext lifecycle with
using
statements - The service uses a factory to create DbContext instances as needed
Why LINQPad for Learning?
I use LINQPad for these examples because it’s the perfect tool for exploring and learning EF Core. With its instant feedback loop and the powerful Dump()
method, you can quickly see the results of your queries without setting up a full application.
The LogTo()
method combined with LINQPad’s Dump()
gives us a powerful way to see the SQL that EF Core generates. This is invaluable for learning and debugging.
The alternative? Creating a new Blazor WASM app just to test a simple EF Core example. No thanks! LINQPad lets you focus on learning the concepts without the overhead of project setup.
Conclusion
Entity Framework Core is a game-changer for .NET developers. By letting the code work for you, you can focus on building great applications instead of wrestling with SQL queries. Whether you choose AddDbContext
or AddDbContextFactory
, you’re taking a step toward more maintainable and efficient database operations.
Ready to dive deeper into Entity Framework Core? Try these examples in LINQPad and see how it can transform your database operations!
Want to learn more about LINQPad and how it can supercharge your development workflow? Check out my comprehensive LINQPad course where I cover everything from basic queries to advanced scenarios like the ones shown here.