Created
February 5, 2020 12:26
-
-
Save CaCTuCaTu4ECKuu/8c5c8b4717e27ce4fe6b054cff839dcc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Collections.Generic; | |
using System.Threading.Tasks; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Logging; | |
namespace Vkm.Core.Events | |
{ | |
using Infrastructure; | |
/// <summary> | |
/// Служба доменных событий | |
/// </summary> | |
public class DomainEventManager | |
{ | |
private readonly IServiceProvider _serviceProvider; | |
private readonly ILogger _logger; | |
private Dictionary<Type, Dictionary<Type, IDomainEventHandler>> _handlers; | |
public DomainEventManager(IServiceProvider serviceProvider) | |
{ | |
_serviceProvider = serviceProvider; | |
_logger = _serviceProvider.GetRequiredService<ILogger<DomainEventManager>>(); | |
_handlers = new Dictionary<Type, Dictionary<Type, IDomainEventHandler>>(); | |
ReloadAssemblyEvents(); | |
} | |
public void ReloadAssemblyEvents(ITypeFinder typeFinder = null) | |
{ | |
// TODO: save subscriptions somewhere to load them again | |
var finder = typeFinder ?? AppDomainTypeFinder.Global; | |
Dictionary<Type, Dictionary<Type, IDomainEventHandler>> handlers = new Dictionary<Type, Dictionary<Type, IDomainEventHandler>>(); | |
var eventTypes = finder.FindClassesOfType<DomainEvent>(true); | |
foreach (var eventType in eventTypes) | |
{ | |
var genericHandler = typeof(DomainEventHandler<>).MakeGenericType(eventType); | |
var typeHandlers = finder.FindClassesOfType(genericHandler, true); | |
var eventHandlers = typeHandlers | |
.Select(h => Activator.CreateInstance(h) as IDomainEventHandler) | |
.OrderBy(h => h.Order) | |
.ToDictionary(t => t.GetType(), h => h); | |
handlers.Add(eventType, eventHandlers); | |
} | |
lock (_handlers) | |
{ | |
_handlers = handlers; | |
} | |
} | |
/// <summary> | |
/// Subscribe handler for defined <see cref="DomainEvent"/>. If <see cref="IDomainEventHandler"/> presented replace it with new | |
/// </summary> | |
/// <typeparam name="TEvent"></typeparam> | |
/// <typeparam name="IDomainEventHandler"></typeparam> | |
public void Subscribe<TEvent, THandler>() | |
where TEvent: DomainEvent | |
where THandler : DomainEventHandler<TEvent>, new() | |
{ | |
// TODO: save subscriptions somewhere to load them again | |
lock (_handlers) | |
{ | |
if (!_handlers.ContainsKey(typeof(TEvent))) | |
{ | |
_handlers[typeof(TEvent)] = new Dictionary<Type, IDomainEventHandler>(); | |
} | |
if (!_handlers[typeof(TEvent)].ContainsKey(typeof(THandler))) | |
{ | |
_handlers[typeof(TEvent)].Add(typeof(THandler), new THandler()); | |
_logger?.LogTrace($"{this.GetType().Name}: Subscribe {typeof(THandler).Name} to handle {typeof(TEvent).Name} events."); | |
} | |
} | |
} | |
public void Unsubscribe<TEvent, THandler>() | |
where TEvent : DomainEvent | |
where THandler : DomainEventHandler<TEvent>, new() | |
{ | |
// TODO: save subscriptions somewhere to load them again | |
lock (_handlers) | |
{ | |
if (!_handlers.ContainsKey(typeof(TEvent))) | |
{ | |
if (_handlers[typeof(TEvent)].ContainsKey(typeof(THandler))) | |
{ | |
_handlers[typeof(TEvent)].Remove(typeof(THandler)); | |
_logger?.LogTrace($"Unsubscribe \"{typeof(THandler).Name}\" from handling \"{typeof(TEvent).Name}\" events."); | |
return; | |
} | |
} | |
} | |
_logger?.LogTrace($"Attemp to unsubscribe not subscribed \"{typeof(THandler).Name}\" from handling \"{typeof(TEvent).Name}\" events."); | |
} | |
public async Task PublishAsync<TEvent>(TEvent @event) | |
where TEvent: DomainEvent | |
{ | |
_logger?.LogTrace($"Event {@event.ID} ({@event.GetType().Name}): Start handling.{(@event.ParentEvent != null ? $" Initiated by {@event.ParentEvent.ID} ({@event.ParentEvent.GetType().Name})" : "")}"); | |
var watch = Stopwatch.StartNew(); | |
Type eType = typeof(TEvent); | |
if (_handlers.ContainsKey(eType) && _handlers[eType].Any()) | |
{ | |
using (var scope = _serviceProvider.CreateScope()) | |
{ | |
bool error = false; | |
foreach (var e in _handlers[typeof(TEvent)]) | |
{ | |
_logger?.LogTrace($"Event {@event.ID}: send to \"{e.Key.Name}\"."); | |
var hWatch = Stopwatch.StartNew(); | |
try | |
{ | |
await e.Value.Handle(scope, @event).ConfigureAwait(false); | |
} | |
catch (Exception ex) | |
{ | |
error = true; | |
_logger?.LogError($"Error while processing {e.Value.GetType().Name}.{Environment.NewLine}{(ex.InnerException != null ? ex.InnerException.Message : ex.Message)}"); | |
} | |
hWatch.Stop(); | |
_logger?.LogTrace($"Event {@event.ID}: {(@event.Handled ? "Handled" : "Not handled")} by \"{e.Key.Name}\" in {hWatch.ElapsedMilliseconds} ms."); | |
} | |
watch.Stop(); | |
_logger?.LogTrace($"Event {@event.ID}: {(@event.Handled ? "Handled" : "Not handled")} in {watch.ElapsedMilliseconds} ms."); | |
if (error) | |
_logger?.LogWarning($"Event {@event.ID}: One or multiple errors occured while handling."); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment