LINQ to SQL存储过程调用的重试策略

时间:2013-01-11 21:57:12

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

我知道之前已经多次询问过,但我想我可能会有不同的解决方案;但是,我需要一些帮助才能让它发挥作用。

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

2 个答案:

答案 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的原帖!非常令人印象深刻的功能。