LINQ to SQL: Single Data Context vs Multiple (Legacy Guide + EF Core Notes)
The 2010-era debate: one giant DataContext for your whole database, or split it per schema or per feature area? This is the original decision guide, preserved. In 2026 the same decision applies to EF Core’s DbContext — the trade-offs have survived three framework generations.
LINQ to SQL was Microsoft’s first LINQ-backed ORM, shipped with .NET 3.5 in 2007. Entity Framework (classic then EF Core) replaced it within two years. This snippet first went up in 2010 when we were still using L2S in production on a project with 40-odd tables across three schemas. The single-vs-multiple decision was genuine. The ORM has changed; the decision hasn’t.
The decision framework
- Single DataContext — one file, all tables, all relationships. Simpler joins, richer LINQ queries across the whole model, no serialization headaches when passing entities across boundaries
- Multiple DataContexts — one per schema or feature area. Smaller files, faster designer load times on large schemas, cleaner separation of concerns, but painful when you need to query across contexts
- When to pick single — fewer than ~50 tables, heavy cross-schema querying, a team small enough that nobody fights merge conflicts on the .dbml
- When to pick multiple — 100+ tables, clear feature boundaries, team-level ownership of modules, or when designer load times cross 10 seconds
- Modern answer (EF Core 2026) — multiple DbContexts is the default recommendation, with Fluent API configuration in separate files per aggregate
The minimal example
LINQ to SQL stays in .NET Framework forever but is not ported to .NET Core/5+. New code uses EF Core. The examples below show both — LINQ to SQL for legacy codebases, EF Core for anything new.
// LEGACY: LINQ to SQL single DataContext (.NET Framework 4.x only)
[Database(Name = "MyApp")]
public class MyAppDataContext : DataContext
{
public Table<User> Users { get; set; }
public Table<Product> Products { get; set; }
public Table<Order> Orders { get; set; }
// ... every table in the database ...
}
// MODERN: EF Core 2026 — split by aggregate root, composed at DI time
public class UserDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
}
public class CatalogDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
// Register both in Program.cs
builder.Services.AddDbContext<UserDbContext>(opts =>
opts.UseSqlServer(connectionString));
builder.Services.AddDbContext<CatalogDbContext>(opts =>
opts.UseSqlServer(connectionString));Why the trade-off matters
A single context means every query has access to every relationship — you can chain context.Orders.Include(o => o.User).Include(o => o.Products) and LINQ will generate one efficient SQL query with joins across all three tables. A multi-context world can’t do that — crossing context boundaries requires either two queries and in-memory stitching, or the ContextPool pattern in EF Core. The upside of splitting is isolation: a bug in the checkout context can’t silently touch the identity context’s tables, team ownership maps cleanly to modules, and the Designer/scaffolding files stay under 500 lines.
2026 recommendations
- For new .NET projects: EF Core 8+ with one DbContext per bounded context (Domain-Driven Design aggregate)
- For CRUD-heavy apps without DDD structure: one DbContext is still fine up to ~80 entities
- For legacy LINQ to SQL codebases: there’s no migration urgency if the app is stable — L2S runs fine on .NET Framework 4.8.x through 2030 support
- For migration planning: Microsoft’s EF Core porting guide covers the L2S → EF Core mapping
- For query performance tuning: Dapper is still the right answer for read-heavy paths, paired with EF Core for writes
FAQs
Is LINQ to SQL still supported?
On .NET Framework 4.x: yes, and will be through 2030 under extended support. On .NET Core / .NET 5+: no, never ported and never will be. Migrate to EF Core if you’re on .NET 6 or later.
What’s the real performance cost of multiple contexts?
Per-query overhead is negligible (context construction is microseconds). The cost is architectural: cross-context joins require application-layer assembly instead of SQL joins. For hot paths that span contexts, consider a shared read-only view or a dedicated query context.
DbContext pooling — relevant here?
Yes. EF Core 6+ ships AddDbContextPool which caches instantiated contexts for reuse across requests. Per-request pooling matters more when you have multiple contexts — without it, each request pays the initialisation cost for every context it touches.
Should I use Dapper instead of EF Core?
For read-heavy workloads with complex queries, yes — Dapper’s 2-3x query speed advantage over EF Core is real, and its mental model is simpler. For writes and model-tracking scenarios, EF Core wins. Many .NET projects use both: EF Core for commands, Dapper for queries (CQRS-lite).
Where does Entity Framework 6 fit in?
EF6 is the .NET Framework-only successor to LINQ to SQL. It’s maintained but not receiving new features — Microsoft’s investment is in EF Core. For legacy .NET Framework apps, EF6 is still a valid choice; for anything on .NET 6+, use EF Core.
What about Supabase, PlanetScale, or other modern stacks?
Those aren’t .NET ecosystem — they target Node.js and Python primarily. .NET 2026’s ecosystem centres on EF Core for most relational work, with specialised libraries like Marten (for PostgreSQL as a document store) and Cassandra drivers for non-relational.