Skip to content

Globally Exclude/Optional component fails with EF Core error on SQLite storage provider #208

Description

@Linducya

Tool version
Current version of the Azure Naming Tool you are running: 5.0.2

Describe the bug
When editing a component and using "Globally Exclude Configuration" → ADD to exclude the component for all resource types, the operation fails with an [InvalidOperationException] from [SQLiteConfigurationRepository.SaveAllAsync()].

The toast initially shows the component was "ADDED as EXCLUDE!" but the underlying save operation throws an EF Core [SaveChangesAsync] error. The same issue affects "Globally Optional Configuration" → ADD.

This bug only affects the SQLite storage provider. JSON file storage is unaffected because file writes are stateless.

To Reproduce

  1. Use SQLite storage provider (default for v5.0.0+).
  2. Go to Configuration → any component section (e.g. Custom Components).
  3. Click Edit on a component.
  4. Expand "Globally Exclude Configuration".
  5. Click ADD ("This option will ADD the component as EXCLUDE for all resource types").
  6. Click UPDATE.
  7. Observe EF Core [InvalidOperationException] error (see screenshots).

Expected behaviour
The component should be successfully added to the Exclude list for all resource types without error.

Screenshots
If applicable, add screenshots to help explain your problem.

Image Image

Installation Method
Azure App Service (Linux), SQLite storage provider, deployed via GitHub Actions CI/CD.

Additional context
Root cause:

[ResourceTypeService.UpdateTypeComponentsAsync()] (line 441) iterates over all resource types and calls [PostItemAsync()] for each one individually:

foreach (ResourceType currenttype in resourceTypes)
{
    // ...modify currenttype.Exclude...
    await PostItemAsync(currenttype);  // ← called per resource type
}

[PostItemAsync()] calls [_repository.SaveAllAsync()] which:

  1. Removes ALL entities from the DbSet ([_dbSet.RemoveRange(existingEntities)]
  2. Re-adds ALL entities ([_dbSet.AddRangeAsync(entities)]
  3. Calls [SaveChangesAsync()]

On the second iteration, the EF Core change tracker still holds entities from the first [SaveAllAsync()] call. When it tries to add entities with the same primary keys, EF Core throws an [InvalidOperationException].

This pattern works with JSON file storage (stateless writes) but breaks with SQLite/EF Core due to entity tracking state.

Suggested fix:

Batch the changes and save once after the loop:

foreach (ResourceType currenttype in resourceTypes)
{
    switch (operation)
    {
        case "exclude-add":
            var currentvalues = new List<string>(currenttype.Exclude.Split(','));
            if (!currentvalues.Contains(component))
            {
                currentvalues.Add(component);
                currenttype.Exclude = String.Join(",", currentvalues);
            }
            break;
        // ...other cases...
    }
}
// Save all resource types once after modifications
await _repository.SaveAllAsync(resourceTypes.OrderBy(x => x.Id).ToList());

Alternatively, clear/detach the DbContext change tracker between iterations, or use a dedicated UpdateAsync method that doesn't delete-and-reinsert the entire collection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions