In this lesson, you will learn how to implement business-level versioning alongside your database migrations to track state changes that EF Core’s built-in schema tools might overlook.
While EF Core migrations handle structural changes to your database schema, business-level versioning is concerned with the lifecycle of the data itself. When you need to maintain a history of an entity—where every update creates a new, immutable record rather than overwriting the old one—you need a strategy that lives in your application layer.
Implementing a Versioned Entity
To implement this, we decouple the "current" state from the "historical" state. A common approach is to include a VersionNumber and an IsCurrent flag within your entity model.
public class Product
{
public int Id { get; set; } // Database surrogate key
public string ProductCode { get; set; } // Business key
public string Name { get; set; }
public int VersionNumber { get; set; }
public bool IsCurrent { get; set; }
}
The Workflow
When you perform an update, you don't just execute a context.Update(). Instead, follow these steps:
- Mark Existing: Locate the current record (where
ProductCode == xandIsCurrent == true) and setIsCurrenttofalse. - Create New: Create a new instance of the entity with the same
ProductCode, increment theVersionNumber, and setIsCurrent = true. - Persist: Add the new entity to the
DbSet.
By using this approach, your database migrations remain focused on the table structure (adding the VersionNumber column, for example), while your DbContext logic handles the business requirement of maintaining a full audit trail of product states. This effectively allows you to query any point-in-time state by filtering on the VersionNumber or simply grabbing the latest state via IsCurrent.
This manual approach complements your schema migrations by ensuring that your data integrity remains intact even as your table structure evolves.
Key Takeaway: Business-level versioning requires you to treat entities as immutable objects by creating new records for updates rather than modifying existing rows, allowing you to maintain a comprehensive historical timeline independent of schema migrations.