Fix: sync DB-generated values back to original models in CrudService.SaveChangesAsync#2988
Fix: sync DB-generated values back to original models in CrudService.SaveChangesAsync#2988alexeyshibanov wants to merge 4 commits intodevfrom
Conversation
…SaveChangesAsync Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Review task created: https://virtocommerce.atlassian.net/browse/VCST-4749 |
vc-ci
left a comment
There was a problem hiding this comment.
Test Suite: Test Suites/Modules/module_Assets
Tests: 13
Failures: 0
Errors: 0
Time: 7.975
Timestamp: 09-03-2026T14:29:29
| // Update the original model in place instead of creating a new one. | ||
| // This ensures DB-generated values are synced back to the original model objects | ||
| // passed to SaveChangesAsync. | ||
| changedEntities[i].ToModel(changedEntry.NewEntry); |
There was a problem hiding this comment.
The CrudService.ToModel() method should be called because it could be overridden, for example here:
https://github.com/VirtoCommerce/vc-module-payment/blob/4d4249bc2292ba2addef6df4b3f4d3c23ec53e8a/src/VirtoCommerce.Payment.Data/Services/PaymentMethodsService.cs#L78
There was a problem hiding this comment.
Good catch! Fixed — the loop now calls the virtual ToModel with a new optional parameter:
changedEntry.NewEntry = ToModel(changedEntities[i], changedEntry.NewEntry);The ToModel signature changed to ToModel(TEntity entity, TModel model = null):
model = null→ creates a new object viaentity.ToModel()(existing behavior forGetAsyncandoriginalModelcreation)model != null→ in-place update viaentity.ToModel(model)(new behavior for post-save sync)
This is an intentional breaking change for existing overrides like PaymentMethodsService.ToModel(entity) — they'll get a compile error and need to update their signature. A compile error is better than a silent regression where the override stops being called.
Change ToModel signature to accept optional target model parameter. When target is provided, updates it in-place; when null, creates new instance (preserving existing behavior for GetAsync and change tracking). This is a breaking change for CrudService overrides of ToModel(entity) — they must update to the new signature ToModel(entity, model). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
|
vc-ci
left a comment
There was a problem hiding this comment.
Test Suite: Test Suites/Modules/module_Assets
Tests: 13
Failures: 0
Errors: 0
Time: 7.825
Timestamp: 13-03-2026T11:32:07

Summary
CrudService.SaveChangesAsyncreplaceschangedEntry.NewEntrywith a new model created via parameterlessentity.ToModel()extension. This means the original model objects passed toSaveChangesAsyncnever receive DB-generated values (auto-incremented IDs, foreign keys, etc.)modelparameter to the virtualToModelmethod:ToModel(TEntity entity, TModel model = null). Whenmodelis provided, it updates in-place viaentity.ToModel(model); whennull, it creates a new instance (preserving existing behavior)changedEntry.NewEntry = ToModel(changedEntities[i], changedEntry.NewEntry), routing through the virtual method so that downstream overrides (e.g.PaymentMethodsService.ToModel) are respectedBreaking change
The
ToModelsignature changed fromToModel(TEntity entity)toToModel(TEntity entity, TModel model = null). Existing overrides will get a compile error and must update their signature. This is intentional — a compile error is better than a silent regression where the override stops being called.Known overrides to update:
PaymentMethodsService.ToModelMotivation
Modules that need DB-generated values synced back to the caller's model objects are forced to copy-paste the entire
SaveChangesAsyncmethod just to change this single line.CustomerOrderServicealready does this.ShoppingCartServiceneeds it too for the same reason.Fixing at the
CrudServicelevel eliminates the need for per-module workarounds and ensures consistent behavior across all CRUD services.Test plan
CrudServiceTestspass (7/7)🤖 Generated with Claude Code
Image tag:
ghcr.io/VirtoCommerce/platform:3.1008.0-pr-2988-bcae-crud-service-sync-model-after-save-bcae5eb5
Note
Medium Risk
Touches the generic
CrudServiceused across modules and changes model-mapping flow after persistence; reflection-based override detection and in-place updates could affect downstream customToModeloverrides and event payloads.Overview
Fixes
CrudService.SaveChangesAsyncto sync database-generated fields back into the same model instances (e.g., IDs/keys) by mapping entities into the existingchangedEntry.NewEntryinstead of always creating a new model.Introduces a new
ToModel(TEntity entity, TModel model)overload that can update in-place, marks the oldToModel(TEntity)as obsolete, and updates read/save code paths to call the new overload while preserving legacy overrides via runtime override detection.Written by Cursor Bugbot for commit bcae5eb. This will update automatically on new commits. Configure here.