Reputation: 34109
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
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