0

Been using EF Core with Razor pages for a few years now, but Blazor with EF Core has me questioning myself on tasks that used to be simple. I'm creating a golf app, and I'm attempting to update a particular person's round of golf.

Having stumbled in the beginning, I have learned that using dependency injection for the dbContext in Blazor causes several errors including the one in my subject line. Instead, I'm using DI to inject an IDbContextFactory and creating a new context in each method of my services.

The following code updates a golfer's round. When editing, the user may change the course, teebox, or any of the 18 scores. I'm able to update the round once, but if I go back into the same round to edit it a second time I get the "cannot be tracked" "already tracking" error.

I've scoured the internet for possible reasons, I've tried .AsNoTracking() on my initial GetRound(), I've tried detaching the entry after SaveChangesAsync(), I've tried using the ChangeTracker to check whether I need to attach to the Round object being updated. Nothing I've done allows me to update the same round twice without doing a reload in between the first and second update.

I'll provide whatever code necessary, but I'll start with the offending code:

public async Task<bool> UpdateRoundAsync(RoundModel Round)
{
        var rtnVal = false;

        try
        {
            using (var _context = _dbFactory.CreateDbContext())
            {
                _context.Rounds.Attach(Round).State = EntityState.Modified;
                await _context.SaveChangesAsync();
                _context.Entry(Round).State = EntityState.Detached;

            }
            rtnVal = true;
        }
        catch (Exception ex)
        {
            Console.Write(ex.Message);
            throw;
        }

        return rtnVal;
}

When I run the above code, I see NOTHING in the change tracker as modified until I attach to the Round. Despite nothing being tracked, despite the dbContext being created new, then disposed, I still get an error that I'm already tracking the entity.

Help? What am I doing wrong?

Danny

UPDATE: Edited the repro as requested, but it did not change the issue - still unable to update the Round twice without a reload in between.

  • Not sure, but why are you fetching the Courses and TeeBoxes before attaching? And if you want to fetch them, why not just Load the navigation properties after attaching? – David Browne - Microsoft Mar 06 '22 at 14:30
  • 1
    @DavidBrowne-Microsoft - Having switched to AsNoTracking on my initial pull of the Round, only the CourseId and TeeBoxId was being updated via the front end. I set them here just because I was stepping through the code here. You're right, there's a better way, but I was unable to update twice with or without those two lines of code. I'll edit them out if that's distracting from the actual issue I'm asking about. – Daniel Bloodworth Mar 06 '22 at 15:17
  • Also create the DbContext in a `using` block, and re-throw the exception. – David Browne - Microsoft Mar 06 '22 at 15:22
  • Are you suggesting that the _context isn't being disposed of properly, and that may be the reason it is still tracking changes? – Daniel Bloodworth Mar 06 '22 at 15:32
  • Those are code issues that distract from your repro. And yes, I don't know how the factory behaves if you leak connections, so you should have a repro where that can't happen. – David Browne - Microsoft Mar 06 '22 at 15:33
  • Fair enough, I'll edit my code, test, and report back. Thanks for your help. – Daniel Bloodworth Mar 06 '22 at 15:35

2 Answers2

0

Caveat: I'm not happy posting this as an answer, but it does solve the problem for now. I won't mark it as THE answer until I understand more about EFCore and Blazor together.

I did find that I was making a call to get course details without telling EF that I didn't want it to track the entity, however, that still didn't fix the problem.

In the end, I simply forced the page to reload programmatically: NavMgr.NavigateTo("[same page]", true) after my update call. It feels very un-Blazor-like to do it this way, but ultimately I'm still learning Blazor and not getting much feedback on this post. I'm going to forage ahead, and hope that clarity comes down the road.

0

For anyone that may run across this post, I ran into the same issue in a completely different project, and finally found something that made sense (here on S/O).

In this line of code:

_context.Rounds.Attach(Round).State = EntityState.Modified;

It should be:

_context.Entry(Round).State = EntityState.Modified;

I never knew that these two were different, and I never had an issue using the first example's syntax before starting to code with Blazor.

If you are unaware, like me, the first way of setting the state to modified updates the entity and all related entities - which is why I was getting the error when I tried to make additional changes to the round-related objects.

The second way of setting the state ONLY updates the entity itself and leaves the related entities in a State of Unchanged.

Thank you to @TwoFingerRightClick for his comment on the accepted answer on this post: Related post