SqlException de Entity Framework: no se permite una nueva transacción porque hay otros subprocesos ejecutándose en la sesión

Actualmente recibo este error:

System.Data.SqlClient.SqlException: No se permite una nueva transacción porque hay otros subprocesos ejecutándose en la sesión.

mientras ejecuta este código:

public class ProductManager : IProductManager
{
    #region Declare Models
    private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
    private RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
    #endregion

    public IProduct GetProductById(Guid productId)
    {
        // Do a quick sync of the feeds...
        SyncFeeds();
        ...
        // get a product...
        ...
        return product;
    }

    private void SyncFeeds()
    {
        bool found = false;
        string feedSource = "AUTO";
        switch (feedSource) // companyFeedDetail.FeedSourceTable.ToUpper())
        {
            case "AUTO":
                var clientList = from a in _dbFeed.Client.Include("Auto") select a;
                foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
                {
                    var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
                    foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
                    {
                        if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
                        {
                            var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
                            foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
                            {
                                foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
                                {
                                    if (targetProduct.alternateProductID == sourceProduct.AutoID)
                                    {
                                        found = true;
                                        break;
                                    }
                                }
                                if (!found)
                                {
                                    var newProduct = new RivWorks.Model.Negotiation.Product();
                                    newProduct.alternateProductID = sourceProduct.AutoID;
                                    newProduct.isFromFeed = true;
                                    newProduct.isDeleted = false;
                                    newProduct.SKU = sourceProduct.StockNumber;
                                    company.Product.Add(newProduct);
                                }
                            }
                            _dbRiv.SaveChanges();  // ### THIS BREAKS ### //
                        }
                    }
                }
                break;
        }
    }
}

Modelo n. ° 1: este modelo se encuentra en una base de datos en nuestro servidor de desarrollo. Model #1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png

Modelo n. ° 2: este modelo se encuentra en una base de datos en nuestro servidor de producción y se actualiza cada día mediante alimentaciones automáticas. alt text http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png

Nota: los elementos encerrados en un círculo rojo en el modelo n. ° 1 son los campos que utilizo para "asignar" al modelo n. ° 2. Por favor ignore los círculos rojos en el Modelo # 2: eso es de otra pregunta que tenía y que ahora está respondida.

Nota: Todavía necesito poner un cheque isDeleted para poder eliminarlo de DB1 si se ha salido del inventario de nuestro cliente.

Todo lo que quiero hacer, con este código en particular, es conectar una empresa en DB1 con un cliente en DB2, obtener su lista de productos de DB2 e INSERTARla en DB1 si aún no está allí. La primera vez debe ser un inventario completo. Cada vez que se ejecuta allí, no debe suceder nada, a menos que ingrese un nuevo inventario en el feed durante la noche.

Entonces, la gran pregunta: ¿cómo puedo resolver el error de transacción que recibo? ¿Necesito soltar y recrear mi contexto cada vez a través de los bucles (no tiene sentido para mí)?

preguntado el 21 de enero de 10 a las 19:01

Esta es la pregunta más detallada que he visto. -

¿Alguien ya no conoce los procedimientos almacenados? -

20 Respuestas

Después de arrancarme mucho el cabello, descubrí que el foreach los bucles fueron los culpables. Lo que debe suceder es llamar a EF pero devolverlo a un IList<T> de ese tipo de objetivo, luego haga un bucle en el IList<T>.

Ejemplo:

IList<Client> clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
   var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
    // ...
}

Respondido 01 Feb 10, 23:02

Sí, esto también me causó dolor de cabeza. ¡Casi me caigo de la silla cuando encontré el problema! Entiendo las razones técnicas detrás del problema, pero esto no es intuitivo y no ayuda al desarrollador a caer en el "pozo del éxito". blogs.msdn.com/brada/archive/2003/10/02/50420.aspx - Doctor jones

¿No es eso malo para el rendimiento de grandes conjuntos de datos? Si tiene millones de registros en la tabla. ToList () los absorberá todos en la memoria. Me estoy encontrando con este mismo problema y me preguntaba si lo siguiente sería factible: a) Separar la entidad b) Crear un nuevo ObjectContext y adjuntarle la entidad separada. c) Llamar a SaveChanges () en el nuevo ObjectContext d) Desasociar la entidad del nuevo ObjectContext e) Adjuntarla de nuevo al antiguo ObjectContext - Abhijeet Patel

El problema es que no puedes llamar SaveChanges mientras todavía está obteniendo resultados de la base de datos. Por lo tanto, otra solución es simplemente guardar los cambios una vez que se haya completado el ciclo. - Dibujó Noakes

Habiendo sido mordido también, agregué esto a Microsoft Connect: connect.microsoft.com/VisualStudio/feedback/details/612369/… No dudes en votarlo. - Ian Mercer

Nuestros desarrolladores tienden a agregar .ToList () a cualquier consulta LINQ sin pensar en las consecuencias. ¡Esta debe ser la primera vez que agregar .ToList () es realmente útil! - Marc

Como ya ha identificado, no puede guardar desde dentro de un foreach que todavía se extrae de la base de datos a través de un lector activo.

llamar ToList() or ToArray() está bien para conjuntos de datos pequeños, pero cuando tiene miles de filas, consumirá una gran cantidad de memoria.

Es mejor cargar las filas en trozos.

public static class EntityFrameworkUtil
{
    public static IEnumerable<T> QueryInChunksOf<T>(this IQueryable<T> queryable, int chunkSize)
    {
        return queryable.QueryChunksOfSize(chunkSize).SelectMany(chunk => chunk);
    }

    public static IEnumerable<T[]> QueryChunksOfSize<T>(this IQueryable<T> queryable, int chunkSize)
    {
        int chunkNumber = 0;
        while (true)
        {
            var query = (chunkNumber == 0)
                ? queryable 
                : queryable.Skip(chunkNumber * chunkSize);
            var chunk = query.Take(chunkSize).ToArray();
            if (chunk.Length == 0)
                yield break;
            yield return chunk;
            chunkNumber++;
        }
    }
}

Dados los métodos de extensión anteriores, puede escribir su consulta de esta manera:

foreach (var client in clientList.OrderBy(c => c.Id).QueryInChunksOf(100))
{
    // do stuff
    context.SaveChanges();
}

El objeto consultable al que llama a este método debe estar ordenado. Esto se debe a que Entity Framework solo admite IQueryable<T>.Skip(int) en consultas ordenadas, lo que tiene sentido si se considera que varias consultas para diferentes rangos requieren que el orden sea estable. Si el orden no es importante para usted, simplemente ordene por clave principal, ya que es probable que tenga un índice agrupado.

Esta versión consultará la base de datos en lotes de 100. Tenga en cuenta que SaveChanges() se llama para cada entidad.

Si desea mejorar drásticamente su rendimiento, debe llamar SaveChanges() poco frecuente. Use un código como este en su lugar:

foreach (var chunk in clientList.OrderBy(c => c.Id).QueryChunksOfSize(100))
{
    foreach (var client in chunk)
    {
        // do stuff
    }
    context.SaveChanges();
}

Esto da como resultado 100 veces menos llamadas de actualización de la base de datos. Por supuesto, cada una de esas llamadas tarda más en completarse, pero aún así, al final, saldrá muy por delante. Su kilometraje puede variar, pero esto fue mucho más rápido para mí.

Y evita la excepción que estaba viendo.

EDITAR Revisé esta pregunta después de ejecutar SQL Profiler y actualicé algunas cosas para mejorar el rendimiento. Para cualquiera que esté interesado, aquí hay un ejemplo de SQL que muestra lo que crea la base de datos.

El primer ciclo no necesita omitir nada, por lo que es más simple.

SELECT TOP (100)                     -- the chunk size 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM [dbo].[Clients] AS [Extent1]
ORDER BY [Extent1].[Id] ASC

Las llamadas posteriores deben omitir fragmentos anteriores de resultados, por lo que introduce el uso de row_number:

SELECT TOP (100)                     -- the chunk size
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM (
    SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], row_number()
    OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
    FROM [dbo].[Clients] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 100   -- the number of rows to skip
ORDER BY [Extent1].[Id] ASC

Respondido el 21 de diciembre de 10 a las 21:12

Gracias. Su explicación fue mucho más útil que la marcada como "Respondida". - Wagner Danda da Silva Filho

Esto es genial. solo una cosa: si está consultando en una columna y actualizando el valor de esa columna, debe estar atento al chunkNumber ++; . Digamos que tiene una columna "ModifiedDate" y está consultando .Where (x => x.ModifiedDate! = Null), y al final de foreach establece un valor para ModifiedDate. De esta manera, no está iterando la mitad de los registros porque la mitad de los registros se omiten. - Arvand

Desafortunadamente, en grandes conjuntos de datos obtendrá OutofMemoryException -ver explicación en Entity framework gran conjunto de datos, excepción de memoria insuficiente. He descrito cómo renovar su contexto en cada lote en SqlException de Entity Framework: no se permite una nueva transacción porque hay otros subprocesos ejecutándose en la sesión - miguel freidgeim

Creo que esto debería funcionar. var skip = 0; const int take = 100; Lista emps; while ((emps = db.Employees.Skip (skip) .Take (take) .ToList ()). Count> 0) {skip + = take; foreach (var emp in emps) {// Hacer cosas aquí}} Formularía esta respuesta, pero estaría enterrada debajo de las pilas de respuestas a continuación y se relaciona con esta pregunta. - disfrutar

¡Gracias! Para "SaveChange" después de "foreach" fue una solución simple en mi caso :) - Alexander S.

Ahora hemos publicado una respuesta oficial a el error se abrió en Connect. Las soluciones alternativas que recomendamos son las siguientes:

Este error se debe a que Entity Framework crea una transacción implícita durante la llamada SaveChanges (). La mejor manera de evitar el error es utilizar un patrón diferente (es decir, no guardar mientras se está leyendo) o declarar explícitamente una transacción. Aquí hay tres posibles soluciones:

// 1: Save after iteration (recommended approach in most cases)
using (var context = new MyContext())
{
    foreach (var person in context.People)
    {
        // Change to person
    }
    context.SaveChanges();
}

// 2: Declare an explicit transaction
using (var transaction = new TransactionScope())
{
    using (var context = new MyContext())
    {
        foreach (var person in context.People)
        {
            // Change to person
            context.SaveChanges();
        }
    }
    transaction.Complete();
}

// 3: Read rows ahead (Dangerous!)
using (var context = new MyContext())
{
    var people = context.People.ToList(); // Note that this forces the database
                                          // to evaluate the query immediately
                                          // and could be very bad for large tables.

    foreach (var person in people)
    {
        // Change to person
        context.SaveChanges();
    }
} 

contestado el 15 de mayo de 14 a las 12:05

Si toma la ruta de Transaction, es posible que simplemente lanzar un TransactionScope no lo solucione; no olvide extender el Tiempo de espera si lo que está haciendo podría llevar mucho tiempo, por ejemplo, si estará depurando interactivamente el código que genera el Llamada DB. Aquí está el código que extiende el tiempo de espera de la transacción a una hora: using (var transaction = new TransactionScope (TransactionScopeOption.Required, new TimeSpan (1, 0, 0))) - chris moschini

¡Me encontré con este error la primera vez que me desvié de la "ruta del tutorial" a un ejemplo real por mi cuenta! Para mí, sin embargo, la solución más simple, AHORRE DESPUÉS DE LA ITERACIÓN, ¡mejor! (Creo que el 99% de las veces este es el caso, y solo el 1% realmente DEBE realizar un guardado de la base de datos DENTRO del ciclo) - hombre araña

Bruto. Me acabo de encontrar con este error. Muy asqueroso. La segunda sugerencia funcionó como un encanto para mí junto con mover mis SaveChanges al ciclo. Pensé que guardar los cambios fuera del ciclo era mejor para los cambios por lotes. Pero esta bien. ¡¿Supongo que no?! :( - Señor joven

No funcionó para mí .NET 4.5. Cuando usé TransactionScope, recibí el siguiente error "El proveedor subyacente falló en EnlistTransaction. {" El administrador de transacciones del socio ha inhabilitado su soporte para transacciones remotas / de red. (Excepción de HRESULT: 0x8004D025) "}". Termino haciendo el trabajo fuera de la iteración. - Diganta Kumar

El uso de TransactionScope es peligroso, porque la tabla está bloqueada durante toda la transacción. - miguel freidgeim

De hecho, no puede guardar cambios dentro de un foreach bucle en C # usando Entity Framework.

context.SaveChanges() El método actúa como una confirmación en un sistema de base de datos regular (RDMS).

Simplemente haga todos los cambios (que Entity Framework almacenará en caché) y luego guárdelos todos a la vez llamando SaveChanges() después del bucle (fuera de él), como un comando de confirmación de la base de datos.

Esto funciona si puede guardar todos los cambios a la vez.

Respondido el 25 de junio de 18 a las 20:06

Pensé que era interesante ver el "sistema de base de datos regular (RDMS)" aquí: comedor

Esto parece incorrecto, ya que llamar repetidamente a SaveChanges está bien en el 90% de los contextos en EF. - pxtl

Parece que llamar repetidamente a SaveChanges está bien, a menos que el bucle foreach esté iterando sobre una entidad db. - kerbasaurio

¡Ajá! ¡Trae contexto dentro de cada ciclo! (pffft ... ¿en qué estaba pensando? ..) ¡Gracias! - Adam Cox

Sólo hay que poner context.SaveChanges() después del final de tu foreach(círculo).

Respondido 06 Jul 17, 13:07

Esta es la mejor opción que encontré en mi caso debido a guardar dentro de foreach: Almeida

Ésta no siempre es una opción. - pxtl

Haciendo sus listas consultables a .ToList () y debería funcionar bien.

Respondido 30 ago 18, 09:08

Proporcione un ejemplo en lugar de simplemente publicar una solución. - Ronnie Oosting

FYI: de un libro y algunas líneas ajustadas porque todavía es válido:

La invocación del método SaveChanges () inicia una transacción que revierte automáticamente todos los cambios persistentes en la base de datos si se produce una excepción antes de que se complete la iteración; de lo contrario, la transacción se confirma. Es posible que tenga la tentación de aplicar el método después de cada actualización o eliminación de entidad en lugar de después de que se complete la iteración, especialmente cuando está actualizando o eliminando cantidades masivas de entidades.

Si intenta invocar SaveChanges () antes de que se hayan procesado todos los datos, incurrirá en una excepción "No se permite una nueva transacción porque hay otros subprocesos ejecutándose en la sesión". La excepción se produce porque SQL Server no permite iniciar una nueva transacción en una conexión que tiene un SqlDataReader abierto, incluso con varios conjuntos de registros activos (MARS) habilitados por la cadena de conexión (la cadena de conexión predeterminada de EF habilita MARS)

A veces es mejor entender por qué suceden las cosas ;-)

Respondido el 13 de enero de 14 a las 15:01

Una buena forma de evitar esto es cuando tienes un lector abierto para abrir un segundo y poner esas operaciones en el segundo lector. Esto es algo que puede necesitar cuando esté actualizando maestro / detalles en el marco de la entidad. Abra la primera conexión para el registro maestro y la segunda para los registros de detalle. si solo está leyendo, no debería haber problemas. los problemas ocurren durante la actualización. - Herman van derblom

Explicación útil. tienes razón, es bueno entender por qué suceden las cosas. - molinero

Esta es solo la respuesta correcta de 20, a lo largo de múltiples preguntas de Stack Overflow. - N73k

Pero la única vez que esto me ha sucedido es cuando realmente involucra múltiples hilos. Un hilo está leyendo en la conexión mientras que otro hilo intenta hacer SaveChanges en la misma conexión (todo a través de Entity Framework, pero no creo que eso importe). - N73k

Sí, ese también fue mi caso. Leo un directorio LDAP completo en una base de datos todas las noches. Entonces, debido a que hay una gran cantidad de datos para importar, utilicé 254 subprocesos para manejar esto ... NET es rápido, pero las transacciones de base de datos no lo son, por lo que puede ayudar a este proceso a usar 254 subprocesos para esto. Hoy en día, esta es una forma de proceso estándar de importar muchos datos rápidamente. Si la importación es simple se puede hacer de otra manera pero en una transformación compleja de datos esto es perfecto. - Herman van derblom

Utilice siempre su selección como lista

P.ej:

var tempGroupOfFiles = Entities.Submited_Files.Where(r => r.FileStatusID == 10 && r.EventID == EventId).ToList();

Luego recorre la colección mientras guardas los cambios

 foreach (var item in tempGroupOfFiles)
             {
                 var itemToUpdate = item;
                 if (itemToUpdate != null)
                 {
                     itemToUpdate.FileStatusID = 8;
                     itemToUpdate.LastModifiedDate = DateTime.Now;
                 }
                 Entities.SaveChanges();

             }

Respondido el 19 de Septiembre de 17 a las 07:09

Esta no es una buena práctica en absoluto. No debe ejecutar SaveChanges con tanta frecuencia si no es necesario, y definitivamente no debe "Usar siempre su selección como lista" - comedor

@Dinerdo realmente depende del escenario. En mi caso, tengo 2 bucles foreach. El exterior tenía la consulta db como lista. Por ejemplo, este foreach atraviesa dispositivos de hardware. El foreach interno recupera varios datos de cada dispositivo. Según el requisito, necesito guardar en la base de datos los datos después de recuperarlos de cada dispositivo uno por uno. No es una opción guardar todos los datos al final del proceso. Encontré el mismo error pero la solución de mzonerz funcionó. - jstuardo

@jstuardo ¿Incluso con el procesamiento por lotes? - comedor

@Dinerdo Estoy de acuerdo en que no es una buena práctica a nivel filosófico. Sin embargo, existen varias situaciones en las que dentro del bucle for el código llama a otro método (digamos un método AddToLog ()) que incluye una llamada a db.SaveChanges () localmente. En esta situación, realmente no puede controlar la llamada a db.Save Changes. En este caso, el uso de ToList () o una estructura similar funcionará como lo sugiere mzonerz. ¡Gracias! - A. Varma

En la práctica, esto le hará más daño de lo que le ayudará. Me atengo a lo que dije: ToList () definitivamente no debería usarse todo el tiempo, y guardar los cambios después de cada elemento es algo que debe evitarse siempre que sea posible en una aplicación de alto rendimiento. Esta sería una solución temporal en mi opinión. Cualquiera que sea el método de registro que tenga, idealmente también debería aprovechar el almacenamiento en búfer. - comedor

Estaba teniendo el mismo problema pero en una situación diferente. Tenía una lista de elementos en un cuadro de lista. El usuario puede hacer clic en un elemento y seleccionar eliminar, pero estoy usando un proceso almacenado para eliminar el elemento porque hay mucha lógica involucrada en la eliminación del elemento. Cuando llamo al proceso almacenado, la eliminación funciona bien, pero cualquier llamada futura a SaveChanges provocará el error. Mi solución fue llamar al proceso almacenado fuera de EF y esto funcionó bien. Por alguna razón, cuando llamo al proceso almacenado usando la forma EF de hacer las cosas, deja algo abierto.

contestado el 25 de mayo de 11 a las 01:05

Tuve un problema similar recientemente: la razón en mi caso fue SELECT declaración en el procedimiento almacenado que produjo un conjunto de resultados vacío y si ese conjunto de resultados no se leyó, SaveChanges lanzó esa excepción. - n0.

Lo mismo con el resultado no leído de SP, muchas gracias por la pista) - pavel k

Aquí hay otras 2 opciones que le permiten invocar SaveChanges () en un para cada bucle.

La primera opción es usar un DBContext para generar sus objetos de lista para iterar, y luego crear un segundo DBContext para llamar a SaveChanges (). Aquí hay un ejemplo:

//Get your IQueryable list of objects from your main DBContext(db)    
IQueryable<Object> objects = db.Object.Where(whatever where clause you desire);

//Create a new DBContext outside of the foreach loop    
using (DBContext dbMod = new DBContext())
{   
    //Loop through the IQueryable       
    foreach (Object object in objects)
    {
        //Get the same object you are operating on in the foreach loop from the new DBContext(dbMod) using the objects id           
        Object objectMod = dbMod.Object.Find(object.id);

        //Make whatever changes you need on objectMod
        objectMod.RightNow = DateTime.Now;

        //Invoke SaveChanges() on the dbMod context         
        dbMod.SaveChanges()
    }
}

La segunda opción es obtener una lista de objetos de la base de datos del DBContext, pero seleccionar solo los identificadores. Y luego iterar a través de la lista de identificadores (presumiblemente un int) y obtener el objeto correspondiente a cada int, e invocar SaveChanges () de esa manera. La idea detrás de este método es tomar una gran lista de enteros, es mucho más eficiente que obtener una gran lista de objetos db y llamar a .ToList () en todo el objeto. A continuación, se muestra un ejemplo de este método:

//Get the list of objects you want from your DBContext, and select just the Id's and create a list
List<int> Ids = db.Object.Where(enter where clause here)Select(m => m.Id).ToList();

var objects = Ids.Select(id => db.Objects.Find(id));

foreach (var object in objects)
{
    object.RightNow = DateTime.Now;
    db.SaveChanges()
}

Respondido 26 ago 16, 14:08

Esta es una gran alternativa que pensé e hice, pero debe ser votada a favor. Nota: i) puede iterar como enumerable, lo que es bueno para conjuntos muy grandes; ii) Puede usar el comando NoTracking para evitar problemas al cargar tantos registros (si ese es su escenario); iii) También me gusta mucho la opción de solo clave primaria; es muy inteligente porque está cargando muchos menos datos en la memoria, pero no está tratando con Take / Skip en un conjunto de datos subyacente potencialmente dinámico. - Todd

Si obtiene este error debido a foreach y realmente necesita guardar una entidad primero dentro del bucle y usar la identidad generada más en el bucle, como fue en mi caso, la solución más fácil es usar otro DBContext para insertar la entidad que devolverá Id y usará este Id en contexto externo

Por ejemplo:

    using (var context = new DatabaseContext())
    {
        ...
        using (var context1 = new DatabaseContext())
        {
            ...
               context1.SaveChanges();
        }                         
        //get id of inserted object from context1 and use is.   
      context.SaveChanges();
   }

Respondido el 15 de enero de 18 a las 17:01

Empezamos a ver este error "No se permite una nueva transacción porque hay otros hilos en ejecución en la sesión" después de migrar de EF5 a EF6.

Google nos trajo aquí pero no estamos llamando SaveChanges() dentro del bucle. Los errores surgieron al ejecutar un procedimiento almacenado utilizando ObjectContext.ExecuteFunction dentro de una lectura de bucle foreach desde la base de datos.

Cualquier llamada a ObjectContext.ExecuteFunction envuelve la función en una transacción. Comenzar una transacción cuando ya hay un lector abierto provoca el error.

Es posible deshabilitar la envoltura del SP en una transacción configurando la siguiente opción.

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

La EnsureTransactionsForFunctionsAndCommands La opción permite que el SP se ejecute sin crear su propia transacción y ya no se genera el error.

Propiedad DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands

contestado el 21 de mayo de 20 a las 12:05

Entonces, en el proyecto donde tuve exactamente el mismo problema, el problema no estaba en el foreach o .toList() en realidad estaba en la configuración de AutoFac que usamos. Esto creó algunas situaciones extrañas en las que se lanzó el error anterior, pero también se lanzaron un montón de otros errores equivalentes.

Esta fue nuestra solución: cambió esto:

container.RegisterType<DataContext>().As<DbContext>().InstancePerLifetimeScope();
container.RegisterType<DbFactory>().As<IDbFactory>().SingleInstance();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();

A:

container.RegisterType<DataContext>().As<DbContext>().As<DbContext>();
container.RegisterType<DbFactory>().As<IDbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().As<IUnitOfWork>();//.InstancePerRequest();

Respondido 21 Jul 15, 14:07

¿Podrías explicar cuál crees que fue el problema? resolvió esto creando un nuevo Dbcontext cada vez? - eran otzap

Sé que es una vieja pregunta, pero hoy me enfrenté a este error.

y descubrí que este error se puede producir cuando un disparador de la tabla de la base de datos recibe un error.

para su información, también puede verificar los activadores de sus tablas cuando reciba este error.

respondido 24 nov., 17:13

Necesitaba leer un ResultSet enorme y actualizar algunos registros en la tabla. Traté de usar trozos como se sugiere en Dibujó Noakes's https://www.youtube.com/watch?v=xB-eutXNUMXJtA&feature=youtu.be.

Desafortunadamente, después de 50000 registros, obtuve OutofMemoryException. La respuesta Entity framework gran conjunto de datos, excepción de memoria insuficiente explica que

EF crea una segunda copia de datos que se utiliza para la detección de cambios (para que pueda conservar los cambios en la base de datos). EF mantiene este segundo conjunto durante la vida útil del contexto y es este conjunto el que te está quedando sin memoria.

La recomendación es volver a crear su contexto para cada lote.

Así que recuperé los valores Mínimo y Máximo de la clave principal; las tablas tienen claves primarias como enteros incrementales automáticos. Luego, recuperé de la base de datos fragmentos de registros abriendo el contexto para cada fragmento. Después de procesar, el contexto del fragmento se cierra y libera la memoria. Asegura que el uso de la memoria no aumente.

A continuación se muestra un fragmento de mi código:

  public void ProcessContextByChunks ()
  {
        var tableName = "MyTable";
         var startTime = DateTime.Now;
        int i = 0;
         var minMaxIds = GetMinMaxIds();
        for (int fromKeyID= minMaxIds.From; fromKeyID <= minMaxIds.To; fromKeyID = fromKeyID+_chunkSize)
        {
            try
            {
                using (var context = InitContext())
                {   
                    var chunk = GetMyTableQuery(context).Where(r => (r.KeyID >= fromKeyID) && (r.KeyID < fromKeyID+ _chunkSize));
                    try
                    {
                        foreach (var row in chunk)
                        {
                            foundCount = UpdateRowIfNeeded(++i, row);
                        }
                        context.SaveChanges();
                    }
                    catch (Exception exc)
                    {
                        LogChunkException(i, exc);
                    }
                }
            }
            catch (Exception exc)
            {
                LogChunkException(i, exc);
            }
        }
        LogSummaryLine(tableName, i, foundCount, startTime);
    }

    private FromToRange<int> GetminMaxIds()
    {
        var minMaxIds = new FromToRange<int>();
        using (var context = InitContext())
        {
            var allRows = GetMyTableQuery(context);
            minMaxIds.From = allRows.Min(n => (int?)n.KeyID ?? 0);  
            minMaxIds.To = allRows.Max(n => (int?)n.KeyID ?? 0);
        }
        return minMaxIds;
    }

    private IQueryable<MyTable> GetMyTableQuery(MyEFContext context)
    {
        return context.MyTable;
    }

    private  MyEFContext InitContext()
    {
        var context = new MyEFContext();
        context.Database.Connection.ConnectionString = _connectionString;
        //context.Database.Log = SqlLog;
        return context;
    }

DesdeAlRango es una estructura simple con propiedades From y To.

Respondido 29 Abr '20, 23:04

No pude ver cómo estabas "renovando" tu contexto. Parece que simplemente está creando un nuevo contexto para cada fragmento. - Suncat2000

@ Suncat2000, tienes razón, el contexto debería ser un objeto de corta duración stackoverflow.com/questions/43474112/… - miguel freidgeim

También estaba enfrentando el mismo problema.

Aquí está la causa y la solución.

http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx

Antes de ejecutar comandos de manipulación de datos, como inserciones, actualizaciones, asegúrese de haber cerrado todos los lectores SQL activos anteriores.

El error más común son las funciones que leen datos de db y devuelven valores. Por ejemplo, funciones como isRecordExist.

En este caso volvemos inmediatamente de la función si encontramos el registro y nos olvidamos de cerrar el lector.

respondido 10 mar '11, 11:03

¿Qué significa "cerrar un lector" en Entity Framework? No hay un lector visible en una consulta como var result = from customer en myDb.Customers donde customer.Id == customerId select customer; return result.FirstOrDefault (); - Antonio

@Anthony Como dicen otras respuestas, si usa EF para enumerar sobre una consulta LINQ (IQueryable), el DataReader subyacente permanecerá abierto hasta que se repita la última fila. Pero aunque MARS es una característica importante para habilitar en una cadena de conexión, el problema en el OP aún no se resuelve solo con MARS. El problema es intentar SaveChanges mientras un DataReader subyacente aún está abierto. - Todd

El siguiente código funciona para mí:

private pricecheckEntities _context = new pricecheckEntities();

...

private void resetpcheckedtoFalse()
{
    try
    {
        foreach (var product in _context.products)
        {
            product.pchecked = false;
            _context.products.Attach(product);
            _context.Entry(product).State = EntityState.Modified;
        }
        _context.SaveChanges();
    }
    catch (Exception extofException)
    {
        MessageBox.Show(extofException.ToString());

    }
    productsDataGrid.Items.Refresh();
}

respondido 25 mar '14, 19:03

¡Bienvenido a SO! Considere agregar una explicación y / o enlaces que describan el porqué esto funciona para ti. Las respuestas de solo código generalmente no se consideran de buena calidad para SO. - codigomagia

En mi caso, el problema apareció cuando llamé al Procedimiento almacenado a través de EF y luego SaveChanges lanzó esta excepción. El problema estaba en llamar al procedimiento, el enumerador no se eliminó. Arreglé el código de la siguiente manera:

public bool IsUserInRole(string username, string roleName, DataContext context)
{          
   var result = context.aspnet_UsersInRoles_IsUserInRoleEF("/", username, roleName);

   //using here solved the issue
   using (var en = result.GetEnumerator()) 
   {
     if (!en.MoveNext())
       throw new Exception("emty result of aspnet_UsersInRoles_IsUserInRoleEF");
     int? resultData = en.Current;

     return resultData == 1;//1 = success, see T-SQL for return codes
   }
}

Respondido el 26 de Septiembre de 15 a las 16:09

Llego mucho tarde a la fiesta pero hoy me enfrenté al mismo error y la forma en que lo resolví fue sencilla. Mi escenario era similar a este código dado que estaba haciendo transacciones de base de datos dentro de los bucles anidados para cada uno.

El problema es que una transacción de base de datos única lleva un poco más de tiempo que para cada bucle, por lo que una vez que la transacción anterior no se completa, la nueva tracción genera una excepción, por lo que la solución es crear un nuevo objeto en el bucle for-each. donde está realizando una transacción de base de datos.

Para los escenarios mencionados anteriormente, la solución será así:

foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
                {
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
                    if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
                    {
                        var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
                        foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
                        {
                            foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
                            {
                                if (targetProduct.alternateProductID == sourceProduct.AutoID)
                                {
                                    found = true;
                                    break;
                                }
                            }
                            if (!found)
                            {
                                var newProduct = new RivWorks.Model.Negotiation.Product();
                                newProduct.alternateProductID = sourceProduct.AutoID;
                                newProduct.isFromFeed = true;
                                newProduct.isDeleted = false;
                                newProduct.SKU = sourceProduct.StockNumber;
                                company.Product.Add(newProduct);
                            }
                        }
                        _dbRiv.SaveChanges();  // ### THIS BREAKS ### //
                    }
                }

Respondido el 03 de junio de 16 a las 13:06

Llegué un poco tarde, pero también tuve este error. Resolví el problema comprobando qué estaban los valores que se estaban actualizando.

Descubrí que mi consulta era incorrecta y que había más de 250 ediciones pendientes. Así que corrigí mi consulta y ahora funciona correctamente.

Entonces en mi situación: Verifique la consulta en busca de errores, depurando el resultado que devuelve la consulta. Después de eso, corrija la consulta.

Espero que esto ayude a resolver problemas futuros.

respondido 08 nov., 16:12

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.