private ConcurrencyResolutionResult HandleEntryGeneric <TEntity>(PropertyValues originalValues, PropertyValues currentValues, PropertyValues proposedValues) { var handlers = _serviceProvider .GetServices <IConcurrencyErrorHandler <TEntity> >(); ConcurrencyLogMessages.ConcurrencyErrorHandlersExecuting(_logger); var result = ConcurrencyResolutionResult.Unhandled; foreach (var handler in handlers) { ConcurrencyLogMessages.ConcurrencyErrorHandlerExecuting(_logger, handler); result = handler.HandleConcurrencyError(originalValues, currentValues, proposedValues); if (result.IsHandled) { ConcurrencyLogMessages.EntityEntryHandledByHandler(_logger, handler); break; } ConcurrencyLogMessages.EntityEntryNotHandledByHandler(_logger, handler); } return(result); }
// For the record, don't even bother trying to Unit Test this. DbUpdateConcurrencyException is not mockable. // Layers upon layers of non-mockable dependencies, plus polymorphism abuse, just for extra fun // (E.G. if you try and use an IEntityType that isn't EntityType, you get an exception) public async Task HandleExceptionAsync(DbUpdateConcurrencyException exception, CancellationToken cancellationToken) { ConcurrencyLogMessages.ConcurrencyExceptionHandling(_logger, exception); foreach (var entry in exception.Entries) { ConcurrencyLogMessages.EntityEntryHandling(_logger, entry); var tEntity = entry.Entity.GetType(); var originalValues = entry.OriginalValues; var currentValues = await entry.GetDatabaseValuesAsync(cancellationToken); var proposedValues = entry.CurrentValues; if (HandleEntry(tEntity, originalValues, currentValues, proposedValues).IsUnhandled) { ConcurrencyLogMessages.ConcurrencyExceptionNotHandled(_logger, exception); throw new InvalidOperationException($"Concurrency exception for entity type {entry.Entity.GetType()} was unhandled", exception); } ConcurrencyLogMessages.EntityEntryHandled(_logger); entry.OriginalValues.SetValues(currentValues); } ConcurrencyLogMessages.ConcurrencyExceptionHandled(_logger); }