将MySQL与.NET 5/EF一起使用时出现暂时错误



我的IT人员刚刚将我们的MySQL数据库更新到一个新的集群,现在我遇到了这样的临时错误:

引发了一个异常,该异常可能是由于瞬态故障引起的。请考虑通过在"UseMySql"调用中添加"EnableRetryOnFailure(("来启用临时错误恢复能力。

因此,经过研究,我添加了以下代码:

options.UseMySQL(Configuration.GetConnectionString(connectionString),
options => options.EnableRetryOnFailure
(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null
)
);
});

不幸的是,这导致了一个新的错误:

错误CS1061"MySQLDbContextOptionsBuilder"不包含"EnableRetryOnFailure"的定义,也没有可访问的扩展方法"EnableRetryNFailure">

我不知道我是否缺少引用,但intellisense对没有帮助

所以,我在这里做了更多的研究和信息https://dev.mysql.com/doc/connector-net/en/connector-net-entityframework60.html在这里https://entityframeworkcore.com/knowledge-base/57595183/using-entity-framework-with-multiple-databases-and-providers-in-the-same-project--sql-server-and-mysql-关于使用MySQL中的这一行:

SetExecutionStrategy("MySql.Data.MySqlClient", () => new MySqlExecutionStrategy());

这也会引发错误。

有人知道我如何使用MySQL实现连接弹性/重试逻辑吗?

.Net 5,MySQL 5.7,MySqlConnector 8.0.20

食用此https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-resilient-entity-framework-core-sql-connections

使用

services.AddDbContext<AppDbContext>(options =>
{
string connectionString = AppConfig.Configuration.GetConnectionString("DefaultConnection");
options.UseMySql(connectionString,
ServerVersion.AutoDetect(connectionString),
mySqlOptions =>
mySqlOptions.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
);
});

我创建了一个库,使您能够通过配置的执行策略和扩展方法恢复SaveChanges/SaveChangesAsync。

首先安装ResilietSaveChanges.EFCore,然后在应用程序启动时对其进行配置,例如:

ResilientSaveChangesConfig.Logger = _logger;
ResilientSaveChangesConfig.LoggerWarnLongRunning = 3_000;
ResilientSaveChangesConfig.ConcurrentSaveChangesLimit = 5;

然后为MySQL设置执行策略,例如:

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;
}
}

然后利用你的执行策略,例如:

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();
});

最后简单地将context.SaveChanges();await context.SaveChangesAsync();分别替换为context.ResilientSaveChanges();context.ResilientSaveChangesAsync();

最新更新