2012-04-17 9 views
6

Tengo una aplicación web bastante grande que usa LINQ-TO-SQL ejecutándose en Azure, y estoy experimentando errores transitorios de SQL-Azure y, por lo tanto, necesito implementar reintentos. Soy consciente de los sitios Transient Fault Handling Framework y varios que dan ejemplos de cómo usarlo, pero parece que hay que envolver cada una de sus consultas LINQ en algo similar a esto:Volver a intentar la lógica para LINQ-TO-SQL en SQL Azure: ¿Implementaciones eficientes?

RetryPolicy retry = new RetryPolicy<MyRetryStrategy>(5, TimeSpan.FromSeconds(5)); 
Result = retry.ExecuteAction(() => 
{ 
    … LINQ query here... 
}); 

Con cientos de consultas LINQ en mi capa de datos, esto parece realmente desordenado, más el hecho de que muchas veces la consulta no se ejecuta hasta que se enumeran los resultados. Por ejemplo, la mayoría de mis funciones en mi capa de datos devuelven IQueryable <> hasta la capa empresarial (lo que las hace más flexibles que devolver una Lista). Así que eso significaría que debe ensuciar su capa de lógica de negocios con la lógica de reintento de la base de datos: fea.

Supongo que para mantener la lógica de reintento en la capa de datos, tendría que poner .ToList() en todas mis consultas para que se ejecuten allí, y no en la capa de arriba.

Realmente me gustaría poder implementar la lógica de reintento en algunas clases base y no tener que cambiar todas mis consultas. Parece que EF también tendría este problema.

¿Es la verdadera respuesta intentar que el equipo de SQL-Azure haga los reintentos automáticos para que no tengamos que preocuparnos por eso en nuestro código?

Respuesta

0

No conozco una buena solución, ya que LINQ to SQL no nos permite interceptar consultas. Pero un pequeño código refactor puede ayudar. Algo así como (pseudo código):

public Result QueryWithRetry(IQueryable query) 
{ 
     RetryPolicy retry = new RetryPolicy<MyRetryStrategy>(5, TimeSpan.FromSeconds(5)); 
    (() => 
    { 
     return retry.ExecuteAction(query); 
    } 
} 

Ahora es un poco más fácil para invocar este método:

Resultado = QueryWithRetry (... LINQ consulta aquí ...);

Sin embargo, todavía es necesario modificar su código y cambiar cada consulta.

Saludos cordiales,

Ming Xu.

+0

Eso ayuda un poco, pero ¿no tendría que preocuparme por las consultas que no se ejecutan hasta más adelante en el código donde están enumeradas? Por lo tanto, tendría que investigar realmente cada consulta y averiguar dónde se está llamando realmente a la base de datos. – PeteShack

+0

Lo siento, no te puedo entender muy bien. ¿Quiere decir que está preocupado si algo sale mal, será más difícil encontrar el origen del problema porque la consulta está dentro de un marco de reintento? En este caso, me gustaría sugerirle que establezca un punto de interrupción en retry.ExecuteAction, y avance a través del código. –

+0

No, solo quiero decir que agregar la lógica de reintento a una aplicación existente con cientos de consultas será una tarea difícil, principalmente porque la llamada a la base de datos no ocurre necesariamente en la consulta de linq a sql en la capa de datos. La ejecución de la consulta se puede retrasar hasta que se enumere IQueryable, lo que podría suceder en la capa empresarial. Simplemente no parece haber una solución limpia a este problema. – PeteShack

1

Después de tener que implementar algo como esto, continué y lo convertí en una biblioteca: https://github.com/daveaglick/LinqToSqlRetry (licencia MIT y disponible en NuGet).

Puede volver a intentarlo SubmitChanges() llamadas escribiendo SubmitChangesRetry() lugar:

using(var context = new MyDbContext()) 
{ 
    context.Items.InsertOnSubmit(new Item { Name = "ABC" }); 
    context.SubmitChangesRetry(); 
} 

También puede intentar las consultas utilizando el método Retry() extensión:

using(var context = new MyDbContext()) 
{ 
    int count = context.Items.Where(x => x.Name == "ABC").Retry().Count(); 
} 

La lógica de reintento específica es controlable por las políticas. Bajo el capó, el mecanismo de reintento se parece a:

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

Entender que la función de la llamada a func() y el objeto retryPolicy se proporcionan en función del uso. Esto solo te da una idea de lo que está sucediendo durante el ciclo de reintento. Solo mira en el repositorio para más información.

Cuestiones relacionadas