【问题标题】:Retry Policy for LINQ to SQL Stored Procedure callsLINQ to SQL 存储过程调用的重试策略
【发布时间】:2013-01-11 21:57:12
【问题描述】:

我知道这已经被问过好几次了,但我想我可能有不同的解决方案;但是,我需要一些帮助才能让它工作。

想法: 业务层调用数据层函数。数据层函数将对数据库上下文的存储过程函数的调用包装在重试策略中。本质上,我希望 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'。另外,我不知道怎么写,所以它适用于任何输入参数长度的存储过程。

【问题讨论】:

  • 这里有问题吗?
  • Sq,什么不适合你。 Linq-to-SQL 是否会引发不同类型的异常?它会破坏上下文吗?
  • 这是否可能与您的存储过程没有返回类型并需要Action<T> 而不是Func<T> 或反之亦然的事实有关?
  • @jesse 感谢您的编辑。例外是正确的,我不确定您所说的破坏上下文是什么意思。重试功能需要工作,不确定它是否是Action <T>,这就是我卡住的地方。 -谢谢
  • 那么您收到的异常消息到底是什么?

标签: .net vb.net linq linq-to-sql


【解决方案1】:

在需要实现类似的东西后,我继续将其设为库:https://github.com/daveaglick/LinqToSqlRetry(MIT 许可,可在 NuGet 上使用)。它是一个标准的 .NET 库,因此它也应该可以在 VB 中使用(尽管我下面的示例是 C# - 请原谅我不太了解 VB)。

您可以改写SubmitChangesRetry() 来重试SubmitChanges() 调用:

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对象中的函数是根据使用情况提供的。这只是让您了解重试循环期间发生了什么。只需在存储库中查看更多信息。

【讨论】:

    【解决方案2】:

    有时答案就在问题中,在这种情况下是字面意思。 What is good C# coding style for catching SQLException and retrying 中的函数 Retry 实际上也适用于存储过程调用!对于那些仍在使用 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 的原帖!非常令人印象深刻的功能。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多