我知道之前已经多次询问过,但我想我可能会有不同的解决方案;但是,我需要一些帮助才能让它发挥作用。
这个想法: 业务层调用数据层功能。数据层函数将调用包装在重试策略中的数据库上下文的存储过程函数中。本质上,我希望LINQ工具可以导入和管理对存储过程的实际调用,但是我希望它将逻辑包含一些重试策略以用于重试错误。
这个概念大部分来自What is good C# coding style for catching SQLException and retrying,但是,这似乎只适用于LINQ to SQL命令,而不是调用DBML中生成的存储过程函数。
旧方法:
Sub BLFunctionWithoutRetry()
Using DB as CustDataContext
DB.sp_GetCustomers()
End Using
End Sub
重试的新逻辑:
Sub BLFunctionWithRetry()
GetCustomers()
End Sub
Function GetCustomers() As List(Of Customer)
Return Retry(Of List(Of Customer))(sp_GetCustomers())
End Function
Function Retry(Of T)(FunctionToCall As Func(Of T)) As T
Dim Attempt As Integer = 0
While True
Try
Using DB as MyDataContext
DB.FunctionToCall()
End Using
Catch ex as SQLException
If Retryable() Then Attempt += 1
If Attempt >= Max Or Not Retryable() Then Throw
End Try
End While
Function Retryable() As Boolean
Return True
End Function
这是一般的想法;但是,我需要帮助编写上面的重试功能。我收到了明显的错误FunctionToCall() is not a member of 'MyDataContext'
。 Additaionlly,我不知道如何写这个,所以它适用于任何存储过程的任何长度的输入参数。
答案 0 :(得分:1)
在需要实现这样的东西之后,我继续把它变成了一个库:https://github.com/daveaglick/LinqToSqlRetry(麻省理工学院许可并在NuGet上提供)。它是一个标准的.NET库,所以它也可以在VB中使用(虽然下面我的例子是C# - 原谅我不太了解VB)。
您可以通过编写SubmitChanges()
来重试SubmitChangesRetry()
来电:
using(var context = new MyDbContext())
{
context.Items.InsertOnSubmit(new Item { Name = "ABC" });
context.SubmitChangesRetry();
}
您还可以使用Retry()
扩展程序重试查询:
using(var context = new MyDbContext())
{
int count = context.Items.Where(x => x.Name == "ABC").Retry().Count();
}
特定的重试逻辑可由策略控制。在引擎盖下,重试机制看起来像:
int retryCount = 0;
while (true)
{
try
{
return func();
}
catch (Exception ex)
{
TimeSpan? interval = retryPolicy.ShouldRetry(retryCount, ex);
if (!interval.HasValue)
{
throw;
}
Thread.Sleep(interval.Value);
}
retryCount++;
}
了解调用func()
和retryPolicy
对象的函数是根据用法提供的。这只是让您了解重试循环期间发生了什么。只需查看存储库以获取更多信息。
答案 1 :(得分:0)
有时候问题的答案就在这个问题上。 What is good C# coding style for catching SQLException and retrying中的重试功能实际上也适用于存储过程调用!对于仍在VB中工作的人,这里是代码:
Public Class DatabaseHelper
Private Enum RetryableSqlErrors
SqlConnectionBroken = -1
SqlTimeout = -2
SqlOutOfMemory = 701
SqlOutOfLocks = 1204
SqlDeadlockVictim = 1205
SqlLockRequestTimeout = 1222
SqlTimeoutWaitingForMemoryResource = 8645
SqlLowMemoryCondition = 8651
SqlWordbreakerTimeout = 30053
End Enum
Public Shared Sub Retry(Of T As {DataContext, New})(retryAction As Action(Of T), ByVal MaxAttempts As Integer, ByVal Delay As Integer)
Dim retryCount = 0
Using ctx = New T()
While True
Try
retryAction(ctx)
Exit Sub
Catch ex As SqlException
If Not [Enum].IsDefined(GetType(RetryableSqlErrors), ex.Number) Then
Throw
End If
retryCount += 1
If retryCount > MaxAttempts Then
Throw
End If
Thread.Sleep(If(ex.Number = CInt(RetryableSqlErrors.SqlTimeout), Delay * 5, Delay))
End Try
End While
End Using
End Sub
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
End Class
然后调用存储过程:
Dim results = New YourType
Retry(Of YourDataContext)(Function(ctx) InlineAssignHelper(Of YourType)(results, ctx.YourStoredProc("Your", "Proc", "Inputs", 1).First), 3, .5)
Return results
与原版稍有不同:我决定只使用1个延迟值,并将其乘以5超时。
感谢David Clarke的原帖!非常令人印象深刻的功能。