diff --git a/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs b/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs index b340033ceab..fbb9ec57d67 100644 --- a/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs +++ b/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; @@ -32,6 +33,7 @@ public abstract class CrudService _repositoryFactory; + private readonly bool _isToModelOverridden; /// /// Construct new CrudService @@ -44,6 +46,10 @@ protected CrudService(Func repositoryFactory, IPlatformMemoryCache _repositoryFactory = repositoryFactory; _platformMemoryCache = platformMemoryCache; _eventPublisher = eventPublisher; + + _isToModelOverridden = GetType() + .GetMethod(nameof(ToModel), BindingFlags.Instance | BindingFlags.NonPublic, [typeof(TEntity)]) + ?.DeclaringType != typeof(CrudService); } /// @@ -75,7 +81,7 @@ protected virtual async Task> GetByIdsNoCache(IList ids, s { using var repository = _repositoryFactory(); - // Disable DBContext change tracking for better performance + // Disable DBContext change tracking for better performance repository.DisableChangesTracking(); var entities = await LoadEntities(repository, ids, responseGroup); @@ -91,7 +97,7 @@ protected virtual void ConfigureCache(MemoryCacheEntryOptions cacheOptions, stri protected virtual IList ProcessModels(IList entities, string responseGroup) { return entities - ?.Select(x => ProcessModel(responseGroup, x, ToModel(x))) + ?.Select(x => ProcessModel(responseGroup, x, ToModel(x, model: null))) .ToList(); } @@ -196,7 +202,7 @@ public virtual async Task SaveChangesAsync(IList models) // https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#detectchanges-honors-store-generated-key-values repository.TrackModifiedAsAddedForNewChildEntities(originalEntity); - var originalModel = ToModel(originalEntity); + var originalModel = ToModel(originalEntity, model: null); originalModels.Add(originalModel); changedEntries.Add(new GenericChangedEntry(model, originalModel, EntryState.Modified)); modifiedEntity.Patch(originalEntity); @@ -226,7 +232,8 @@ public virtual async Task SaveChangesAsync(IList models) foreach (var (changedEntry, i) in changedEntries.Select((x, i) => (x, i))) { - changedEntry.NewEntry = ToModel(changedEntities[i]); + // Sync database-generated values back to the original models + changedEntry.NewEntry = ToModel(changedEntities[i], changedEntry.NewEntry); } await AfterSaveChangesAsync(models, changedEntries); @@ -330,6 +337,20 @@ protected virtual void ClearSearchCache(IList models) GenericSearchCachingRegion.ExpireRegion(); } + protected virtual TModel ToModel(TEntity entity, TModel model) + { + // Call the obsolete method temporarily if it has been overridden in a derived class, to avoid breaking changes. + if (_isToModelOverridden) + { +#pragma warning disable VC0014 // Type or member is obsolete + return ToModel(entity); +#pragma warning restore VC0014 // Type or member is obsolete + } + + return entity.ToModel(); + } + + [Obsolete("Use ToModel(entity, model)", DiagnosticId = "VC0014", UrlFormat = "https://docs.virtocommerce.org/products/products-virto3-versions")] protected virtual TModel ToModel(TEntity entity) { return entity.ToModel();