Actualizado. El problema no era la transacción del contexto de Entity Framework (Entity Framework no hace uso de transacciones para la consulta de datos si no se lo especificamos, aunque si para las actualizaciones (insert, update o delete)). El problema reside en que recorriendo la colección hay un Reader abierto y no permite la modificación de los datos del mismo. Gracias a Unai por las aclaraciones con las transacciones y el Reader en Entity Framework.
El problema nos surge cuando obtenemos una colección de entidades, las recorremos con un ForEach y realizamos alguna actualización de estas entidades o de otras dentro de este ForEach, realizando un SaveChanges para cada una de las entidades de la colección que estamos recorriendo.
1: using (var context = new ClientesModelContainer())
2: {
3: var empresaList = from p in context.EmpresaSet
4: select p;
5:
6: foreach (var empresa in empresaList)
7: {
8: empresa.Direccion.Calle = "Actualizacion Calle 1";
9: empresa.Nombre = "Nombre Empresa 1";
10:
11: Vehiculo vehiculo = new Vehiculo();
12: vehiculo.Marca = "Marca 1";
13: vehiculo.Modelo = "Modelo 1";
14:
15: context.AddToVehiculoSet(vehiculo);
16:
17: context.SaveChanges();
18: Console.WriteLine("Actualizada {0}", empresa.Nombre);
19: }
20:
21: Console.ReadLine();
22: }
El resultado de esta actualización es un EntityException que nos indica que se ha producido un error iniciando una transacción en el proveedor de conexión y con el siguiente mensaje de exepción ‘New transaction is not allowed because there are other threads running in the session’ (No están permitidas nuevas transacciones cuando existen otros hilos ejecutándose en la sesión).
Esto nos indica que cuando Entity Framework ejecuta el SaveChanges e intenta ejecutar actualizaciones sobre la conexión que está abierta por el ForEach, el proveedor SqlClient no se lo permite porque existe un Reader abierto y no es posible la actualización del mismo.
Veamos las posibles soluciones a este problemilla.
1. Ejecutar el SaveChanges fuera del ForEach y al final de todas las modificaciones. Esta solución nos pueda valer, siempre que nuestro código no necesite que se actualice la base de datos y se pueda esperar al final para realizar los cambios, el código quedaría tal como sigue.
1: using (var context = new ClientesModelContainer())
2: {
3: var empresaList = from p in context.EmpresaSet
4: select p;
5:
6: foreach (var empresa in empresaList)
7: {
8: empresa.Direccion.Calle = "Actualizacion Calle 1";
9: empresa.Nombre = "Nombre Empresa 1";
10:
11: Vehiculo vehiculo = new Vehiculo();
12: vehiculo.Marca = "Marca 1";
13: vehiculo.Modelo = "Modelo 1";
14: context.AddToVehiculoSet(vehiculo);
15:
16: Console.WriteLine("Actualizada {0}", empresa.Nombre);
17: }
18:
19: context.SaveChanges();
20:
21: Console.ReadLine();
2. Obtener la colección antes de recorrerla. Este es el método que prefiero, ya que obtenemos la colección de entidades en un Array o List para que no se mantenga abierto el Reader.
1: using (var context = new ClientesModelContainer())
2: {
3: var empresaList = (from p in context.EmpresaSet
4: select p).ToList();
5:
6: foreach (var empresa in empresaList)
7: {
8: empresa.Direccion.Calle = "Actualizacion Calle 1";
9: empresa.Nombre = "Nombre Empresa 1";
10:
11: Vehiculo vehiculo = new Vehiculo();
12: vehiculo.Marca = "Marca 1";
13: vehiculo.Modelo = "Modelo 1";
14:
15: context.AddToVehiculoSet(vehiculo);
16:
17: context.SaveChanges();
18: Console.WriteLine("Actualizada {0}", empresa.Nombre);
19: }
20:
21: Console.ReadLine();
22: }
3. Especificar la Transacción. Podemos especificar, haciendo uso del TransactionScope, la transacción de la conexión para delegar en este la actualización de los datos.
1: using (TransactionScope transactionScope = new TransactionScope())
2: {
3: using (var context = new ClientesModelContainer())
4: {
5: var empresaList = from p in context.EmpresaSet
6: select p;
7:
8: foreach (var empresa in empresaList)
9: {
10: empresa.Direccion.Calle = "Actualizacion Calle 1";
11: empresa.Nombre = "Nombre Empresa 1";
12:
13: Vehiculo vehiculo = new Vehiculo();
14: vehiculo.Marca = "Marca 1";
15: vehiculo.Modelo = "Modelo 1";
16:
17: context.AddToVehiculoSet(vehiculo);
18:
19: context.SaveChanges();
20: Console.WriteLine("Actualizada {0}", empresa.Nombre);
21: }
22:
23: Console.ReadLine();
24: }
25: }
Este código no suele ser el más empleado, ya que las aplicaciones que desarrollamos con Entity Framework se preparan para que soporten la desconexión de los contextos y de las entidades (n-tiers), pero puede convertirse en un dolor de cabeza, si recibimos este tipo de error que no es muy descriptivo.
Saludos a todos…