EF Core Shadow Property — Complete Guide
In this tutorial, you'll learn about EF Core Shadow Property. We cover key concepts, practical examples, and best practices.
You need a LastModified column on every table for auditing, but you do not want to add a LastModified property to every entity class. Shadow properties let you map database columns that exist only in the EF Core model, not in your entity class.
Wrong
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// Every entity needs this:
public DateTime LastModified { get; set; }
}
public class Order
{
public int Id { get; set; }
// Also needs LastModified...
public DateTime LastModified { get; set; }
}
Output: Works. But LastModified is duplicated in every entity. Adding it to 50 entities means 50 property declarations.
Right
// Entity — no LastModified property
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
// Configuration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().Property<DateTime>("LastModified");
}
// Set the value via the context
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries())
{
if (entry.State is EntityState.Added or EntityState.Modified)
{
entry.Property("LastModified").CurrentValue = DateTime.UtcNow;
}
}
return base.SaveChanges();
}
Query shadow properties:
var oldProducts = await db.Products
.Where(p => EF.Property<DateTime>(p, "LastModified") < cutoff)
.ToListAsync();
Prevention
- Use shadow properties for cross-cutting concerns (audit timestamps, soft-delete flags, tenant IDs).
- Use
EF.Property<T>(entity, name)to query or set shadow property values. - Use shadow properties to keep entity POCOs clean.
- Use
ChangeTrackerinSaveChangesto auto-populate shadow properties. - Use shadow properties for foreign keys that should not be exposed in the entity.
- Use
[NotMapped]for the opposite — CLR properties not persisted to the database.
Common Mistakes with core shadow property
- Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
- Using
returnto exit a function early instead of wrapping a pure value in the monad - Mixing let bindings with <- bindings in do notation, producing type errors
These mistakes appear frequently in real-world EF code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Shadow properties are used in DodaTech's audit infrastructure across all entities. For more EF Core, visit DodaTech.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro