Swevo.EFCore.RowVersion
1.0.1
Prefix Reserved
dotnet add package Swevo.EFCore.RowVersion --version 1.0.1
NuGet\Install-Package Swevo.EFCore.RowVersion -Version 1.0.1
<PackageReference Include="Swevo.EFCore.RowVersion" Version="1.0.1" />
<PackageVersion Include="Swevo.EFCore.RowVersion" Version="1.0.1" />
<PackageReference Include="Swevo.EFCore.RowVersion" />
paket add Swevo.EFCore.RowVersion --version 1.0.1
#r "nuget: Swevo.EFCore.RowVersion, 1.0.1"
#:package Swevo.EFCore.RowVersion@1.0.1
#addin nuget:?package=Swevo.EFCore.RowVersion&version=1.0.1
#tool nuget:?package=Swevo.EFCore.RowVersion&version=1.0.1
Swevo.EFCore.RowVersion
Compile-time optimistic concurrency for EF Core. Stamp [Optimistic] on any partial entity class and the source generator wires up a RowVersion byte array property and the IOptimisticEntity interface. Two ready-made retry extensions handle concurrent-write conflicts without any Polly dependency.
Quick-start
dotnet add package Swevo.EFCore.RowVersion
1 — Mark your entities
using EFCore.RowVersion;
[Optimistic]
public partial class Order
{
public int Id { get; set; }
public string Description { get; set; } = "";
// byte[] RowVersion { get; set; } is generated automatically
}
The class must be
partial. Non-partial classes produce diagnosticRVRS001.
2 — Configure DbContext
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
=> modelBuilder.AddOptimisticConcurrencyConfiguration();
}
3 — Save with retry
// Client wins — re-applies your changes over the updated DB values
await db.SaveChangesClientWinsAsync(maxRetries: 3);
// Database wins — discards your in-memory changes, accepts the DB values
await db.SaveChangesDatabaseWinsAsync(maxRetries: 3);
SQL Server setup
On SQL Server the generated [Timestamp] attribute on RowVersion is recognised by EF Core convention and automatically configures the property as a database-managed rowversion column. No additional code is needed.
// For SQL Server, also tell EF Core to use the database-managed rowversion:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddOptimisticConcurrencyConfiguration();
// Optional – [Timestamp] convention already does this for SQL Server:
// modelBuilder.Entity<Order>().Property(o => o.RowVersion).IsRowVersion();
}
Non-SQL-Server setup
For SQLite, PostgreSQL, and other providers, override ValueGeneratedNever() and update the version manually on each save (e.g. using an interceptor):
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddOptimisticConcurrencyConfiguration();
modelBuilder.Entity<Order>().Property(o => o.RowVersion).ValueGeneratedNever();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.AddInterceptors(new VersionIncrementInterceptor());
// Interceptor that stamps a new Guid on every save
sealed class VersionIncrementInterceptor : SaveChangesInterceptor
{
public override InterceptionResult<int> SavingChanges(DbContextEventData e, InterceptionResult<int> r)
{
foreach (var entry in e.Context!.ChangeTracker.Entries<IOptimisticEntity>()
.Where(x => x.State is EntityState.Added or EntityState.Modified))
entry.Entity.RowVersion = Guid.NewGuid().ToByteArray();
return base.SavingChanges(e, r);
}
}
How it works
| Piece | What it does |
|---|---|
[Optimistic] |
Marks the entity for code generation |
| Source generator | Emits [Timestamp] byte[] RowVersion + IOptimisticEntity implementation |
AddOptimisticConcurrencyConfiguration |
Calls IsConcurrencyToken() for all IOptimisticEntity entities in OnModelCreating |
SaveChangesClientWinsAsync |
Retries on DbUpdateConcurrencyException; refreshes original values from DB, re-applies client values |
SaveChangesDatabaseWinsAsync |
Retries on DbUpdateConcurrencyException; reloads the entity, discards client changes |
Stacking with other Swevo packages
[Optimistic] // → RowVersion + optimistic concurrency retry
[Auditable] // → CreatedAt, UpdatedAt
[SoftDelete] // → IsDeleted + global query filter
[Tenant] // → TenantId + per-tenant query filter
public partial class Order
{
public OrderId Id { get; set; }
}
Requirements
- .NET 8+
- EF Core 8+
License
MIT
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 135 | 6/26/2026 |