ChatGPT解决这个技术问题 Extra ChatGPT

SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session

I am currently getting this error:

System.Data.SqlClient.SqlException: New transaction is not allowed because there are other threads running in the session.

while running this code:

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

Model #1 - This model sits in a database on our Dev Server. Model #1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png

Model #2 - This model sits in a database on our Prod Server and is updated each day by automatic feeds. alt text http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png

Note - The red circled items in Model #1 are the fields I use to "map" to Model #2. Please ignore the red circles in Model #2: that is from another question I had which is now answered.

Note: I still need to put in an isDeleted check so I can soft delete it from DB1 if it has gone out of our client's inventory.

All I want to do, with this particular code, is connect a company in DB1 with a client in DB2, get their product list from DB2 and INSERT it in DB1 if it is not already there. First time through should be a full pull of inventory. Each time it is run there after nothing should happen unless new inventory came in on the feed over night.

So the big question - how to I solve the transaction error I am getting? Do I need to drop and recreate my context each time through the loops (does not make sense to me)?

This is the most detailed question i've ever seen.
Anybody misses stored procedures yet ?

J
John Saunders

After much pulling out of hair I discovered that the foreach loops were the culprits. What needs to happen is to call EF but return it into an IList<T> of that target type then loop on the IList<T>.

Example:

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;
    // ...
}

Yeah, this caused me a headache too. I almost fell off my chair when I found the problem! I understand the technical reasons behind the problem, but this isn't intuitive and it isn't helping the developer to fall into the "pit of success" blogs.msdn.com/brada/archive/2003/10/02/50420.aspx
Isn't that bad for performance for large datasets? If you have a millions records in the table. ToList() will suck them all into memory. I'm running into this very problem and was wondering whether the following would be feasible a)Detach the entity b)Create a new ObjectContext and attach the detached entity to it. c)Call SaveChanges() on the new ObjectContext d)Detach the entity from the new ObjectContext e)Attach it back to the old ObjectContext
The issue is that you can't call SaveChanges while you're still pulling results from the DB. Therefore another solution is just to save changes once the loop has completed.
Having been bitten also, I added this to Microsoft Connect: connect.microsoft.com/VisualStudio/feedback/details/612369/… Feel free to vote it up.
Our devs tend to append .ToList() to any LINQ query without thinking about the consequences. This must be the first time appending .ToList() is really useful!
D
Drew Noakes

As you've already identified, you cannot save from within a foreach that is still drawing from the database via an active reader.

Calling ToList() or ToArray() is fine for small data sets, but when you have thousands of rows, you will be consuming a large amount of memory.

It's better to load the rows in chunks.

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

Given the above extension methods, you can write your query like this:

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

The queryable object you call this method on must be ordered. This is because Entity Framework only supports IQueryable<T>.Skip(int) on ordered queries, which makes sense when you consider that multiple queries for different ranges require the ordering to be stable. If the ordering isn't important to you, just order by primary key as that's likely to have a clustered index.

This version will query the database in batches of 100. Note that SaveChanges() is called for each entity.

If you want to improve your throughput dramatically, you should call SaveChanges() less frequently. Use code like this instead:

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

This results in 100 times fewer database update calls. Of course each of those calls takes longer to complete, but you still come out way ahead in the end. Your mileage may vary, but this was worlds faster for me.

And it gets around the exception you were seeing.

EDIT I revisited this question after running SQL Profiler and updated a few things to improve performance. For anyone who is interested, here is some sample SQL that shows what is created by the DB.

The first loop doesn't need to skip anything, so is simpler.

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

Subsequent calls need to skip previous chunks of results, so introduces usage of 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

Thanks. Your explanation was much more useful than the one marked as "Answered".
This is great. just one thing: If you're querying on a column and updating the value of that column, you need to be ware of the chunkNumber++; . Let's say you have a column "ModifiedDate" and you're querying .Where(x=> x.ModifiedDate != null), and at the end of foreach you set a value for ModifiedDate. In this way you're not iterating half of the records because half of the records are getting skipped.
Unfortunately on huge datasets you'll got OutofMemoryException -see explanation in Entity framework large data set, out of memory exception. I've described how to renew your context each batch in SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session
I think this should work. var skip = 0; const int take = 100; List emps ; while ((emps = db.Employees.Skip(skip).Take(take).ToList()).Count > 0) { skip += take; foreach (var emp in emps) { // Do stuff here } } I would formulate this an answer but it would be buried below the piles of answers below and it relates to this question.
Thank you! To "SaveChange" after "foreach" was a simple solution in my case :)
d
d219

We have now posted an official response to the bug opened on Connect. The workarounds we recommend are as follows:

This error is due to Entity Framework creating an implicit transaction during the SaveChanges() call. The best way to work around the error is to use a different pattern (i.e., not saving while in the midst of reading) or by explicitly declaring a transaction. Here are three possible solutions:

// 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();
    }
} 

If you take the Transaction route, just throwing in a TransactionScope might not fix it - don't forget to extend the Timeout if what you're doing could take a long time - for example if you'll be interactively debugging the code making the DB call. Here's code extending the transaction timeout to an hour: using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0)))
I've bumped into this error the very first time I have digressed from the "tutorial path" into a real example on my own! For me, however, the simpler solution, SAVE AFTER ITERATION, the better! (I think 99% of the times this is the case, and only 1% really MUST perform a database save INSIDE the loop)
Gross. I just bumped into this error. Very nasty. The 2nd suggestion worked like a charm for me along with moving my SaveChanges into the loop. I thought having save changes outside of the loop was better for batching changes. But alright. I guess not?! :(
Did not work for me .NET 4.5. When used the TransactionScope I got the following error "The underlying provider failed on EnlistTransaction.{"The partner transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D025)"}". I end up doing the job outside the iteration.
Using TransactionScope is dangerous, because table is locked for the time of the whole transaction.
P
Paul Roub

Indeed you cannot save changes inside a foreach loop in C# using Entity Framework.

context.SaveChanges() method acts like a commit on a regular database system (RDMS).

Just make all changes (which Entity Framework will cache) and then save all of them at once calling SaveChanges() after the loop (outside of it), like a database commit command.

This works if you can save all changes at once.


I thought it was interesting to see "regular database system (RDMS)" here
This seems wrong, since repeatedly calling SaveChanges is fine in 90% of contexts in EF.
It seems as though repeatedly calling SaveChanges is fine, unless the foreach loop is iterating over a db Entity.
Aha! Bring context inside for-each loop! (pffft... what was I thinking?..) Thanks!
M
Majid

Just put context.SaveChanges() after end of your foreach(loop).


This is the better option I found out in my case due to save inside foreach
This is not always an option.
W
Wojciech Seweryn

Making your queryable lists to .ToList() and it should work fine.


Please provide an example instead of just posting a solution.
d
d219

FYI: from a book and some lines adjusted because it's still valid:

Invoking SaveChanges() method begins a transaction which automatically rolls back all changes persisted to the database if an exception occurs before iteration completes; otherwise the transaction commits. You might be tempted to apply the method after each entity update or deletion rather than after iteration completes, especially when you're updating or deleting massive numbers of entities.

If you try to invoke SaveChanges() before all data has been processed, you incur a "New transaction is not allowed because there are other threads running in the session" exception. The exception occurs because SQL Server doesn't permit starting a new transaction on a connection that has a SqlDataReader open, even with Multiple Active Record Sets (MARS) enabled by the connection string (EF's default connection string enables MARS)

Sometimes its better to understand why things are happening ;-)


A good way to avoid this is when you have a reader open to open a second one and put those operations in the second reader. This is something that you can need when you are updating master/details in the entity framework. You open the first connection for the master record and the second for the detail records. if you are only reading there should be no problems. the problems occur during updating.
Helpful explanation. you're right, it's good to understand why things are happening.
This is only correct answer out of like 20, throughout multiple Stack Overflow questions.
But the only time this has happened to me is when it really does involve multiple threads. One thread is reading on the connection while another thread tries to do SaveChanges on the same connection (all via Entity Framework, but I don't think that matters).
Yes, that also was the case with me. I read a complete LDAP directory in a database every night. So because that is a lot of data to import I used 254 threads to handle this..NET is fast, but database transactions are not, so you can help this process to use 254 threads for this. Nowadays this a standard process way of importing lots of data fast. If the import is simple it can be done in another way but in a complex transformation of data this is perfect.
m
mzonerz

Always Use your selection as List

Eg:

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

Then Loop through the Collection while save changes

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

             }

This is not good practice at all. You shouldn't execute SaveChanges that often if you don't need to, and you definitely shouldn't "Always use your selection as List"
@Dinerdo it really depends on the scenario. In my case, I have 2 foreach loops. Outer one had the db query as the list. For instance, this foreach traverses hardware devices. Inner foreach retrieves several data from each device. As per requirement, I need to save to database the data after it is retrieved from each device one by one. It is not an option to save all data at the end of the process. I encountered the same error but the mzonerz's solution worked.
@jstuardo Even with batching?
@Dinerdo I agree it is not a good practice at the philosophical level. However, several situations exist where inside the for loop the code calls another method (let's say a AddToLog() method) which includes a call to db.SaveChanges() locally. In this situation you can't really control the call to db.Save Changes. In this case, using a ToList() or a similar struct will work as suggested by mzonerz. Thanks!
In practice, this will hurt you more than it will help. I stand by what I said - ToList() definitely shouldn't be used all the time, and saving changes after every single item is something to be avoided wherever possible in a high-performance app. This would be a temp fix IMO. Whatever logging method you have should also ideally take advantage of buffering.
M
MikeKulls

I was getting this same issue but in a different situation. I had a list of items in a list box. The user can click an item and select delete but I am using a stored proc to delete the item because there is a lot of logic involved in deleting the item. When I call the stored proc the delete works fine but any future call to SaveChanges will cause the error. My solution was to call the stored proc outside of EF and this worked fine. For some reason when I call the stored proc using the EF way of doing things it leaves something open.


Had similar issue recently: the reason in my case was SELECT statement in stored procedure that produced empty result set and if that result set was not read, SaveChanges threw that exception.
Same thing with unread result from SP, thanks a lot for hint)
j
jjspierx

Here are another 2 options that allow you to invoke SaveChanges() in a for each loop.

The first option is use one DBContext to generate your list objects to iterate through, and then create a 2nd DBContext to call SaveChanges() on. Here is an example:

//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()
    }
}

The 2nd option is to get a list of database objects from the DBContext, but to select only the id's. And then iterate through the list of id's (presumably an int) and get the object corresponding to each int, and invoke SaveChanges() that way. The idea behind this method is grabbing a large list of integers, is a lot more efficient then getting a large list of db objects and calling .ToList() on the entire object. Here is an example of this method:

//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()
}

This is a great alternative that I thought of and did, but this needs to be upvoted. Note: i) you can iterate as enumerable which is good for very large sets; ii) You can use the NoTracking command to avoid problems with loading so many records (if that's your scenario); iii) I really like the primary-key only option as well - that's very smart because you're loading a lot less data into memory, but you're not dealing with Take/Skip on a potentially dynamic underlying dataset.
H
Hemant Sakta

If you get this error due to foreach and you really need to save one entity first inside loop and use generated identity further in loop, as was in my case, the easiest solution is to use another DBContext to insert entity which will return Id and use this Id in outer context

For example

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

J
JamPickle

We started seeing this error "New transaction is not allowed because there are other threads running in the session" after migrating from EF5 to EF6.

Google brought us here but we are not calling SaveChanges() inside the loop. The errors were raised when executing a stored procedure using the ObjectContext.ExecuteFunction inside a foreach loop reading from the DB.

Any call to ObjectContext.ExecuteFunction wraps the function in a transaction. Beginning a transaction while there is already an open reader causes the error.

It is possible to disable wrapping the SP in a transaction by setting the following option.

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

The EnsureTransactionsForFunctionsAndCommands option allows the SP to run without creating its own transaction and the error is no longer raised.

DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands Property


V
Vinod T. Patil

I was also facing same issue.

Here is the cause and solution.

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

Make sure before firing data manipulation commands like inserts, updates, you have closed all previous active SQL readers.

Most common error is functions that read data from db and return values. For e.g functions like isRecordExist.

In this case we immediately return from the function if we found the record and forget to close the reader.


What does "close a reader" mean in Entity Framework? There is no visible reader in a query like var result = from customer in myDb.Customers where customer.Id == customerId select customer; return result.FirstOrDefault();
@Anthony As other answers say, if you use EF to enumerate over a LINQ query (IQueryable), the underlying DataReader will remain open until the last row is iterated over. But although MARS is an important feature to enable in a connection-string, the problem in the OP is still not solved with MARS alone. The problem is trying to SaveChanges while an underlying DataReader is still open.
V
VeldMuijz

So in the project were I had this exact same issue the problem wasn't in the foreach or the .toList() it was actually in the AutoFac configuration we used. This created some weird situations were the above error was thrown but also a bunch of other equivalent errors were thrown.

This was our fix: Changed this:

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

To:

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

Could you elaborate what you think the problem was? you solved this by creating a new Dbcontext each time ?
n
nadir

I know it is an old question but i faced this error today.

and i found that, this error can be thrown when a database table trigger gets an error.

for your information, you can check your tables triggers too when you get this error.


M
Michael Freidgeim

I needed to read a huge ResultSet and update some records in the table. I tried to use chunks as suggested in Drew Noakes's answer.

Unfortunately after 50000 records I've got OutofMemoryException. The answer Entity framework large data set, out of memory exception explains, that

EF creates second copy of data which uses for change detection (so that it can persist changes to the database). EF holds this second set for the lifetime of the context and its this set thats running you out of memory.

The recommendation is to re-create your context for each batch.

So I've retrieved Minimal and Maximum values of the primary key- the tables have primary keys as auto incremental integers.Then I retrieved from the database chunks of records by opening context for each chunk. After processing the chunk context closes and releases the memory. It insures that memory usage is not growing.

Below is a snippet from my code:

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

FromToRange is a simple structure with From and To properties.


I failed to see how you were "renewing" your context. It looks like you are simply creating a new context for each chunk.
@Suncat2000, you are right , context should be a short-living object stackoverflow.com/questions/43474112/…
u
user1551655

Recently I faced the same issue in my project so posting my experience and it might help some on the same boat as i was. The issue was due to i am looping through the results of EF select query (results are not retrieved into memory).

var products = (from e in _context.Products
                              where e.StatusId == 1
                              select new { e.Name, e.Type });

        foreach (var product in products)
        {
           //doing some insert EF Queries
           //some EF select quries
            await _context.SaveChangesAsync(stoppingToken); // This code breaks.
        }

I have updated my Products select query to bring the results into LIST rather than IQueryable (This seems to be opening the reader throughout for each loop and hence save was failing).

 var products = (from e in _context.Products
                              where e.StatusId == 1
                              select new { e.Name, e.Type })**.ToList()**; //see highlighted

u
ugo

The code below works for me:

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();
}

Welcome to SO! Consider adding an explanation and/or links describing why this works for you. Code-only answers are typically considered not good quality for SO.
T
Tomas Kubes

In my case, the problem appeared when I called Stored Procedure via EF and then later SaveChanges throw this exception. The problem was in calling the procedure, the enumerator was not disposed. I fixed the code following way:

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

U
Usman

I am much late to the party but today I faced the same error and how I resolved was simple. My scenario was similar to this given code I was making DB transactions inside of nested for-each loops.

The problem is as a Single DB transaction takes a little bit time longer than for-each loop so once the earlier transaction is not complete then the new traction throws an exception, so the solution is to create a new object in the for-each loop where you are making a db transaction.

For the above mentioned scenarios the solution will be like this:

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 ### //
                    }
                }

M
Max

I am a little bit late, but I had this error too. I solved the problem by checking what where the values that where updating.

I found out that my query was wrong and that there where over 250+ edits pending. So I corrected my query, and now it works correct.

So in my situation: Check the query for errors, by debugging over the result that the query returns. After that correct the query.

Hope this helps resolving future problems.


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now