LP13
LP13

Reputation: 34109

How to save changes twice in one method using Entity Framework Core

Using Entity Framework Core i am trying to save changes twice for the same entity collection.

    public async Task Save(IEnumerable<Request> request)
    {            
        var entities = request.Select(x => new MyEntity()
        {
            FileName = x.FileName,                
            Status = Status.Downloading                
        });

        // save as downloading
        await _dbContext.MyFiles.AddRangeAsync(entities).ConfigureAwait(false);
        await _dbContext.SaveChangesAsync().ConfigureAwait(false);

        // start downloading
        await _storage.DownloadFilesAsync(request).ConfigureAwait(false);

        // save as downloaded
        foreach (var entity in entities)
        {
            entity.Status = Status.Downloaded;
        }

        // this save DOES NOT update the entities with status Downloaded
        await _dbContext.SaveChangesAsync().ConfigureAwait(false);          
    }

The first call to SaveChanges() method creates entities in the DB with status Downloading.

Then i am modifying entity's status to Downloaded and call SaveChanges() again. However this time entities does not get updated in DB.

I thought entities are already loaded in the Db context, so i dont have to reload them before second SaveChanges. Just modifying the property would mark them modified. But its not working.

Update 1
I updated code as below and explicitly set State as Modified

 // save as downloaded
 foreach (var entity in entities)
 {
    entity.Status = Status.Downloaded;      

    // The exception occurs at line below on 2nd loop
   _dbContext.Entry(entity).State = EntityState.Modified;
 }

Now im getting exception

System.InvalidOperationException: The instance of entity type 'MyEntity' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context. at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges)

The entity has Id property of type int and The Id property also has [Key] attribute.

public class MyEntity
{
   [System.ComponentModel.DataAnnotations.Key]
   public int Id {get;set;}
   public string FileName {get;set;}
   public string Status {get;set;}
}

Update 2
So after doing little more debugging, i found the first SaveChanges() operation is creating the records in the database and also assigning unique Ids to each record as expected. The Id is auto incremented identity column in the database. However the entities are not refreshed with new Ids. so all the entities still have value 0 after first savechanges.

How do i refresh these entities with newly created Ids?

Inside foreach loop i tried reloading as

   _dbContext.Entry<OcrFile>(entity).ReloadAsync().ConfigureAwait(false);

but that didn't refresh.

Upvotes: 1

Views: 2565

Answers (1)

LP13
LP13

Reputation: 34109

Resolved!!. I have to eagerly load entities using ToList() or ToArray() before i pass them to AddRange method

    var entities = request.Select(x => new MyEntity()
    {
        FileName = x.FileName,                
        Status = Status.Downloading                
    }).ToList();

I am not sure that is a bug in EF but i created a separate SO thread for that question

Upvotes: 1

Related Questions