-1

I am using EF for .NET Core 3.1

I have a DbContext which is taking ages to save 3000 objects

This seems to get slower the further into the loop

Has anyone had this?

I have tried AddAsync and Add with no difference

foreach (var item in itemsToSave)
{
  await _myRepository.CreateNonThreadedAsync(item, false);
}

In my repository....

public async Task CreateAsync(TEntity entity, bool isSaved = true)
{
  await DbContext.Set<TEntity>().AddAsync(entity);
  if (isSaved)
  {
      await SaveChangesAsync();
  }
}

    
public async Task CreateNonThreadedAsync(TEntity entity, bool isSaved = true)
{
    DbContext.Set<TEntity>().Add(entity);
    if (isSaved)
    {
        await SaveChangesAsync();
    }
}

I have to turn on change tracking because this is part of a process where I call SaveChangesAsync on the context once everything is finished

There is another piece of logic outside of this where I call SaveChangesAsync so that is why it is not here

I tried calling AddRangeAsync but nothing gets saved after SaveChangesAsync has been called and there are no errors

Paul

Paul
  • 2,773
  • 7
  • 41
  • 96
  • DbContext has no CreateAsync nor does it need one. The code you posted will perform 42 DELETEs and 67 UPDATEs, if they are pending. – Panagiotis Kanavos Jun 26 '23 at 13:50
  • There are no threads involved either. A DbContext is a Unit Of Work. It persists **all** pending changes in a single transaction when `SaveChanges` is called. This way, if you don't want the changes you just discard the DbContext. `Add` doesn't add anything, it starts tracking the object and all its relations in the `Added` state so that when `SaveChangesAsync` is called, all will be saved. `AddAsync` isn't needed to insert rows asynchronously – Panagiotis Kanavos Jun 26 '23 at 13:52
  • `AddAsync` is *not* needed when keys are generated by the database. `SaveChangesAsync` will retrieve the generated keys and update the objects. [AddAsync](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.addasync?view=efcore-7.0) is only needed in rare scenarios where more complex key algorithms are used, like HiLo which have to ask the database how to generate keys – Panagiotis Kanavos Jun 26 '23 at 13:55
  • Long story short, the code is slow because of the code itself. Instead of adding items Row By Agonizing Row (it's actually an acronym, RBAR), use `dbContext.Customers.AddRange(customers);` and once finished, call `await dbContext.SaveChangesAsync();` to save everything – Panagiotis Kanavos Jun 26 '23 at 13:57
  • PS: nothing is saved unless you actually call `SaveChangesAsync`, so the loop doesn't do anything – Panagiotis Kanavos Jun 26 '23 at 13:59

2 Answers2

2

I have a DbContext which is taking ages to save 3000 objects

Adding entites by one can negatively affect the performance which can be mitigated with AddRange(Async), so start from creating a Create overload which accepts a collection of entities and will determine internally how to batch the requests:

public async Task CreateAsync(TEntity[] entities, bool isSaved = true)
{
  await DbContext.Set<TEntity>().AddRangeAsync(entities); // or just AddRange
  if (isSaved)
  {
      await SaveChangesAsync();
  }
}

But in general EF Core is not the best tool for batch inserts, and be sure that in your code you perform the batch saving (i.e. that you will perform 3000 separate requests and will wait for each one sequentially with isSaved set to true).

Read more:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
1

Try this code

await DbContext.Set<TEntity>().AddRangeAsync(itemsToSave):
await SaveChangesAsync();