Reputation: 870
This question is a common one, but I still can't understand how to update the related entity properly?
I have the following code:
public async Task<bool> UpdateStepAssignedToLevelAsync(Step step, Guid levelId, int priority = -1)
{
var item = await this._context.StepLevels
.Include(sl => sl.Step)
.FirstOrDefaultAsync(x => x.StepId == step.Id && x.LevelId == levelId);
if (item == null)
{
return false;
}
//this._context.Entry(item).State = EntityState.Detached;
if (priority > -1)
{
item.Priority = priority;
}
item.Step = step;
//this._context.StepLevels.Update(item);
var rows = await this._context.SaveChangesAsync();
return rows > 0;
}
When it runs I'm getting the following error:
InvalidOperationException: The instance of entity type 'Step' cannot be tracked because another instance with the key value '{Id: 35290c18-5b0a-46a5-8f59-8888cf548df5}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
As I understand the entity is being tracked since the select request at the method's start. Okay, but when I'm detaching the entity and calling the Update method (see commented lines), the Step entity is not being changed. But StepLevel does: the priority is changing. When I'm trying to call just Update the EF tries to insert the new Step instead of updating the existing.
So, could you please advice me, what is the best practicing here?
Thanks a lot in advance!
Upvotes: 3
Views: 9791
Reputation: 738
The context is tracking the corresponding Step entity loaded from the DB for that reason it is throwing this error. You can update each Step property -
public bool UpdateStepAssignedToLevelAsync(Step step, int levelId)
{
using(var context = new StackOverFlowDbContext())
{
var item = context.StepLevels
.Include(sl => sl.Step)
.FirstOrDefault(x => x.Id == step.Id && x.Id == levelId);
if (item == null)
{
return false;
}
// Updating Name property
item.Step.Name = step.Name;
// Other properties can be upadated
var rows = context.SaveChanges();
return rows > 0;
}
}
Upvotes: 0
Reputation: 205539
First, detaching an entity does not detach related entities, so detaching item
does not detach the item.Step
retrieved with .Include(sl => sl.Step)
.
Second, since you don't want to change the item.Step
, but to update the existing Step
entity (x.StepId == step.Id
), and you know that the context is tracking (contains) the corresponding Step
entity loaded from the database, you should use the procedure for updating a db entity from a detached entity via Entry(...).CurrentValues.SetValues
method.
So remove
item.Step = step;
and use the following instead:
this._context.Entry(item.Step).CurrentValues.SetValues(step);
Last note. When you work with attached entities, there is no need to (should not) use Update
method. The change tracker will automatically detect the changed property values if any of the entities being tracked.
Upvotes: 9