I'm using DbContext scope implementation. Basically it means that I relay on ambient DbContext approach. More information
I simplified my real code and removed all logic related to the context flow behavior...
using (var dbContextParentScope = ...)
{
DbContext parentContext = dbContextParentScope.Get<ApplicationContext>();
// that data will be in the DbContext cache
var accounts = parentContext.Accounts.ToList();
// this is always part of a separate method
using (var dbContextChildScope = ...)
{
DbContext childContext = dbContextChildScope.Get<ApplicationContext>();
var childAccounts = childContext.Accounts.ToList();
childAccounts.ForEach(a => a.DisplayName = "New name");
// after that operation parentContext data will be outdated
childContext.SaveChanges();
// this will reload updated entities in the parentContext
dbContextChildScope.RefreshEntitiesInParentScope(childAccounts);
}
// without Refresh operation we will work with outdated data
parentContext.SaveChanges();
}
This is exceptional case but sometimes we have to save important information outside main transaction.
Entity Framework has possibility to get record by key from DbContext:
if (((IObjectContextAdapter)childContext).ObjectContext.ObjectStateManager.TryGetObjectStateEntry(childAccounts[0], out ObjectStateEntry stateInChildScope))
{
var key = stateInChildScope.EntityKey;
if (((IObjectContextAdapter)parentContext).ObjectContext.ObjectStateManager.TryGetObjectStateEntry(key, out ObjectStateEntry stateInParentScope))
{
if (stateInParentScope.State == EntityState.Unchanged)
{
((IObjectContextAdapter)parentContext).ObjectContext.Refresh(RefreshMode.StoreWins, stateInParentScope.Entity);
}
}
}
EF Core doesn't have such functionality but it has 'ChangeTracker'. It can return entries that DbContext is tracking:
parentContext.ChangeTracker.Entries().Where(w => w.State == EntityState.Unchanged).ToList()
But I want to reload only records that were updated in the childContext. So if I write something like this:
var updatedEntry = childContext.Entry(childAccounts[0]);
parentContext.Entry(updatedEntry.Entity).Reload();
EF Core thinks about updatedEntry like it is new and will throw System.InvalidOperationException:
The instance of entity type 'Account' cannot be tracked because another instance with the same key value for {'EntityId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Also I found some internal functionality but I don't want to use internal EF Core code (except it is only way to make it works) because it can be changed/removed:
var entityType = parentContext.Model.FindEntityType(typeof(TEntityType));
var key = entityType.FindPrimaryKey();
var stateManager = parentContext.GetDependencies().StateManager;
var keysDictionary = key.Properties.ToDictionary(x => x.Name, x => x.PropertyInfo.GetValue(childAccounts[0]));
var entry = stateManager.TryGetEntry(key, keysDictionary.Values.ToArray());
parentContext.Entry(entry.Entity).Reload();
Does anyone know how can I update entities from parent context?
I hope for your help because I'm stuck here and I ran out of any ideas