.net - net - Manejo de fallas transitorias con SQL Azure utilizando Entity Framework
deploy web api to azure (5)
Afortunadamente, es bastante simple con el nuevo bloque de aplicación de manejo de fallas transitorias. Todo lo que necesitas se puede encontrar aquí:
y en forma de video:
http://channel9.msdn.com/Shows/Cloud+Cover/Episode-68-Throttling-in-SQL-Azure-with-Scott-Klein
Un ejemplo de los enlaces anteriores:
using (NorthwindEntities dc = new NorthwindEntities())
{
RetryPolicy myPolicy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(3);
Employee e1 = myPolicy.ExecuteAction<Employee>(() =>
(from x in dc.Employees
where x.LastName == "King"
select x).First());
}
Como puede ver, todo lo que necesita hacer es crear una RetryPolicy y llamar a su función ExecuteAction con una consulta envuelta en una acción.
* * EDITAR
Ejemplo de reemplazo de contexto:
private RetryPolicy m_RetryPolicy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(....
public override int SaveChanges()
{
return m_RetryPolicy.ExecuteAction<int>(() =>
{
return base.SaveChanges();
});
}
// Pass anonymous query func in here
public T AutoRetryQuery<T>(Func<T> query)
{
return m_RetryPolicy.ExecuteAction<T>(query);
}
Actualmente utilizo SQL Azure y Entity SQL en mi aplicación.
p.ej
Entities model = new Entities();
db_Item item = model.db_Item.First();
Ahora quiero usar el manejo de fallas transitorias fuera de Enterprise Library, pero no hay ejemplos o soluciones que pueda encontrar que me permitan hacer algo como anular la clase Entidades, por lo que no tengo que actualizar mi código en cientos. de lugares.
¿Podría alguien proporcionar más información sobre cómo podría hacerse esto?
En realidad, usted desea utilizar los métodos de extensión proporcionados por MS. Ref: http://msdn.microsoft.com/en-us/library/hh680906(v=pandp.50).aspx
"Además, las clases SqlCommandExtensions y SqlConnectionExtensions proporcionan un conjunto de métodos de extensión que permiten a los desarrolladores de .NET abrir conexiones de base de datos de SQL Azure e invocar los comandos SQL. Estos métodos de extensión son útiles en caso de que no pueda adaptar su código. ventaja de la clase ReliableSqlConnection. Por ejemplo, puede usar el Bloque de aplicaciones de acceso a datos de Enterprise Library o Entity Framework que usa instancias de SqlConnection internamente. En este caso, los métodos de extensión le ayudan a agregar las capacidades de reintento que ofrece el Bloque de aplicaciones de manejo de fallas transitorias. al código existente sin mayor re-trabajo.
"
Pasando por lo que tengo hasta ahora.
Entity Framework no proporciona acceso a la conexión abierta ni a la sección donde se envía el SQL al servidor, por lo que actualmente es imposible proporcionar una lógica de reintento alrededor de esta área.
El equipo de EF es consciente de este déficit y planea integrar realmente la lógica de reintento en EF para posiblemente la versión 6.
Según el Caso # 3 de [1] puede enviar un comando SQL a la base de datos en OnContextCreated. Sin embargo, esto significa que para TODAS las llamadas de base de datos individuales que realice a la base de datos, deberá realizar 2. No lo recomendaría en casi ninguna situación a menos que no le importe el rendimiento.
La única opción viable hasta ahora es implementar la lógica de reintento en la forma del Bloque de aplicación de manejo de fallas transitorias de Enterprise Library [2] en cada llamada a la base de datos. En aplicaciones existentes esto es extremadamente tedioso.
Cuando tengo tiempo, estoy buscando más en el código fuente de EF para ver si se puede hacer algo más, mientras esperamos a EF 6. Me gustaría echar un vistazo a [3]
Algo de esperanza, actualmente está siendo revisado por el equipo de EF. [4]
Actualización: 2013-11-14
Simplemente pensé que actualizaría esta publicación para que todos sepan que EF6 se ha lanzado y es compatible con la capacidad de conexión de forma inmediata. https://www.nuget.org/packages/EntityFramework/
No hay necesidad de soluciones más.
Actualización: 2013-03-23
Se lanzó EF 6 Alpha 3 con resistencia de conexión - http://entityframework.codeplex.com/wikipage?title=Connection%20Resiliency%20Spec
Actualización: 2012-11-04
El equipo de EF ha anunciado oficialmente que está previsto para EF 6. [4]
[2] http://msdn.microsoft.com/en-us/library/hh680934(v=pandp.50).aspx
[3] http://entityframework.codeplex.com/wikipage?title=Roadmap
También tuve el mismo problema. En mi caso, estoy usando LINQ-to-SQL (creo que lo mismo se aplica al marco de Entity también) y esto es lo que planeo hacer.
Resuma las consultas de Linq-to-sql usando un patrón de repositorio y ajuste el código de Linq-to-sql que genera la solicitud de base de datos con el mecanismo de reintento.
Para eso sugeriría crear un método llamado ExecuteWithRetry(Action action)
y luego invocar este método como este ExecuteWithRetry(()=> { db_Item item = model.db_Item.First(); });
El método podría ser implementado de esta manera.
private void ExecuteWithRetry(Action action)
{
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2));
// Define your retry policy using the retry strategy and the Windows Azure storage
// transient fault detection strategy.
var retryPolicy =
new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);
// Receive notifications about retries.
retryPolicy.Retrying += (sender, args) =>
{
// Log details of the retry.
var msg = String.Format("Retry - Count:{0}, Delay:{1}, Exception:{2}",
args.CurrentRetryCount, args.Delay, args.LastException);
Trace.WriteLine(msg, "Information");
};
try
{
this.retryPolicy.ExecuteAction(action);
}
catch (Exception ex)
{
Trace.TraceError(ex.TraceInformation());
throw;
}
}
También voy a probar este enfoque, esperando que funcione.
Ya que esta parece una de las preguntas más populares sobre SO sobre el manejo de transitorios de Azure, agregaré esta respuesta aquí.
Entity Framework sí tiene un código de resiliencia incorporado (según la respuesta de Adam)
PERO:
1) Debes agregar código para activarlo, manualmente.
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
this.SetExecutionStrategy(
"System.Data.SqlClient",
() => new SqlAzureExecutionStrategy());
this.SetTransactionHandler(
SqlProviderServices.ProviderInvariantName,
() => new CommitFailureHandler());
}
...
La primera llamada de método activa los reintentos, la segunda llamada establece un manejador para evitar actualizaciones duplicadas cuando ocurren los reintentos.
Nota: esta clase se encontrará y se creará una instancia automáticamente, como se explica aquí: https://msdn.microsoft.com/en-us/library/jj680699(v=vs.113).aspx . Simplemente asegúrese de que la clase esté en el mismo ensamblaje que su clase DbContext y tenga un constructor público sin parámetros.
2) La SqlAzureExecutionStrategy incorporada no es lo suficientemente buena . No cubre todos los errores transitorios. Esto no es sorprendente cuando se considera que el equipo de SQL Server está trabajando independientemente de Entity Framework, por lo que es poco probable que alguna vez estén completamente sincronizados con los posibles errores transitorios. También es difícil darse cuenta de eso.
La solución que utilizamos, respaldada por una sugerencia de otra compañía de software, es crear nuestra propia estrategia de ejecución, que reintenta cada excepción SqlException y TimeoutException, excepto algunas que no vale la pena reintentar (como permiso denegado).
public class WhiteListSqlAzureExecutionStrategy : DbExecutionStrategy
{
public WhiteListSqlAzureExecutionStrategy()
{
}
protected override bool ShouldRetryOn(Exception exception)
{
var sqlException = exception as SqlException;
// If this is an SqlException then we want to always retry
// Unless the all the exception types are in the white list.
// With those errors there is no point in retrying.
if (sqlException != null)
{
var retry = false;
foreach (SqlError err in sqlException.Errors)
{
// Exception white list.
switch (err.Number)
{
// Primary Key violation
// https://msdn.microsoft.com/en-us/library/ms151757(v=sql.100).aspx
case 2627:
// Constraint violation
case 547:
// Invalid column name, We have seen this happen when the Snapshot helper runs for a column ''CreatedOn''
// This is not one of our columns and it appears to be using our execution strategy.
// An invalid column is also something that probably doesn''t get resolved by retries.
case 207:
break;
// The server principal "username" is not able to access the database "dbname" under the current security context
// May occur when using restricted user - Entity Framework wants to access master for something
// probably not transient
case 916:
break;
// XXX permission denied on object. (XXX = select, etc)
// Should not occur if db access is correct, but occurred when using restricted user - EF accessing __MigrationHistory
case 229:
break;
// Invalid object name ''xxx''.
// Occurs at startup because Entity Framework looks for EdmMetadata, an old table
// (Perhaps only if it can''t access __MigrationHistory?)
case 208:
break;
default:
retry = true;
break;
}
}
return retry;
}
if (exception is TimeoutException)
{
return true;
}
return false;
}
}
3) Hay un tipo de error en el que EF ejecuta los reintentos N ^ 2 veces en lugar de N, lo que provoca demoras mucho más largas de lo que cabría esperar. Se supone que debe tardar hasta 26 segundos, pero el error hace que tarde unos minutos. Sin embargo, esto no es tan malo porque en realidad SQL Azure regularmente no está disponible por más de un minuto :( https://entityframework.codeplex.com/workitem/2849
4) Si aún no lo ha hecho, realmente necesita deshacerse de su DbContext después de su uso. Parece que este es el punto en el que CommitFailureHandler ejecuta su purga para ordenar la tabla __TransactionHistory; Si no dispone, esta tabla crecerá para siempre (aunque vea el siguiente punto).
5) Probablemente debería llamar a ClearTransactionHistory en algún lugar de su inicio o en un hilo en segundo plano, para borrar cualquier resto en __TransactionHistory.