diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index f84b4c5..aeb3764 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -1,5 +1,8 @@ # Breaking Changes +## 1.3.0 +- The NuGet reference to `Microsoft.Extensions.Logging.Abstractions` now requires version 6.0.0 and up. + ## 0.11.0 - `DecoratorCommandStrategy` not longer exists. Use `DelegatingCommandStrategy` instead. diff --git a/src/DynamicMvvm.Abstractions/DynamicMvvm.Abstractions.csproj b/src/DynamicMvvm.Abstractions/DynamicMvvm.Abstractions.csproj index 575dfa2..5265643 100644 --- a/src/DynamicMvvm.Abstractions/DynamicMvvm.Abstractions.csproj +++ b/src/DynamicMvvm.Abstractions/DynamicMvvm.Abstractions.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 10 Chinook.DynamicMvvm nventive nventive @@ -30,7 +31,7 @@ - + diff --git a/src/DynamicMvvm.Abstractions/LoggerMessages.Abstractions.cs b/src/DynamicMvvm.Abstractions/LoggerMessages.Abstractions.cs new file mode 100644 index 0000000..63dd8c1 --- /dev/null +++ b/src/DynamicMvvm.Abstractions/LoggerMessages.Abstractions.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.Logging; + +namespace Chinook.DynamicMvvm +{ + internal static partial class LoggerMessagesAbstractions + { + [LoggerMessage(101, LogLevel.Warning, "Resolving property '{ViewModelTypeName}.{PropertyName}' using reflection on '{ViewModelName}'.")] + public static partial void LogViewModelResolvingPropertyUsingReflection(this ILogger logger, string viewModelTypeName, string propertyName, string viewModelName); + } +} diff --git a/src/DynamicMvvm.Abstractions/ViewModel/IViewModel.Extensions.Properties.cs b/src/DynamicMvvm.Abstractions/ViewModel/IViewModel.Extensions.Properties.cs index 8934931..b6e48c8 100644 --- a/src/DynamicMvvm.Abstractions/ViewModel/IViewModel.Extensions.Properties.cs +++ b/src/DynamicMvvm.Abstractions/ViewModel/IViewModel.Extensions.Properties.cs @@ -248,7 +248,7 @@ public static IDynamicProperty GetOrResolveProperty(this IViewModel viewModel, s if (!viewModel.TryGetDisposable(name, out var property)) { - typeof(IViewModel).Log().LogWarning($"Resolving property '{viewModel.GetType().Name}.{name}' using reflection on '{viewModel.Name}'."); + typeof(IViewModel).Log().LogViewModelResolvingPropertyUsingReflection(viewModel.GetType().Name, name, viewModel.Name); // This is a rare case where the property was resolved before being created. // We simply resolve it manually on the type. diff --git a/src/DynamicMvvm.Benchmarks/Benchmark.cs b/src/DynamicMvvm.Benchmarks/Benchmark.cs index ab0c920..70599f8 100644 --- a/src/DynamicMvvm.Benchmarks/Benchmark.cs +++ b/src/DynamicMvvm.Benchmarks/Benchmark.cs @@ -30,6 +30,12 @@ public void CreateViewModel() var vm = new ViewModel(_serviceProvider); } + [Benchmark] + public void CreateViewModel_WithExplicitName() + { + var vm = new ViewModel("ViewModel", _serviceProvider); + } + [IterationSetup(Targets = new[] { nameof(ReadProperty_Unresolved), @@ -40,7 +46,7 @@ public void CreateViewModel() })] public void SetupViewModel() { - _viewModel1 = new ViewModel(_serviceProvider); + _viewModel1 = new ViewModel("ViewModel", _serviceProvider); } [IterationSetup(Targets = new[] @@ -51,7 +57,7 @@ public void SetupViewModel() })] public void SetupViewModelWithListener() { - _viewModel2 = new ViewModel(_serviceProvider); + _viewModel2 = new ViewModel("ViewModel", _serviceProvider); _viewModel2.PropertyChanged += (s, e) => { }; } @@ -101,5 +107,5 @@ public void SetProperty_Resolved_WithListener() public void DisposeViewModel_WithListener() { _viewModel2!.Dispose(); - } + } } diff --git a/src/DynamicMvvm.Benchmarks/Program.cs b/src/DynamicMvvm.Benchmarks/Program.cs index b43091b..fb0f6a8 100644 --- a/src/DynamicMvvm.Benchmarks/Program.cs +++ b/src/DynamicMvvm.Benchmarks/Program.cs @@ -1,4 +1,22 @@ using BenchmarkDotNet.Running; using DynamicMvvm.Benchmarks; +using Chinook.DynamicMvvm; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; BenchmarkRunner.Run(); + +// The following section is to profile manually using Visual Studio's debugger. + +//var serviceProvider = new HostBuilder() +// .ConfigureServices(serviceCollection => serviceCollection +// .AddSingleton() +// .AddSingleton() +// ) +// .Build() +// .Services; + +//var vm1 = new ViewModel("ViewModel", serviceProvider); +//var vm2 = new ViewModel("ViewModel", serviceProvider); + +//Console.Read(); diff --git a/src/DynamicMvvm.Benchmarks/ViewModel.cs b/src/DynamicMvvm.Benchmarks/ViewModel.cs index 7a547aa..a83e880 100644 --- a/src/DynamicMvvm.Benchmarks/ViewModel.cs +++ b/src/DynamicMvvm.Benchmarks/ViewModel.cs @@ -9,12 +9,17 @@ namespace DynamicMvvm.Benchmarks; public class ViewModel : ViewModelBase { - public ViewModel(IServiceProvider serviceProvider) - : base(serviceProvider: serviceProvider) + public ViewModel(string? name, IServiceProvider serviceProvider) + : base(name, serviceProvider) { var value = NumberResolved; } + public ViewModel(IServiceProvider serviceProvider) + : this(name: default, serviceProvider: serviceProvider) + { + } + public int Number { get => this.Get(initialValue: 42); diff --git a/src/DynamicMvvm.FluentValidation/DynamicMvvm.FluentValidation.csproj b/src/DynamicMvvm.FluentValidation/DynamicMvvm.FluentValidation.csproj index 70c8495..ab4d05e 100644 --- a/src/DynamicMvvm.FluentValidation/DynamicMvvm.FluentValidation.csproj +++ b/src/DynamicMvvm.FluentValidation/DynamicMvvm.FluentValidation.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 10 Chinook.DynamicMvvm nventive nventive diff --git a/src/DynamicMvvm.FluentValidation/IViewModel.Extensions.Validation.cs b/src/DynamicMvvm.FluentValidation/IViewModel.Extensions.Validation.cs index 65b1287..96b234a 100644 --- a/src/DynamicMvvm.FluentValidation/IViewModel.Extensions.Validation.cs +++ b/src/DynamicMvvm.FluentValidation/IViewModel.Extensions.Validation.cs @@ -168,7 +168,7 @@ private void OnValueChanged(IDynamicProperty property) } catch (Exception e) { - this.Log().LogError(e, $"Validation failed for property '{property.Name}'."); + this.Log().LogValidationFailed(property.Name, e); } }); } diff --git a/src/DynamicMvvm.FluentValidation/LoggerMessages.FluentValidation.cs b/src/DynamicMvvm.FluentValidation/LoggerMessages.FluentValidation.cs new file mode 100644 index 0000000..c3585d0 --- /dev/null +++ b/src/DynamicMvvm.FluentValidation/LoggerMessages.FluentValidation.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.Logging; + +namespace Chinook.DynamicMvvm +{ + internal static partial class LoggerMessages + { + [LoggerMessage(201, LogLevel.Error, "Validation failed for property '{PropertyName}'.")] + public static partial void LogValidationFailed(this ILogger logger, string propertyName, Exception exception); + } +} diff --git a/src/DynamicMvvm.Reactive/Deactivation/DeactivatableObservable.cs b/src/DynamicMvvm.Reactive/Deactivation/DeactivatableObservable.cs index 2dcc60c..01372d2 100644 --- a/src/DynamicMvvm.Reactive/Deactivation/DeactivatableObservable.cs +++ b/src/DynamicMvvm.Reactive/Deactivation/DeactivatableObservable.cs @@ -61,7 +61,7 @@ public void Deactivate() IsDeactivated = true; - typeof(IDeactivatable).Log().LogDebug($"Deactivated observable of type '{typeof(T).Name}'."); + typeof(IDeactivatable).Log().LogDeactivatedObservable(typeof(T).Name); } /// @@ -79,7 +79,7 @@ public void Reactivate() _subscription = _source.Subscribe(_observer); } - typeof(IDeactivatable).Log().LogDebug($"Reactivated observable of type '{typeof(T).Name}'."); + typeof(IDeactivatable).Log().LogReactivatedObservable(typeof(T).Name); } /// diff --git a/src/DynamicMvvm.Reactive/DynamicMvvm.Reactive.csproj b/src/DynamicMvvm.Reactive/DynamicMvvm.Reactive.csproj index edfafa2..ec6f944 100644 --- a/src/DynamicMvvm.Reactive/DynamicMvvm.Reactive.csproj +++ b/src/DynamicMvvm.Reactive/DynamicMvvm.Reactive.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 10 Chinook.DynamicMvvm nventive nventive @@ -29,8 +30,6 @@ - - diff --git a/src/DynamicMvvm.Reactive/LoggerMessages.Reactive.cs b/src/DynamicMvvm.Reactive/LoggerMessages.Reactive.cs new file mode 100644 index 0000000..152e433 --- /dev/null +++ b/src/DynamicMvvm.Reactive/LoggerMessages.Reactive.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.Logging; + +namespace Chinook.DynamicMvvm +{ + internal static partial class LoggerMessagesReactive + { + [LoggerMessage(301, LogLevel.Debug, "Deactivated observable of type '{TypeName}'.")] + public static partial void LogDeactivatedObservable(this ILogger logger, string typeName); + + [LoggerMessage(302, LogLevel.Debug, "Reactivated observable of type '{TypeName}'.")] + public static partial void LogReactivatedObservable(this ILogger logger, string typeName); + + } +} diff --git a/src/DynamicMvvm.Uno.WinUI/Dispatchers/BatchingDispatcherQueueDispatcher.cs b/src/DynamicMvvm.Uno.WinUI/Dispatchers/BatchingDispatcherQueueDispatcher.cs index c63b52b..9b679b1 100644 --- a/src/DynamicMvvm.Uno.WinUI/Dispatchers/BatchingDispatcherQueueDispatcher.cs +++ b/src/DynamicMvvm.Uno.WinUI/Dispatchers/BatchingDispatcherQueueDispatcher.cs @@ -56,13 +56,13 @@ public async Task ExecuteOnDispatcher(CancellationToken ct, Action action) { if (ct.IsCancellationRequested) { - this.Log().LogDebug($"Cancelled 'ExecuteOnDispatcher' because of the cancellation token."); + this.Log().LogCancelledExecuteOnDispatcherBecauseOfCancellationToken(); return; } if (GetHasDispatcherAccess()) { - this.Log().LogDebug($"Executed action immediately because already on dispatcher."); + this.Log().LogExecutedActionImmediatelyBecauseAlreadyOnDispatcher(); action(); return; } @@ -94,7 +94,8 @@ await _dispatcherQueue.RunAsync(DispatcherQueuePriority.High, () => { if (request.CT.IsCancellationRequested) { - this.Log().LogDebug($"Cancelled 'ExecuteOnDispatcher' because of the cancellation token."); + this.Log().LogCancelledExecuteOnDispatcherBecauseOfCancellationToken(); + continue; } @@ -102,12 +103,12 @@ await _dispatcherQueue.RunAsync(DispatcherQueuePriority.High, () => } catch (Exception e) { - this.Log().LogError(e, "Failed 'ExecuteOnDispatcher'."); + this.Log().LogFailedExecuteOnDispatcher(e); } } }); - this.Log().LogDebug($"Batched {requests.Length} dispatcher requests."); + this.Log().LogBatchedDispatcherRequests(requests.Length); } private class Request diff --git a/src/DynamicMvvm.Uno.WinUI/Dispatchers/DispatcherQueueDispatcher.cs b/src/DynamicMvvm.Uno.WinUI/Dispatchers/DispatcherQueueDispatcher.cs index 3a654e8..494fa14 100644 --- a/src/DynamicMvvm.Uno.WinUI/Dispatchers/DispatcherQueueDispatcher.cs +++ b/src/DynamicMvvm.Uno.WinUI/Dispatchers/DispatcherQueueDispatcher.cs @@ -51,7 +51,7 @@ public async Task ExecuteOnDispatcher(CancellationToken ct, Action action) { if (ct.IsCancellationRequested) { - this.Log().LogDebug($"Cancelled 'ExecuteOnDispatcher' because of the cancellation token."); + this.Log().LogCancelledExecuteOnDispatcherBecauseOfCancellationToken(); return; } @@ -61,7 +61,7 @@ public async Task ExecuteOnDispatcher(CancellationToken ct, Action action) { if (ct.IsCancellationRequested) { - this.Log().LogDebug($"Cancelled 'ExecuteOnDispatcher' because of the cancellation token."); + this.Log().LogCancelledExecuteOnDispatcherBecauseOfCancellationToken(); return; } @@ -69,7 +69,7 @@ public async Task ExecuteOnDispatcher(CancellationToken ct, Action action) } catch (Exception e) { - this.Log().LogError(e, "Failed 'ExecuteOnDispatcher'."); + this.Log().LogFailedExecuteOnDispatcher(e); } }); } diff --git a/src/DynamicMvvm.Uno.WinUI/DynamicMvvm.Uno.WinUI.csproj b/src/DynamicMvvm.Uno.WinUI/DynamicMvvm.Uno.WinUI.csproj index 75c731b..4de9da1 100644 --- a/src/DynamicMvvm.Uno.WinUI/DynamicMvvm.Uno.WinUI.csproj +++ b/src/DynamicMvvm.Uno.WinUI/DynamicMvvm.Uno.WinUI.csproj @@ -1,6 +1,6 @@  - net6.0-windows10.0.18362;netstandard2.0;net6.0-android;net6.0-ios;net6.0-macos;net6.0-maccatalyst; + net6.0-windows10.0.18362;netstandard2.0;net6.0-android;net6.0-ios;net6.0-maccatalyst; 10.0 true diff --git a/src/DynamicMvvm.Uno.WinUI/LoggerMessages.Uno.WinUI.cs b/src/DynamicMvvm.Uno.WinUI/LoggerMessages.Uno.WinUI.cs new file mode 100644 index 0000000..f3edc3d --- /dev/null +++ b/src/DynamicMvvm.Uno.WinUI/LoggerMessages.Uno.WinUI.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Chinook.DynamicMvvm +{ + internal static partial class LoggerMessagesUnoWinUI + { + [LoggerMessage(401, LogLevel.Debug, "Cancelled 'ExecuteOnDispatcher' because of the cancellation token.")] + public static partial void LogCancelledExecuteOnDispatcherBecauseOfCancellationToken(this ILogger logger); + + [LoggerMessage(402, LogLevel.Debug, "Executed action immediately because already on dispatcher.")] + public static partial void LogExecutedActionImmediatelyBecauseAlreadyOnDispatcher(this ILogger logger); + + [LoggerMessage(403, LogLevel.Debug, "Batched {RequestCount} dispatcher requests.")] + public static partial void LogBatchedDispatcherRequests(this ILogger logger, int requestCount); + + [LoggerMessage(404, LogLevel.Error, "Failed 'ExecuteOnDispatcher'.")] + public static partial void LogFailedExecuteOnDispatcher(this ILogger logger, Exception exception); + } +} diff --git a/src/DynamicMvvm/Command/DynamicCommand.cs b/src/DynamicMvvm/Command/DynamicCommand.cs index 123f5fc..042725a 100644 --- a/src/DynamicMvvm/Command/DynamicCommand.cs +++ b/src/DynamicMvvm/Command/DynamicCommand.cs @@ -75,7 +75,7 @@ public async Task Execute(object parameter) if (_isDisposed) { // Note that we don't throw an ObjectDisposedException here because we're likely on a UI thread. - this.Log().LogError("Failed to execute command '{Name}' because it's disposed.", Name); + this.Log().LogCommandFailedBecauseDisposed(Name); return; } @@ -99,7 +99,7 @@ public async Task Execute(object parameter) { // This will run on a UI thread, so we want to make sure // this task doesn't throw otherwise it could lead to a crash. - this.Log().LogError(e, $"Command execution of '{Name}' failed. Consider using {nameof(ErrorHandlerCommandStrategy)}."); + this.Log().LogCommandFailedConsiderUsingErrorHandlerCommandStrategy(Name, e); } } diff --git a/src/DynamicMvvm/Command/Strategies/LoggerCommandStrategy.cs b/src/DynamicMvvm/Command/Strategies/LoggerCommandStrategy.cs index 61b0595..4a63e3b 100644 --- a/src/DynamicMvvm/Command/Strategies/LoggerCommandStrategy.cs +++ b/src/DynamicMvvm/Command/Strategies/LoggerCommandStrategy.cs @@ -40,15 +40,15 @@ public override async Task Execute(CancellationToken ct, object parameter, IDyna { try { - _logger.LogDebug($"Executing command '{command.Name}'."); + _logger.LogCommandExecuting(command.Name); await base.Execute(ct, parameter, command); - _logger.LogInformation($"Executed command '{command.Name}'."); + _logger.LogCommandExecuted(command.Name); } catch (Exception e) { - _logger.LogError(e, $"Failed to execute command '{command.Name}'."); + _logger.LogCommandFailed(command.Name, e); throw; } diff --git a/src/DynamicMvvm/Deactivation/DeactivatableDynamicPropertyFromObservable.cs b/src/DynamicMvvm/Deactivation/DeactivatableDynamicPropertyFromObservable.cs index 952fdc3..52225b1 100644 --- a/src/DynamicMvvm/Deactivation/DeactivatableDynamicPropertyFromObservable.cs +++ b/src/DynamicMvvm/Deactivation/DeactivatableDynamicPropertyFromObservable.cs @@ -72,7 +72,7 @@ public void Deactivate() IsDeactivated = true; - typeof(IDeactivatable).Log().LogDebug($"Deactivated observable source of property '{Name}'."); + typeof(IDeactivatable).Log().LogDeactivatedObservableSource(Name); } /// @@ -87,7 +87,7 @@ public void Reactivate() _subscription = _source.Subscribe(_propertyObserver); - typeof(IDeactivatable).Log().LogDebug($"Reactivated observable source of property '{Name}'."); + typeof(IDeactivatable).Log().LogReactivatedObservableSource(Name); } } } diff --git a/src/DynamicMvvm/Deactivation/DeactivatableViewModelBase.cs b/src/DynamicMvvm/Deactivation/DeactivatableViewModelBase.cs index da2b7bd..bcf487b 100644 --- a/src/DynamicMvvm/Deactivation/DeactivatableViewModelBase.cs +++ b/src/DynamicMvvm/Deactivation/DeactivatableViewModelBase.cs @@ -84,7 +84,7 @@ public virtual void Deactivate() child.Deactivate(); } - typeof(IDeactivatable).Log().LogDebug($"Deactivated ViewModel '{Name}'."); + typeof(IDeactivatable).Log().LogDeactivatedViewModel(Name); } /// @@ -116,7 +116,7 @@ public virtual void Reactivate() child.Reactivate(); } - typeof(IDeactivatable).Log().LogDebug($"Reactivated ViewModel '{Name}' and raised {toRaise.Length} property changes."); + typeof(IDeactivatable).Log().LogReactivatedViewModel(Name, toRaise.Length); } } } diff --git a/src/DynamicMvvm/DynamicMvvm.csproj b/src/DynamicMvvm/DynamicMvvm.csproj index 551645f..f0ec937 100644 --- a/src/DynamicMvvm/DynamicMvvm.csproj +++ b/src/DynamicMvvm/DynamicMvvm.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 10 Chinook.DynamicMvvm nventive nventive @@ -29,8 +30,6 @@ - - diff --git a/src/DynamicMvvm/LoggerMessages.cs b/src/DynamicMvvm/LoggerMessages.cs new file mode 100644 index 0000000..a8e0ba5 --- /dev/null +++ b/src/DynamicMvvm/LoggerMessages.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.Logging; + +namespace Chinook.DynamicMvvm +{ + internal static partial class LoggerMessages + { + [LoggerMessage(1, LogLevel.Information, "ViewModel '{ViewModelName}' created.")] + public static partial void LogViewModelCreated(this ILogger logger, string viewModelName); + + [LoggerMessage(2, LogLevel.Information, "ViewModel '{ViewModelName}' disposed.")] + public static partial void LogViewModelDisposed(this ILogger logger, string viewModelName); + + [LoggerMessage(3, LogLevel.Debug, "Disposing ViewModel '{ViewModelName}'.")] + public static partial void LogViewModelDisposing(this ILogger logger, string viewModelName); + + [LoggerMessage(4, LogLevel.Error, "Failed to dispose disposable '{DisposableKey}' of ViewModel '{ViewModelName}'.")] + public static partial void LogViewModelFailedToDisposeDisposable(this ILogger logger, string disposableKey, string viewModelName, Exception exception); + + [LoggerMessage(5, LogLevel.Information, "ViewModel '{ViewModelName}' destroyed.")] + public static partial void LogViewModelDestroyed(this ILogger logger, string viewModelName); + + [LoggerMessage(6, LogLevel.Debug, "Skipped '{MethodName}' on ViewModel '{ViewModelName}' because it's disposing.")] + public static partial void LogViewModelSkippedMethodBecauseDisposing(this ILogger logger, string methodName, string viewModelName); + + [LoggerMessage(7, LogLevel.Debug, "Skipped '{MethodName}' for key '{DisposableKey}' on ViewModel '{ViewModelName}' because it's disposing.")] + public static partial void LogViewModelSkippedMethodBecauseDisposing_DisposableKey(this ILogger logger, string methodName, string disposableKey, string viewModelName); + + [LoggerMessage(8, LogLevel.Debug, "Skipped '{MethodName}' for '{TypeName}.{PropertyName}' on ViewModel '{ViewModelName}' because it's disposing.")] + public static partial void LogViewModelSkippedMethodBecauseDisposing_PropertyName(this ILogger logger, string methodName, string typeName, string propertyName, string viewModelName); + + [LoggerMessage(9, LogLevel.Debug, "Skipped '{MethodName}' for '{TypeName}.{PropertyName}' on ViewModel '{ViewModelName}' because it's disposed.")] + public static partial void LogViewModelSkippedMethodBecauseDisposed_PropertyName(this ILogger logger, string methodName, string typeName, string propertyName, string viewModelName); + + [LoggerMessage(10, LogLevel.Debug, "Raised property changed for '{PropertyName}' from ViewModel '{ViewModelName}'.")] + public static partial void LogViewModelRaisedPropertyChanged(this ILogger logger, string propertyName, string viewModelName); + + [LoggerMessage(11, LogLevel.Error, "Failed to raise PropertyChanged. Your IVewModel.Dispatcher is null. Make sure you set it. Make sure you link child viewmodels to their parent by using one of the following IViewModel extension method: AttachChild, GetChild, AttachOrReplaceChild.")] + public static partial void LogViewModelFailedToRaisePropertyChangedWhenDispatcherIsNull(this ILogger logger, Exception exception); + + [LoggerMessage(20, LogLevel.Debug, "Executing command '{CommandName}'.")] + public static partial void LogCommandExecuting(this ILogger logger, string commandName); + + [LoggerMessage(21, LogLevel.Information, "Executed command '{CommandName}'.")] + public static partial void LogCommandExecuted(this ILogger logger, string commandName); + + [LoggerMessage(22, LogLevel.Error, "Failed to execute command '{CommandName}'.")] + public static partial void LogCommandFailed(this ILogger logger, string commandName, Exception exception); + + [LoggerMessage(23, LogLevel.Error, "Failed to execute command '{CommandName}'. Consider using ErrorHandlerCommandStrategy.")] + public static partial void LogCommandFailedConsiderUsingErrorHandlerCommandStrategy(this ILogger logger, string commandName, Exception exception); + + [LoggerMessage(24, LogLevel.Error, "Failed to execute command '{CommandName}' because it's disposed.")] + public static partial void LogCommandFailedBecauseDisposed(this ILogger logger, string commandName); + + [LoggerMessage(30, LogLevel.Error, "Source observable subscription failed for property '{PropertyName}'. The property will no longer be updated by the observable.")] + public static partial void LogDynamicPropertySourceObservableSubscriptionFailed(this ILogger logger, string propertyName, Exception exception); + + [LoggerMessage(31, LogLevel.Error, "Source task failed for property '{PropertyName}'.")] + public static partial void LogDynamicPropertySourceTaskFailed(this ILogger logger, string propertyName, Exception exception); + + [LoggerMessage(40, LogLevel.Debug, "Deactivated observable source of property '{PropertyName}'.")] + public static partial void LogDeactivatedObservableSource(this ILogger logger, string propertyName); + + [LoggerMessage(41, LogLevel.Debug, "Reactivated observable source of property '{PropertyName}'.")] + public static partial void LogReactivatedObservableSource(this ILogger logger, string propertyName); + + [LoggerMessage(42, LogLevel.Debug, "Deactivated ViewModel '{ViewModelName}'.")] + public static partial void LogDeactivatedViewModel(this ILogger logger, string viewModelName); + + [LoggerMessage(43, LogLevel.Debug, "Reactivated ViewModel '{ViewModelName}' and raised {PropertyChangedCount} property changes.")] + public static partial void LogReactivatedViewModel(this ILogger logger, string viewModelName, int propertyChangedCount); + + } +} diff --git a/src/DynamicMvvm/Property/DynamicPropertyFromObservable.cs b/src/DynamicMvvm/Property/DynamicPropertyFromObservable.cs index 486ebe5..f60bd55 100644 --- a/src/DynamicMvvm/Property/DynamicPropertyFromObservable.cs +++ b/src/DynamicMvvm/Property/DynamicPropertyFromObservable.cs @@ -88,7 +88,7 @@ public void OnCompleted() public void OnError(Exception e) { - this.Log().LogError(e, $"Source observable subscription failed for property '{_owner.Name}'. The property will no longer be updated by the observable."); + this.Log().LogDynamicPropertySourceObservableSubscriptionFailed(_owner.Name, e); } public void OnNext(T value) diff --git a/src/DynamicMvvm/Property/DynamicPropertyFromTask.cs b/src/DynamicMvvm/Property/DynamicPropertyFromTask.cs index d0a1dac..e03b3df 100644 --- a/src/DynamicMvvm/Property/DynamicPropertyFromTask.cs +++ b/src/DynamicMvvm/Property/DynamicPropertyFromTask.cs @@ -68,7 +68,7 @@ private async Task SetValueFromSource(CancellationToken ct, Func @@ -147,7 +147,7 @@ public void Dispose() _diagnostics.Write("Destroyed", Name); } - _logger.LogInformation($"ViewModel '{Name}' destroyed."); + _logger.LogViewModelDestroyed(Name); } } } diff --git a/src/DynamicMvvm/ViewModel/ViewModelBase.Errors.cs b/src/DynamicMvvm/ViewModel/ViewModelBase.Errors.cs index e99d11d..24b6af3 100644 --- a/src/DynamicMvvm/ViewModel/ViewModelBase.Errors.cs +++ b/src/DynamicMvvm/ViewModel/ViewModelBase.Errors.cs @@ -45,7 +45,7 @@ public void SetErrors(string propertyName, IEnumerable errors) if (_isDisposing) { - _logger.LogDebug($"Skipped '{nameof(SetErrors)}' for '{GetType().Name}.{propertyName}' on ViewModel '{Name}' because it's disposing."); + _logger.LogViewModelSkippedMethodBecauseDisposing_PropertyName(nameof(SetErrors), GetType().Name, propertyName, Name); return; } @@ -61,7 +61,7 @@ public void SetErrors(IDictionary> errors) if (_isDisposing) { - _logger.LogDebug($"Skipped '{nameof(SetErrors)}' for ViewModel '{Name}' because it's disposing."); + _logger.LogViewModelSkippedMethodBecauseDisposing(nameof(SetErrors), Name); return; } @@ -77,7 +77,7 @@ public void ClearErrors(string propertyName = null) if (_isDisposing) { - _logger.LogDebug($"Skipped '{nameof(ClearErrors)}' for '{GetType().Name}.{propertyName}' on ViewModel '{Name}' because it's disposing."); + _logger.LogViewModelSkippedMethodBecauseDisposing_PropertyName(nameof(ClearErrors), GetType().Name, propertyName, Name); return; } diff --git a/src/DynamicMvvm/ViewModel/ViewModelBase.PropertyChanged.cs b/src/DynamicMvvm/ViewModel/ViewModelBase.PropertyChanged.cs index afc389b..6a9209f 100644 --- a/src/DynamicMvvm/ViewModel/ViewModelBase.PropertyChanged.cs +++ b/src/DynamicMvvm/ViewModel/ViewModelBase.PropertyChanged.cs @@ -17,7 +17,7 @@ public virtual void RaisePropertyChanged(string propertyName) if (_isDisposing) { - _logger.LogDebug($"Skipped '{nameof(RaisePropertyChanged)}' for '{GetType().Name}.{propertyName}' on ViewModel '{Name}' because it's disposing."); + _logger.LogViewModelSkippedMethodBecauseDisposing_PropertyName(nameof(RaisePropertyChanged), GetType().Name, propertyName, Name); return; } @@ -40,13 +40,13 @@ private void RaisePropertyChangedInner(string propertyName) { if (_isDisposing) { - _logger.LogDebug($"Skipped '{nameof(RaisePropertyChangedInner)}' for '{GetType().Name}.{propertyName}' on ViewModel '{Name}' because it's disposing."); + _logger.LogViewModelSkippedMethodBecauseDisposing_PropertyName(nameof(RaisePropertyChangedInner), GetType().Name, propertyName, Name); return; } if (_isDisposed) { - _logger.LogDebug($"Skipped '{nameof(RaisePropertyChangedInner)}' for '{GetType().Name}.{propertyName}' on ViewModel '{Name}' because it's disposed."); + _logger.LogViewModelSkippedMethodBecauseDisposed_PropertyName(nameof(RaisePropertyChangedInner), GetType().Name, propertyName, Name); return; } @@ -57,11 +57,11 @@ private void RaisePropertyChangedInner(string propertyName) catch (Exception exception) when (Dispatcher is null) { // Give some details and tips on how to fix the issue. - _logger.LogError(exception, "Failed to raise PropertyChanged. Your IVewModel.Dispatcher is null. Make sure you set it. Make sure you link child viewmodels to their parent by using one of the following IViewModel extension method: AttachChild, GetChild, AttachOrReplaceChild."); + _logger.LogViewModelFailedToRaisePropertyChangedWhenDispatcherIsNull(exception); throw; } - _logger.LogDebug($"Raised property changed for '{propertyName}' from ViewModel '{Name}'."); + _logger.LogViewModelRaisedPropertyChanged(propertyName, Name); } } } diff --git a/src/DynamicMvvm/ViewModel/ViewModelBase.cs b/src/DynamicMvvm/ViewModel/ViewModelBase.cs index b0c5285..2cd5123 100644 --- a/src/DynamicMvvm/ViewModel/ViewModelBase.cs +++ b/src/DynamicMvvm/ViewModel/ViewModelBase.cs @@ -33,7 +33,7 @@ public ViewModelBase(string name = null, IServiceProvider serviceProvider = null _diagnostics.Write("Created", Name); } - _logger.LogInformation($"ViewModel '{Name}' created."); + _logger.LogViewModelCreated(Name); } ///