Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core throwing a concurrency error when adding entities to a list

I'm having an EF Core DbUpdateConcurrencyException issue. I've got this Widget object, and it has a list of Package objects. When I try to update the Widget and add some new Packages to its list, I keep getting this DbUpdateConcurrencyException.

Here's the gist of my code:

public async Task<RequestResponse> Handle(UpdateWidgetCommand request, CancellationToken cancellationToken)
{
    // ... validation ...

    var package = new Package
    {
        PackageName = "New Package",
        // ... other properties ...
        Packages = [new()
        {
            PackageName = "Sub Package",
            // ... other properties ...
        }]
    };

    var success = await _repository.UpdateWidget(request.WidgetId, request.PackageId, package);

    if (success)
    {
        await _repository.SaveChangesAsync();
        return new RequestResponse(OperationState.Success());
    }
    else
    {
        return new RequestResponse(OperationState.Failed("Failed to update Widget"));
    }
}

public async Task<bool> UpdateWidget(Guid widgetId, Guid packageId, Package package)
{
    var widget = await _context.Widgets
        .Include(w => w.Packages)
        .Include(w => w.Components)
        .SingleOrDefaultAsync(w => w.Id == widgetId);

    var existingPackage = widget.Packages.SingleOrDefault(p => p.Id == packageId);

    // Changing properties on an existing Package works fine
    existingPackage.PackageName = package.PackageName;
    existingPackage.ModifiedDate = DateTime.Now;

    // This line blows up with a concurrency exception
    existingPackage.Packages.AddRange(package.Packages);

    // But this line, updating existing entries in another list, works fine
    widget.Components.ForEach(x => x.IsInstalled = false);

    return true;
}

Basically:

  • Widget has a list of Packages and another list called Components.
  • I'm loading the Widget and its lists.
  • I'm changing some properties of an existing Package in the Packages list which works.
  • When I try to add new Packages to the existingPackage.Packages list using AddRangeI get the Concurrency error.
  • However, I can loop through the Components list and change stuff, and that works.

I get that the concurrency error means something's changed in the database since I loaded the Widget, but I don't get why it's only happening when I add to the Packages list.

So, my questions are:

  1. Why does adding to the Packages list cause a problem, but changing properties on an existing Package and looping through Components doesn't?
  2. What's the right way to add these new Packages without getting this error?
  3. Any tips for dealing with this kind of thing in EF Core?

TIA

like image 391
atamata Avatar asked Dec 06 '25 15:12

atamata


2 Answers

It looks like (from the validation code) a package contains child packages, but when a widget is loaded from context, those child objects possibly aren't being included (and so won't be tracked).

If a package contains child packages I'd expect something like (note the ThenInclude to include the child package references):

    var widget = await _context.Widgets
        .Include(w => w.Packages).ThenInclude(p => p.Packages)
        .Include(w => w.Components)
        .SingleOrDefaultAsync(w => w.Id == widgetId);

Although to me it's not clear from the above what the Primary Key for the entities are, so if you (for example) have something like PackageId, ParentPackageId defined.

One other thing you could try is seeing what the change tracking debugging shows : https://learn.microsoft.com/en-gb/ef/core/change-tracking/debug-views

The exception that you are showing happens when the change tracker cannot detect changes (which is why I suggested looking at the child packages within the object model), the debug view may show this (or otherwise why it thinks there are no changes). See here for more details on the exception : https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.infrastructure.dbupdateconcurrencyexception?view=entity-framework-6.2.0

Hope this helps - if you could post more details about the entity relationships it may help people understand the issue, but from the above I suspect the issue is related to load including the child packages.

like image 94
Nick Pattman Avatar answered Dec 12 '25 04:12

Nick Pattman


Probably it doesn't quite clear hierarchy for EF by adding a chain of packages this way.

Try to extend the Package entity by int? ParentPackageId property and set the hierarchy first by traversing each object in the package argument.

Then the collection of packages should be saved using the package repository.

The parent package should be added to the widget object and saved.

like image 29
Alex Avatar answered Dec 12 '25 04:12

Alex



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!