add keyed based publication to mediator

This commit is contained in:
TheXamlGuy
2024-01-18 20:56:14 +00:00
parent a3065b25ee
commit 78cedcdeb8
15 changed files with 151 additions and 87 deletions
+19
View File
@@ -1,7 +1,25 @@
namespace Hyperbar;
[AttributeUsage(AttributeTargets.Class)]
public class NotificationHandlerAttribute(object key) : Attribute
{
public object Key { get; } = key;
}
public interface IMediator
{
Task PublishAsync<TNotification>(TNotification notification,
object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
Task PublishAsync<TNotification>(object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification,
new();
Task PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
@@ -9,6 +27,7 @@ public interface IMediator
Task PublishAsync<TNotification>(TNotification notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
+53 -19
View File
@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
using System.Reflection;
namespace Hyperbar;
@@ -7,18 +8,37 @@ public class Mediator(IServiceProvider provider,
IDispatcher dispatcher) :
IMediator
{
private readonly ConcurrentDictionary<Type, List<dynamic>> subjects = [];
private readonly ConcurrentDictionary<object, List<dynamic>> subjects = [];
public Task PublishAsync<TNotification>(object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification,
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken);
public Task PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()), cancellationToken);
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
}
public Task PublishAsync<TNotification>(TNotification notification,
object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken);
}
public Task PublishAsync<TNotification>(TNotification notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default)
where TNotification :
INotification
@@ -26,14 +46,11 @@ public class Mediator(IServiceProvider provider,
List<INotificationHandler<TNotification>> handlers =
provider.GetServices<INotificationHandler<TNotification>>().ToList();
foreach (KeyValuePair<Type, List<dynamic>> handler in subjects)
foreach (KeyValuePair<object, List<dynamic>> handler in subjects)
{
if (handler.Key == typeof(TNotification))
if (key is not null && handler.Key.Equals(key) || handler.Key.Equals(typeof(TNotification)))
{
foreach (dynamic value in handler.Value)
{
handlers.Add(value);
}
handlers.Add(handler.Value[0]);
}
}
@@ -48,7 +65,8 @@ public class Mediator(IServiceProvider provider,
public Task PublishAsync<TNotification>(CancellationToken cancellationToken = default)
where TNotification :
INotification,
new() => PublishAsync(new TNotification(), null, cancellationToken);
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
public Task<TResponse?> SendAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default)
@@ -88,21 +106,37 @@ public class Mediator(IServiceProvider provider,
public void Subscribe(object handler)
{
Type[] interfaceTypes = handler.GetType().GetInterfaces();
foreach (Type interfaceType in interfaceTypes.Where(x => x.IsGenericType))
Type handlerType = handler.GetType();
object? key = null;
if (Attribute.GetCustomAttribute(handlerType, typeof(NotificationHandlerAttribute))
is NotificationHandlerAttribute attribute)
{
if (interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>))
if (handlerType.GetProperty($"{attribute.Key}") is PropertyInfo property)
{
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
if (property.GetValue(handler, null) is { } value)
{
Type notificationType = arguments[0];
subjects.AddOrUpdate(notificationType, [handler], (value, collection) =>
{
collection.Add(handler);
return collection;
});
key = value;
}
}
else
{
key = attribute.Key;
}
}
foreach (Type interfaceType in handlerType.GetInterfaces().Where(x => x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(INotificationHandler<>)))
{
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
{
key ??= arguments[0];
subjects.AddOrUpdate(key, [handler], (value, collection) =>
{
collection.Add(handler);
return collection;
});
}
}
}
}
+1 -7
View File
@@ -1,9 +1,3 @@
namespace Hyperbar;
public record Inserted<TValue>(int Index, TValue Value, object Target) : INotification
{
public static Inserted<TValue> For<TTarget>(int index, TValue value)
{
return new Inserted<TValue>(index, value, typeof(TTarget).Name);
}
}
public record Inserted<TValue>(int Index, TValue Value) : INotification;
@@ -229,12 +229,9 @@ public partial class ObservableCollectionViewModel<TItem> :
public Task Handle(Created<TItem> notification,
CancellationToken cancellationToken)
{
if (notification.Target.Equals(GetType().Name))
if (notification.Value is TItem item)
{
if (notification.Value is TItem item)
{
Add(item);
}
Add(item);
}
return Task.CompletedTask;
@@ -243,12 +240,9 @@ public partial class ObservableCollectionViewModel<TItem> :
public Task Handle(Inserted<TItem> notification,
CancellationToken cancellationToken)
{
if (notification.Target.Equals(GetType().Name))
if (notification.Value is TItem item)
{
if (notification.Value is TItem item)
{
Insert(notification.Index, item);
}
Insert(notification.Index, item);
}
return Task.CompletedTask;
+6 -2
View File
@@ -3,14 +3,15 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar;
[NotificationHandler(nameof(Id))]
public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
ITemplateFactory templateFactory,
Guid guid = default,
Guid id,
string? text = null,
string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory)
{
[ObservableProperty]
private IRelayCommand? click = command;
@@ -18,6 +19,9 @@ public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
[ObservableProperty]
private string? icon = icon;
[ObservableProperty]
private Guid id = id;
[ObservableProperty]
private string? text = text;
}
+2 -11
View File
@@ -7,29 +7,20 @@ public partial class WidgetComponentViewModel :
IWidgetComponentViewModel,
ITemplatedViewModel
{
[ObservableProperty]
private Guid id;
public WidgetComponentViewModel(IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
ITemplateFactory templateFactory,
IEnumerable<IWidgetComponentViewModel> items,
Guid id = default) : base(serviceFactory, mediator, disposer, items)
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, mediator, disposer, items)
{
this.id = id;
TemplateFactory = templateFactory;
}
public WidgetComponentViewModel(IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
ITemplateFactory templateFactory,
Guid id = default) : base(serviceFactory, mediator, disposer)
ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer)
{
this.id = id;
TemplateFactory = templateFactory;
}
+6 -2
View File
@@ -3,14 +3,15 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar;
[NotificationHandler(nameof(Id))]
public partial class WidgetMenuViewModel(IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
ITemplateFactory templateFactory,
Guid guid = default,
Guid id = default,
string? text = null,
string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory)
{
[ObservableProperty]
private IRelayCommand? click = command;
@@ -18,6 +19,9 @@ public partial class WidgetMenuViewModel(IServiceFactory serviceFactory,
[ObservableProperty]
private string? icon = icon;
[ObservableProperty]
private Guid id = id;
[ObservableProperty]
private string? text = text;
}
+6 -2
View File
@@ -3,15 +3,16 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar;
[NotificationHandler(nameof(Id))]
public partial class WidgetSplitButtonViewModel(IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
ITemplateFactory templateFactory,
IEnumerable<IWidgetComponentViewModel> items,
Guid guid = default,
Guid id = default,
string? text = null,
string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, items, guid)
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, items)
{
[ObservableProperty]
private IRelayCommand? click = command;
@@ -19,6 +20,9 @@ public partial class WidgetSplitButtonViewModel(IServiceFactory serviceFactory,
[ObservableProperty]
private string? icon = icon;
[ObservableProperty]
private Guid id = id;
[ObservableProperty]
private string? text = text;
}