c# - transacciones - ¿Por qué no funciona TransactionScope con Entity Framework?
transaction entity framework 5 (8)
Vea el código a continuación. Si inicializo más de un contexto de entidad, obtendré la siguiente excepción solo en el segundo conjunto de códigos . Si comento el segundo conjunto funciona.
{"El proveedor subyacente falló en la apertura."}
Interno: {"La comunicación con el administrador de transacciones subyacente ha fallado."}
Interno: {"El error HRESULT E_FAIL se ha devuelto desde una llamada a un componente COM".}
Tenga en cuenta que esta es una aplicación de ejemplo y sé que no tiene sentido crear 2 contextos seguidos. Sin embargo, el código de producción tiene motivos para crear múltiples contextos en el mismo TransactionScope
, y esto no se puede cambiar.
Editar
Aquí hay una pregunta anterior de mí tratando de configurar MS-DTC. Parece estar habilitado tanto en el servidor como en el cliente. No estoy seguro de si está configurado correctamente. También tenga en cuenta que una de las razones por las que estoy tratando de hacer esto es que el código existente en TransactionScope
usa ADO.NET y Linq 2 Sql ... Me gustaría que también usen la misma transacción. (Eso probablemente suene loco, pero necesito hacerlo funcionar si es posible).
¿Cómo uso TransactionScope en C #?
Solución
Windows Firewall estaba bloqueando las conexiones a MS-DTC.
using(TransactionScope ts = new System.Transactions.TransactionScope())
{
using (DatabaseEntityModel o = new DatabaseEntityModel())
{
var v = (from s in o.Advertiser select s).First();
v.AcceptableLength = 1;
o.SaveChanges();
}
//-> By commenting out this section, it works
using (DatabaseEntityModel o = new DatabaseEntityModel())
{
//Exception on this next line
var v = (from s1 in o.Advertiser select s1).First(); v.AcceptableLength = 1;
o.SaveChanges();
}
//->
ts.Complete();
}
Agregue C: / Windows / msdtc.exe a las excepciones de firewall tanto en el firewall como en el servidor. Pasé las edades jugando en torno a la apertura de números de puertos específicos y rangos en vano antes de hacer esto.
El problema es que 2 DataContext diferentes crean efectivamente dos conexiones diferentes.
En ese caso, la transacción DEBE ser promovida a una transacción distribuida. Supongo que su problema proviene de la configuración de MS DTC (Coordinador de transacciones distribuidas de Microsoft) en el servidor o en el cliente. Si el servidor no está configurado para permitir conexiones remotas para MSDTC, por ejemplo, encontrará ese tipo de excepción.
puede consultar esta página de MS, por ejemplo, para solucionar problemas de MSDTC, y Google está repleto de artículos / foros sobre este tema.
Ahora, podría ser otra cosa, pero realmente parece que es un problema de MSDTC.
He escrito una respuesta en otra pregunta sobre cómo diagnosticar fallas en las transacciones de MSDTC.
Puede que encuentre útil esa respuesta.
Por cierto, debes considerar usar SaveChanges (false) en combinación con AcceptChanges () cuando usas transacciones explícitas como esta.
De esa forma, si algo falla en SaveChanges (falso), el ObjectContext no ha descartado sus cambios, por lo que puede volver a aplicar más tarde o hacer algún registro de errores, etc.
Consulte esta publicación para obtener más información: http://blogs.msdn.com/alexj/archive/2009/01/11/savechanges-false.aspx
Aclamaciones
Alex
Puede evitar usar una transacción distribuida administrando su propia EntityConnection y pasando esta EntityConnection a su ObjectContext. De lo contrario, echa un vistazo a estos.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=580828&SiteID=1&mode=1 http://forums.microsoft.com/msdn/showpost.aspx?postid=113669&siteid=1&sb=0&d=1&at=7&ft=11&tf=0&pageid=1
EntityConnection conn = new EntityConnection(ConnectionString);
using (TransactionScope ts = new TransactionScope())
{
using (DatabaseEntityModel o = new DatabaseEntityModel(conn))
{
var v = (from s in o.Advertiser select s).First();
v.AcceptableLength = 1;
}
//-> By commenting out this section, it works
using (DatabaseEntityModel o = new DatabaseEntityModel(conn))
{
//Exception on this next line
var v = (from s1 in o.Advertiser select s1).First();
v.AcceptableLength = 1;
}
//->
ts.Complete();
}
Su MS-DTC (Coordinador de transacciones distribuidas) no funciona correctamente por algún motivo. MS-DTC se utiliza para coordinar los resultados de transacciones a través de múltiples recursos heterogéneos, incluidas múltiples conexiones de SQL.
Eche un vistazo a este enlace para obtener más información sobre lo que está sucediendo.
Básicamente, si se asegura de que su MS-DTC se está ejecutando y funciona correctamente, no debería tener problemas con el uso de 2 conexiones ADO.NET, ya sean conexiones de entramado de entidades o cualquier otro tipo.
Tuve errores similares cuando usé DTC al leer mensajes de la cola de MQ, procesándolos y almacenándolos en la base de datos de SQL 2005 Express Edition. No tengo tiempo suficiente para investigar hasta el final si la edición de 2005 o la edición Express causó este problema, pero el cambio a la Norma 2008 desvaneció ese comportamiento en particular.
Voy a guardar esto aquí porque ayer pasé 3 horas con un colega para solucionar este problema. Cada respuesta que rodea esto dice que esto siempre es un problema de firewall; Sin embargo, en nuestro caso no lo fue. Esperemos que esto le ahorre el dolor a otra persona.
La situación que tenemos es que actualmente estamos en el proceso de migrar al Entity Framework. Eso significa que tenemos partes del código donde, dentro de una sola transacción, las conexiones se abren directamente usando un new SqlConnection(connectionString).Open()
e indirectamente usando un contexto de datos de EF.
Esto ha estado funcionando bien en nuestra aplicación por un tiempo, pero cuando comenzamos a ir retrospectivamente y pusimos a prueba el código que funcionaba en producción, el código ejecutado desde el corredor de prueba lanzó este error la primera vez que el objeto EF intentó conectarse. a la base de datos después de haber realizado una conexión directa en la misma transacción.
La causa del error resultó ser que si no proporciona un argumento Application Name=
a su cadena de conexión, el Entity Framework agrega uno por defecto (algo como EntityFrameworkMUF
). Esto significa que tiene dos conexiones distintas en su grupo de conexiones:
- El que abres manualmente sin
Application Name=
argumento - Un
Application Name=EntityFrameworkMUF
un sufijo generado automáticamenteApplication Name=EntityFrameworkMUF
y no es posible abrir dos conexiones distintas dentro de una sola transacción. El código de producción especificó un nombre de aplicación; de ahí funcionó; El código de prueba no lo hizo. Especificar el Application Name=
la Application Name=
argumento solucionó el error para nosotros.