From a3065b25eeab9281b74bd34fe2628ba11906b1e9 Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Thu, 18 Jan 2024 16:54:38 +0000 Subject: [PATCH] Medistor reworked to handle ui threading in a cleaner way --- .../Lifecycles/MediaController.cs | 8 ++-- .../Lifecycles/MediaControllerHandler.cs | 10 ++--- .../Views/MediaInformationViewModel.cs | 4 +- .../PrimaryWidgetConfigurationHandler.cs | 6 +-- .../WidgetComponentViewModelFactory.cs | 1 + .../Lifecycles/ConfigurationChangedHandler.cs | 4 +- .../KeyAcceleratorHandler.cs | 6 +-- .../StartProcessHandler.cs | 6 +-- .../IServiceCollectionExtensions.cs | 24 +++++------ Hyperbar/Lifecycles/AppService.cs | 1 - ...eHandlerDelegate.cs => HandlerDelegate.cs} | 2 +- ...assHandlerWrapper.cs => HandlerWrapper.cs} | 12 +++--- Hyperbar/Mediators/IHandler.cs | 8 ++-- Hyperbar/Mediators/IMediator.cs | 18 +++++--- Hyperbar/Mediators/INotificationHandler.cs | 2 +- Hyperbar/Mediators/IPipelineBehavior.cs | 4 +- Hyperbar/Mediators/Mediator.cs | 43 +++++++++++-------- .../Mediators/NotficationPipelineHandler.cs | 15 ------- .../Views/ObservableCollectionViewModel.cs | 12 +++--- 19 files changed, 91 insertions(+), 95 deletions(-) rename Hyperbar.Windows/{Mediators => Lifecycles}/KeyAcceleratorHandler.cs (70%) rename Hyperbar.Windows/{Mediators => Lifecycles}/StartProcessHandler.cs (60%) rename Hyperbar/Mediators/{MessageHandlerDelegate.cs => HandlerDelegate.cs} (53%) rename Hyperbar/Mediators/{RequestClassHandlerWrapper.cs => HandlerWrapper.cs} (52%) delete mode 100644 Hyperbar/Mediators/NotficationPipelineHandler.cs diff --git a/Hyperbar.Windows.MediaController/Lifecycles/MediaController.cs b/Hyperbar.Windows.MediaController/Lifecycles/MediaController.cs index 0c73287..010cad5 100644 --- a/Hyperbar.Windows.MediaController/Lifecycles/MediaController.cs +++ b/Hyperbar.Windows.MediaController/Lifecycles/MediaController.cs @@ -34,21 +34,21 @@ public class MediaController : disposer.Dispose(this); } - public async ValueTask Handle(Play notification, + public async Task Handle(Play notification, CancellationToken cancellationToken) => await session.TryPlayAsync(); - public async ValueTask Handle(Pause notification, + public async Task Handle(Pause notification, CancellationToken cancellationToken) => await session.TryPauseAsync(); - public async ValueTask Handle(Request notification, + public async Task Handle(Request notification, CancellationToken cancellationToken) { await mediator.PublishAsync(new Changed(), cancellationToken); } - public async ValueTask Handle(Request _, + public async Task Handle(Request _, CancellationToken cancellationToken) { using (await asyncLock) diff --git a/Hyperbar.Windows.MediaController/Lifecycles/MediaControllerHandler.cs b/Hyperbar.Windows.MediaController/Lifecycles/MediaControllerHandler.cs index 3a1d42c..a48d42c 100644 --- a/Hyperbar.Windows.MediaController/Lifecycles/MediaControllerHandler.cs +++ b/Hyperbar.Windows.MediaController/Lifecycles/MediaControllerHandler.cs @@ -4,16 +4,16 @@ namespace Hyperbar.Windows.MediaController; public class MediaControllerHandler(IMediator mediator, IServiceScopeProvider scopeProvider, - ICache cache) : + ICache cache) : INotificationHandler> { - public async ValueTask Handle(Created notification, + public async Task Handle(Created notification, CancellationToken cancellationToken) { if (notification.Value is MediaController mediaController) { if (scopeProvider.TryGet(mediaController, out IServiceScope? serviceScope)) - { + if (serviceScope is not null) { if (serviceScope.ServiceProvider.GetService>() @@ -27,8 +27,6 @@ public class MediaControllerHandler(IMediator mediator, } } } - } } - } -} +} \ No newline at end of file diff --git a/Hyperbar.Windows.MediaController/Views/MediaInformationViewModel.cs b/Hyperbar.Windows.MediaController/Views/MediaInformationViewModel.cs index 3f6035a..cbab067 100644 --- a/Hyperbar.Windows.MediaController/Views/MediaInformationViewModel.cs +++ b/Hyperbar.Windows.MediaController/Views/MediaInformationViewModel.cs @@ -21,7 +21,7 @@ public partial class MediaInformationViewModel(IServiceFactory serviceFactory, public ICommand Initialize => new AsyncRelayCommand(InitializeAsync); - public ValueTask Handle(Changed notification, + public Task Handle(Changed notification, CancellationToken cancellationToken) { if (notification.Value is MediaInformation value) @@ -30,7 +30,7 @@ public partial class MediaInformationViewModel(IServiceFactory serviceFactory, Description = value.Description; } - return ValueTask.CompletedTask; + return Task.CompletedTask; } public async Task InitializeAsync() => diff --git a/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs b/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs index b3efd67..3dc87c2 100644 --- a/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs +++ b/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs @@ -6,7 +6,7 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator, ICache cache) : INotificationHandler> { - public async ValueTask Handle(ConfigurationChanged notification, + public async Task Handle(ConfigurationChanged notification, CancellationToken cancellationToken) { HashSet configurationIds = new(configuration.SelectMany(item => new[] { item } @@ -23,10 +23,10 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator, { if (!cache.ContainsKey(item.Id)) { - if (factory.Create(item) is IWidgetComponentViewModel value) + if (factory.Create(item) is IWidgetComponentViewModel viewModel) { await mediator.PublishAsync(Inserted - .For(item.Order, value), + .For(item.Order, viewModel), cancellationToken); } } diff --git a/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs b/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs index f7d03d8..957e5a8 100644 --- a/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs +++ b/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs @@ -25,6 +25,7 @@ public class WidgetComponentViewModelFactory(IServiceFactory service, if (processCommandConfiguration.Commands is { Count: > 0 } childCommandConfigurations) { List childViewModels = []; + foreach (PrimaryCommandConfiguration childCommandConfiguration in childCommandConfigurations) { WidgetComponentViewModel? childViewModel = null; diff --git a/Hyperbar.Windows/Lifecycles/ConfigurationChangedHandler.cs b/Hyperbar.Windows/Lifecycles/ConfigurationChangedHandler.cs index 67c9e4a..a2541e5 100644 --- a/Hyperbar.Windows/Lifecycles/ConfigurationChangedHandler.cs +++ b/Hyperbar.Windows/Lifecycles/ConfigurationChangedHandler.cs @@ -6,9 +6,9 @@ public class AppConfigurationChangedHandler(DesktopBar desktopFlyout, AppConfiguration configuration) : INotificationHandler> { - public ValueTask Handle(ConfigurationChanged notification, CancellationToken cancellationToken) + public Task Handle(ConfigurationChanged notification, CancellationToken cancellationToken) { desktopFlyout.Placement = configuration.Placement; - return ValueTask.CompletedTask; + return Task.CompletedTask; } } diff --git a/Hyperbar.Windows/Mediators/KeyAcceleratorHandler.cs b/Hyperbar.Windows/Lifecycles/KeyAcceleratorHandler.cs similarity index 70% rename from Hyperbar.Windows/Mediators/KeyAcceleratorHandler.cs rename to Hyperbar.Windows/Lifecycles/KeyAcceleratorHandler.cs index 5435cfc..769acbf 100644 --- a/Hyperbar.Windows/Mediators/KeyAcceleratorHandler.cs +++ b/Hyperbar.Windows/Lifecycles/KeyAcceleratorHandler.cs @@ -3,12 +3,12 @@ namespace Hyperbar.Windows; public class KeyAcceleratorHandler(IVirtualKeyboard virtualKeyboard) : - IRequestHandler + IHandler { - public ValueTask Handle(KeyAccelerator request, + public Task Handle(KeyAccelerator request, CancellationToken cancellationToken) { virtualKeyboard.Send((int)request.Key, request.Modifiers?.Select(modifier => (int)modifier).ToArray() ?? []); - return default; + return Task.FromResult(default); } } diff --git a/Hyperbar.Windows/Mediators/StartProcessHandler.cs b/Hyperbar.Windows/Lifecycles/StartProcessHandler.cs similarity index 60% rename from Hyperbar.Windows/Mediators/StartProcessHandler.cs rename to Hyperbar.Windows/Lifecycles/StartProcessHandler.cs index 3898f33..ab720ba 100644 --- a/Hyperbar.Windows/Mediators/StartProcessHandler.cs +++ b/Hyperbar.Windows/Lifecycles/StartProcessHandler.cs @@ -3,12 +3,12 @@ namespace Hyperbar.Windows; public class StartProcessHandler : - IRequestHandler + IHandler { - public ValueTask Handle(StartProcess request, + public Task Handle(StartProcess request, CancellationToken cancellationToken) { Process.Start(request.Process); - return default; + return Task.FromResult(default); } } diff --git a/Hyperbar/Extensions/IServiceCollectionExtensions.cs b/Hyperbar/Extensions/IServiceCollectionExtensions.cs index e57af4d..645b36a 100644 --- a/Hyperbar/Extensions/IServiceCollectionExtensions.cs +++ b/Hyperbar/Extensions/IServiceCollectionExtensions.cs @@ -138,9 +138,8 @@ public static class IServiceCollectionExtensions { Type notificationType = arguments[0]; - services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); - services.Add(new ServiceDescriptor(typeof(INotificationHandler<>).MakeGenericType(notificationType), - provider => provider.GetRequiredService(), lifetime)); + //services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); + services.Add(new ServiceDescriptor(typeof(INotificationHandler<>).MakeGenericType(notificationType), typeof(THandler), lifetime)); } } @@ -151,7 +150,7 @@ public static class IServiceCollectionExtensions Type requestType = arguments[0]; Type responseType = arguments[1]; - Type wrapperType = typeof(RequestClassHandlerWrapper<,>).MakeGenericType(requestType, responseType); + Type wrapperType = typeof(HandlerWrapper<,>).MakeGenericType(requestType, responseType); services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); services.Add(new ServiceDescriptor(wrapperType, @@ -169,14 +168,15 @@ public static class IServiceCollectionExtensions return services; } - public static IServiceCollection AddNotificationPipeline(this IServiceCollection services) - where TFromNotification : - INotification - where TToNotification : - INotification, new() - { - return services.AddHandler>(); - } + //public static IServiceCollection AddNotificationPipeline(this IServiceCollection services) + // where TFromNotification : + // INotification + // where TToNotification : + // INotification, new() + //{ + // return services.AddHandler>(); + //} + public static IServiceCollection AddWidgetTemplate(this IServiceCollection services) where TWidgetContent : IWidgetViewModel diff --git a/Hyperbar/Lifecycles/AppService.cs b/Hyperbar/Lifecycles/AppService.cs index ce7e39d..07d318a 100644 --- a/Hyperbar/Lifecycles/AppService.cs +++ b/Hyperbar/Lifecycles/AppService.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Hosting; -using System; namespace Hyperbar; diff --git a/Hyperbar/Mediators/MessageHandlerDelegate.cs b/Hyperbar/Mediators/HandlerDelegate.cs similarity index 53% rename from Hyperbar/Mediators/MessageHandlerDelegate.cs rename to Hyperbar/Mediators/HandlerDelegate.cs index 3f0271e..dfd4744 100644 --- a/Hyperbar/Mediators/MessageHandlerDelegate.cs +++ b/Hyperbar/Mediators/HandlerDelegate.cs @@ -1,6 +1,6 @@ namespace Hyperbar; -public delegate ValueTask MessageHandlerDelegate(TMessage message, +public delegate Task HandlerDelegate(TMessage message, CancellationToken cancellationToken) where TMessage : notnull, diff --git a/Hyperbar/Mediators/RequestClassHandlerWrapper.cs b/Hyperbar/Mediators/HandlerWrapper.cs similarity index 52% rename from Hyperbar/Mediators/RequestClassHandlerWrapper.cs rename to Hyperbar/Mediators/HandlerWrapper.cs index da5e11e..5b51b03 100644 --- a/Hyperbar/Mediators/RequestClassHandlerWrapper.cs +++ b/Hyperbar/Mediators/HandlerWrapper.cs @@ -1,19 +1,19 @@ namespace Hyperbar; -public class RequestClassHandlerWrapper +public class HandlerWrapper where TRequest : class, IRequest { - private readonly MessageHandlerDelegate handler; + private readonly HandlerDelegate handler; - public RequestClassHandlerWrapper(IHandler concreteHandler, + public HandlerWrapper(IHandler concreteHandler, IEnumerable> pipelineBehaviours) { - MessageHandlerDelegate handler = concreteHandler.Handle; + HandlerDelegate handler = concreteHandler.Handle; foreach (IPipelineBehavior? pipeline in pipelineBehaviours.Reverse()) { - MessageHandlerDelegate handlerCopy = handler; + HandlerDelegate handlerCopy = handler; IPipelineBehavior pipelineCopy = pipeline; handler = (TRequest message, CancellationToken cancellationToken) => @@ -23,5 +23,5 @@ public class RequestClassHandlerWrapper this.handler = handler; } - public ValueTask Handle(TRequest request, CancellationToken cancellationToken) => handler(request, cancellationToken); + public Task Handle(TRequest request, CancellationToken cancellationToken) => handler(request, cancellationToken); } \ No newline at end of file diff --git a/Hyperbar/Mediators/IHandler.cs b/Hyperbar/Mediators/IHandler.cs index 5420bf3..b97da05 100644 --- a/Hyperbar/Mediators/IHandler.cs +++ b/Hyperbar/Mediators/IHandler.cs @@ -7,13 +7,11 @@ public interface IHandler : where TRequest : IRequest { - ValueTask Handle(TRequest request, + Task Handle(TRequest request, CancellationToken cancellationToken); } -public interface IRequestHandler : +public interface IHandler : IHandler where TRequest : - IRequest -{ -} \ No newline at end of file + IRequest; \ No newline at end of file diff --git a/Hyperbar/Mediators/IMediator.cs b/Hyperbar/Mediators/IMediator.cs index 96b10c5..d33ff90 100644 --- a/Hyperbar/Mediators/IMediator.cs +++ b/Hyperbar/Mediators/IMediator.cs @@ -2,21 +2,27 @@ public interface IMediator { - ValueTask PublishAsync(TNotification notification, + Task PublishAsync(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification; - ValueTask PublishAsync(CancellationToken cancellationToken = default) + Task PublishAsync(TNotification notification, + Func, Task> marshal, + CancellationToken cancellationToken = default) + where TNotification : + INotification; + + Task PublishAsync(CancellationToken cancellationToken = default) where TNotification : INotification, new(); - ValueTask SendAsync(IRequest request, + Task SendAsync(IRequest request, CancellationToken cancellationToken = default); - ValueTask SendAsync(object message, CancellationToken + Task SendAsync(object message, CancellationToken cancellationToken = default); - - void Subscribe(object subscriber); + + void Subscribe(object handler); } \ No newline at end of file diff --git a/Hyperbar/Mediators/INotificationHandler.cs b/Hyperbar/Mediators/INotificationHandler.cs index 346d75b..75980ad 100644 --- a/Hyperbar/Mediators/INotificationHandler.cs +++ b/Hyperbar/Mediators/INotificationHandler.cs @@ -5,6 +5,6 @@ public interface INotificationHandler : where TNotification : INotification { - ValueTask Handle(TNotification notification, + Task Handle(TNotification notification, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/Hyperbar/Mediators/IPipelineBehavior.cs b/Hyperbar/Mediators/IPipelineBehavior.cs index 036a8d2..8b357fa 100644 --- a/Hyperbar/Mediators/IPipelineBehavior.cs +++ b/Hyperbar/Mediators/IPipelineBehavior.cs @@ -5,7 +5,7 @@ public interface IPipelineBehavior notnull, IMessage { - ValueTask Handle(TMessage message, - MessageHandlerDelegate next, + Task Handle(TMessage message, + HandlerDelegate next, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/Hyperbar/Mediators/Mediator.cs b/Hyperbar/Mediators/Mediator.cs index 01030d9..47d91d4 100644 --- a/Hyperbar/Mediators/Mediator.cs +++ b/Hyperbar/Mediators/Mediator.cs @@ -7,11 +7,18 @@ public class Mediator(IServiceProvider provider, IDispatcher dispatcher) : IMediator { - private readonly SynchronizationContext? context = SynchronizationContext.Current; - private readonly ConcurrentDictionary> subjects = []; - public async ValueTask PublishAsync(TNotification notification, + public Task PublishAsync(TNotification notification, + CancellationToken cancellationToken = default) + where TNotification : + INotification + { + return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()), cancellationToken); + } + + public Task PublishAsync(TNotification notification, + Func, Task> marshal, CancellationToken cancellationToken = default) where TNotification : INotification @@ -32,19 +39,21 @@ public class Mediator(IServiceProvider provider, foreach (INotificationHandler handler in handlers) { - await dispatcher.InvokeAsync(async () => await handler.Handle(notification, cancellationToken)); + marshal(() => handler.Handle(notification, cancellationToken)); } + + return Task.CompletedTask; } - public ValueTask PublishAsync(CancellationToken cancellationToken = default) + public Task PublishAsync(CancellationToken cancellationToken = default) where TNotification : INotification, - new() => PublishAsync(new TNotification(), cancellationToken); + new() => PublishAsync(new TNotification(), null, cancellationToken); - public ValueTask SendAsync(IRequest request, + public Task SendAsync(IRequest request, CancellationToken cancellationToken = default) { - dynamic? handler = provider.GetService(typeof(RequestClassHandlerWrapper<,>) + dynamic? handler = provider.GetService(typeof(HandlerWrapper<,>) .MakeGenericType(request.GetType(), typeof(TResponse))); if (handler is not null) @@ -52,10 +61,10 @@ public class Mediator(IServiceProvider provider, return handler.Handle((dynamic)request, cancellationToken); } - return default; + return Task.FromResult(default); } - public ValueTask SendAsync(object message, + public Task SendAsync(object message, CancellationToken cancellationToken = default) { if (message.GetType().GetInterface(typeof(IRequest<>).Name) is { } requestType) @@ -64,7 +73,7 @@ public class Mediator(IServiceProvider provider, { Type responseType = arguments[0]; - dynamic? handler = provider.GetService(typeof(RequestClassHandlerWrapper<,>) + dynamic? handler = provider.GetService(typeof(HandlerWrapper<,>) .MakeGenericType(message.GetType(), responseType)); if (handler is not null) @@ -74,12 +83,12 @@ public class Mediator(IServiceProvider provider, } } - return default; + return Task.FromResult(default); } - public void Subscribe(object subject) + public void Subscribe(object handler) { - Type[] interfaceTypes = subject.GetType().GetInterfaces(); + Type[] interfaceTypes = handler.GetType().GetInterfaces(); foreach (Type interfaceType in interfaceTypes.Where(x => x.IsGenericType)) { if (interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>)) @@ -87,10 +96,10 @@ public class Mediator(IServiceProvider provider, if (interfaceType.GetGenericArguments() is { Length: 1 } arguments) { Type notificationType = arguments[0]; - subjects.AddOrUpdate(notificationType, [subject], (value, collection) => + subjects.AddOrUpdate(notificationType, [handler], (value, collection) => { - collection.Add(subject); - return collection; + collection.Add(handler); + return collection; }); } } diff --git a/Hyperbar/Mediators/NotficationPipelineHandler.cs b/Hyperbar/Mediators/NotficationPipelineHandler.cs deleted file mode 100644 index 3ea0f47..0000000 --- a/Hyperbar/Mediators/NotficationPipelineHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Hyperbar; - -public class NotficationPipelineHandler(IMediator mediator) : - INotificationHandler, - IHandler - where TFromNotification : - INotification - where ToNotification : - INotification, new() -{ - private readonly IMediator mediator = mediator; - - public ValueTask Handle(TFromNotification notification, CancellationToken cancellationToken) => - mediator.PublishAsync(new ToNotification(), cancellationToken); -} diff --git a/Hyperbar/Views/ObservableCollectionViewModel.cs b/Hyperbar/Views/ObservableCollectionViewModel.cs index 0ddace7..11bc477 100644 --- a/Hyperbar/Views/ObservableCollectionViewModel.cs +++ b/Hyperbar/Views/ObservableCollectionViewModel.cs @@ -206,7 +206,7 @@ public partial class ObservableCollectionViewModel : IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator(); - public ValueTask Handle(Removed notification, + public Task Handle(Removed notification, CancellationToken cancellationToken) { foreach (TItem item in this.ToList()) @@ -223,10 +223,10 @@ public partial class ObservableCollectionViewModel : } } - return ValueTask.CompletedTask; + return Task.CompletedTask; } - public ValueTask Handle(Created notification, + public Task Handle(Created notification, CancellationToken cancellationToken) { if (notification.Target.Equals(GetType().Name)) @@ -237,10 +237,10 @@ public partial class ObservableCollectionViewModel : } } - return ValueTask.CompletedTask; + return Task.CompletedTask; } - public ValueTask Handle(Inserted notification, + public Task Handle(Inserted notification, CancellationToken cancellationToken) { if (notification.Target.Equals(GetType().Name)) @@ -251,7 +251,7 @@ public partial class ObservableCollectionViewModel : } } - return ValueTask.CompletedTask; + return Task.CompletedTask; } public int IndexOf(TItem item) =>