0

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

思路:业务层调用数据层函数。数据层函数将对数据库上下文的存储过程函数的调用包装在重试策略中。本质上,我希望 LINQ 工具能够导入和管理对存储过程的实际调用,但我希望它能够使用一些重试策略来包装逻辑,以应对可重试的错误。

这个概念的大部分来自What is good C# coding style for catch 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'。另外,我不知道如何编写它,因此它适用于具有任意长度输入参数的任何存储过程。

4

2 回答 2

1

在需要实现类似这样的东西之后,我继续将它变成了一个库:https ://github.com/daveaglick/LinqToSqlRetry (MIT 许可并在 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对象是根据使用情况提供的。这只是让您了解重试循环期间发生了什么。只需在存储库中查看更多信息。

于 2014-12-22T22:51:51.190 回答
0

有时答案就在问题中,在这种情况下完全是字面意思。什么是好的 C# 编码风格中用于捕获 SQLException 和重试的函数 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 以表示超时。

感谢大卫克拉克的原帖!非常令人印象深刻的功能。

于 2013-01-14T23:23:14.067 回答