Connection Per Tenant in EF Core: Design & Pitfalls for SQL Server

September 19, 2025 · 6 min

Multi-tenant SaaS applications face a critical decision: how to isolate tenant data while maintaining performance and security. One wrong move and Tenant A sees Tenant B’s customer records. I’ve seen this exact scenario destroy customer trust overnight.

The connection-per-tenant approach offers strong isolation but introduces complexity that can sink your architecture if not handled correctly. Let’s look at the real-world patterns, pitfalls, and production-ready solutions.

The Multi-Tenant Connection Problem

Most SaaS applications start simple: one database, one connection string, everything shared. This works until you need compliance certifications, enterprise customers demand data isolation, or you face your first security audit.

Common Early Mistakes

Shared Connection String Chaos: Using the same connection string for all tenants with TenantId filtering sounds efficient. Until someone forgets a WHERE clause and exposes everything.

Per-Tenant Schema Confusion: Mapping different schemas in EF Core without proper context switching leads to runtime errors that are painful to debug.

Security Breach Example: I’ve consulted on incidents where a missing global query filter resulted in tenant data leakage. The fix took hours, but the damage to customer relationships lasted months.

Multi-Tenant Database Design Patterns

Here are three proven approaches for handling tenant connections, each with distinct trade-offs:

Database Per Tenant (Full Isolation) …

Read more

EF Core Tenant Isolation: Global Query Filters for Secure Multi-Tenant SaaS

September 18, 2025 · 6 min

In one of our enterprise SaaS projects last year, we discovered a critical bug during a routine audit. Customer A could see order data belonging to Customer B when filtering by a specific date range. The root cause? A missing WHERE TenantId = @tenantId clause in a complex reporting query.

This wasn’t just embarrassing. It was a potential GDPR violation that could have resulted in significant penalties. That incident taught us that manual tenant filtering is prone to human error, especially in large codebases with multiple developers.

EF Core’s global query filters solved this problem by automatically applying tenant isolation at the ORM level. Here’s how we implemented bulletproof tenant isolation that passes SOC2 and HIPAA compliance requirements.

The Multi-Tenant Entity Foundation

Every entity in our system includes a TenantId property. This isn’t optional, it’s the foundation of data isolation.

public class Order
{
    public int Id { get; set; }
    public Guid TenantId { get; set; }
    public string CustomerName { get; set; }
    public decimal Amount { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public Guid TenantId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

The TenantId is resolved through dependency injection using a tenant provider service:

public interface ITenantProvider
{
    Guid TenantId { get; }
}

public …
...

Read more

TL;DR: My EF Core Entity Modeling Checklist

September 17, 2025 · 7 min

A few years back, I spent a solid week chasing a weird performance bug. Queries that should have taken milliseconds were sometimes taking seconds to run. The culprit? A simple string property on a core entity. Nobody had set a max length, so EF Core defaulted it to nvarchar(max). SQL Server handles those columns differently, and it was wrecking our query plans.

This one burned me in production, and it taught me a hard lesson: your entity model is the foundation of your application’s performance. Get it wrong, and you’ll pay for it with slow queries and painful migrations.

This checklist is the result of modeling dozens of schemas for EF Core on SQL Server. I’m focusing on SQL Server here because its defaults and indexing behaviors are unique.

Getting the Basics Right: The Entity Itself

Let’s start with the entity class. If you mess this up, everything else gets more complicated.

  • Primary Keys: Just use Id or [EntityName]Id. Don’t get clever. I avoid composite keys unless the domain model is impossible without them. They make joins and repository logic a pain to write and maintain.

    // Good: Simple, clean, effective.
    public int Id { get; private set; }
    
    // Avoid: This complicates everything.
    public class OrderItemKey
    {
        public int OrderId { get; set; }
        public int ProductId { get; set; }
    }
    
  • Value Objects: For data that has no identity on its own, use Owned Types. An Address or a Money value doesn’t need its own Id. It belongs to its parent. This …

Read more