ResilientSaveChanges.EFCore
8.0.10
dotnet add package ResilientSaveChanges.EFCore --version 8.0.10
NuGet\Install-Package ResilientSaveChanges.EFCore -Version 8.0.10
<PackageReference Include="ResilientSaveChanges.EFCore" Version="8.0.10" />
paket add ResilientSaveChanges.EFCore --version 8.0.10
#r "nuget: ResilientSaveChanges.EFCore, 8.0.10"
// Install ResilientSaveChanges.EFCore as a Cake Addin #addin nuget:?package=ResilientSaveChanges.EFCore&version=8.0.10 // Install ResilientSaveChanges.EFCore as a Cake Tool #tool nuget:?package=ResilientSaveChanges.EFCore&version=8.0.10
ResilientSaveChanges.EFCore
A library that allows resilient context.SaveChanges / SaveChangesAsync in Entity Framework Core, logging of long-running transactions and limiting of concurrent SaveChanges.
Installation
The recommended means is to use NuGet, but you could also download the source code from here.
Configuration
ResilientSaveChangesConfig.Logger = _logger;
ResilientSaveChangesConfig.LoggerWarnLongRunning = 3_000;
ResilientSaveChangesConfig.ConcurrentSaveChangesLimit = 5;
Setting up an exectution strategy
You now need to create an execution strategy. An example using MySQL and Pomelo:
public static class Constants
{
public const int MAX_RETRY_COUNT = 10;
public const int MAX_RETRY_DELAY_SECONDS = 6;
public const int COMMAND_TIMEOUT = 120;
}
public class MyExecutionStrategy : ExecutionStrategy
{
public MyExecutionStrategy(MyDbContext context) : base(
context,
Constants.MAX_RETRY_COUNT,
TimeSpan.FromSeconds(Constants.MAX_RETRY_DELAY_SECONDS))
{ }
public MyExecutionStrategy(ExecutionStrategyDependencies dependencies) : base(
dependencies,
Constants.MAX_RETRY_COUNT,
TimeSpan.FromSeconds(Constants.MAX_RETRY_DELAY_SECONDS))
{ }
public MyExecutionStrategy(MyDbContext context, int maxRetryCount, TimeSpan maxRetryDelay) : base(
context,
maxRetryCount,
maxRetryDelay)
{ }
protected override bool ShouldRetryOn([NotNull] Exception exception)
{
if (exception is MySqlException mySqlException)
{
if (mySqlException.IsTransient)
{
Debug.WriteLine($"MySqlException transient error detected. Retrying in {Constants.MAX_RETRY_DELAY_SECONDS} seconds");
return true;
}
Debug.WriteLine($"Non-transient MySqlException detected.");
return false;
}
if (exception is DbUpdateException)
{
Debug.WriteLine($"DbUpdateException detected. Retrying in {Constants.MAX_RETRY_DELAY_SECONDS} seconds");
return true;
}
Debug.WriteLine($"Error that won't be retried. Type is {exception.GetType()}");
return false;
}
}
Using the execution strategy
You now need to set your MySQL or SQL Server options to enable retry on failure and to use your execution strategy. An example using MySQL and Pomelo:
services.AddPooledDbContextFactory<MyDbContext>(options =>
{
options.UseMySql(
Configuration.GetConnectionString("DefaultConnection"),
"8.0.29",
options =>
{
options.EnableRetryOnFailure(
Constants.MAX_RETRY_COUNT,
TimeSpan.FromSeconds(Constants.MAX_RETRY_DELAY_SECONDS),
null);
options.CommandTimeout(Constants.COMMAND_TIMEOUT);
options.ExecutionStrategy(s => new MyExecutionStrategy(s));
}
).EnableDetailedErrors();
});
How to use
Now simply replace your context.SaveChanges();
and await context.SaveChangesAsync();
with context.ResilientSaveChanges();
and context.ResilientSaveChangesAsync();
respectively.
Credits
Some code has been adapted from code found in .NET Microservices: Architecture for Containerized .NET Applications (de la Torre, Wagner, & Rousos, 2022)
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Microsoft.EntityFrameworkCore (>= 8.0.10)
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 |
---|---|---|
8.0.10 | 87 | 10/13/2024 |
8.0.7 | 114 | 7/10/2024 |
8.0.0 | 633 | 12/2/2023 |
7.0.14 | 137 | 12/2/2023 |
7.0.13 | 135 | 12/2/2023 |
7.0.12 | 170 | 10/11/2023 |
7.0.5 | 131 | 9/17/2023 |
7.0.4 | 316 | 3/25/2023 |
7.0.2.2 | 313 | 1/29/2023 |
7.0.2 | 312 | 1/11/2023 |
7.0.1.2 | 324 | 12/16/2022 |
7.0.1.1 | 309 | 12/13/2022 |
7.0.1 | 316 | 12/12/2022 |
7.0.0 | 482 | 11/13/2022 |
6.0.10 | 448 | 10/18/2022 |
1.0.5 | 460 | 6/29/2022 |
1.0.4 | 444 | 6/29/2022 |
1.0.3 | 443 | 6/26/2022 |
Support for EFCore 8.0.10, deterministic builds and AOT.