I have a controller which calls the ChangeActivity
method, the source
and destination
is gained using another dbContext than what is used here.
All code can be found here.
I also have a lot of unit tests which also test aginst an SQL Server database, and they do not encounter this problem.
Models
public class EntryModel
{
public int Id { get; set; }
public List<ActivityModel> Activities { get; set; }
...
}
public class ActivityModel
{
public int Id { get; set; }
...
}
Problematic Code
public ActivityChangeModel ChangeActivity(ActivityModel source, ActivityModel destination, Guid userGuid)
{
DeleteActivity(source);
OverrideEntries(source, destination, userGuid);
var change = AddChange(source, destination, userGuid);
_db.SaveChanges();
return change;
}
private void DeleteActivity(ActivityModel source)
{
var model = _db.Activity.Single(x => x.Id == source.Id);
model.Deleted = true;
}
private void OverrideEntries(ActivityModel source, ActivityModel destination, Guid userGuid)
{
var entries = _db.Entry.Include(x => x.Activities).Where(x => x.Activities.Contains(source)).ToList();
foreach (var entry in entries)
{
entry.Deleted = true;
}
foreach (var entry in entries)
{
var newEntry = new EntryModel(entry.StartTime, entry.EndTime, entry.RecordedTime, new List<ActivityModel>(), false, userGuid);
_db.Entry.Add(newEntry);
var activities = entry.Activities;
activities.Remove(source);
activities.Add(destination);
newEntry.Activities = activities;
}
}
private ActivityChangeModel AddChange(ActivityModel source, ActivityModel destination, Guid userGuid)
{
var change = new ActivityChangeModel(source, destination, _timeService.Current, userGuid);
_db.ActivityChange.Add(change); // Throws Error
return change;
}
The error which is thrown is
System.InvalidOperationException: 'The instance of entity type 'ActivityModel' cannot be tracked because another instance with the key value '{Id: 5}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.'
I have tried
_db.ChangeTracker.Clear();
before I add the activity to activities_db.ChangeTracker.DebugView.LongView
just before var activities = entry.Activities;
which says that the activity Id: 5
is being tracked, but it is unchangedAny help is much appreciated
The reason why I got the error is that the source
and destination
objects, received by ChangeActivity
, are retrieved using another dbContext
. This means that when destination
is retrieved from the current dbContext
we have two equivalent destination
- one tracked by the new dbContext
and another which is not.
The fix is to either override source
and destination
using _db.Activity.SingleOrDefault(x => x.Id == source.Id)
, or (in my case) change the dbContext
to be scoped instead of a transient service. Changing it to scoped means that the two destination
objects we had before are now the same one, tracked by the dbContext
.