Course contentsShow
← C#
Lesson 1048 of 1,90810. Entity Framework Core ModelingFree lesson

Shadow Foreign Keys

Manage foreign key relationships without exposing technical columns in the domain model.

In this lesson, you will learn how to maintain referential integrity in your database without cluttering your clean C# domain entities with "technical" foreign key properties.

The Problem: Leaky Abstractions

Usually, when you define a relationship in Entity Framework Core, you add a navigation property (e.g., public Blog Blog { get; set; }) and a matching foreign key property (e.g., public int BlogId { get; set; }). While this is standard, sometimes your business logic shouldn't care about the underlying database IDs. You might want to interact solely with the object itself, keeping the class "pure" and free of database-specific plumbing.

The Solution: Shadow Foreign Keys

A Shadow Foreign Key is a foreign key that exists in the database schema and is managed by EF Core, but does not exist as a property in your C# class. EF Core tracks these values internally within the ChangeTracker, allowing you to maintain relationships while keeping your domain models clean.

To implement this, you omit the foreign key property from your entity and use the Fluent API to explicitly map the relationship.

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    // No BlogId property here!
    public Blog Blog { get; set; } 
}

public class MyDbContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey("BlogId"); // String name creates the shadow property
    }
}

How It Works

When you call HasForeignKey("BlogId") in the Fluent API, EF Core creates a hidden property named BlogId on the Post entity that is only visible to the database layer. You can still set or retrieve this value using Entry(post).Property("BlogId").CurrentValue, but your business logic remains focused on the Blog object.

This approach is particularly useful in Domain-Driven Design (DDD), where you want to prevent developers from accidentally writing code that bypasses the navigation property in favor of simple ID assignments. It enforces the use of the object model while keeping the persistence layer happy.

Key Takeaway: Shadow Foreign Keys allow you to enforce database relationships through the Fluent API while keeping your domain classes clean of database-specific ID properties.