Skip to content

Instantly share code, notes, and snippets.

@CaCTuCaTu4ECKuu
Created February 5, 2020 12:26
Show Gist options
  • Save CaCTuCaTu4ECKuu/8c5c8b4717e27ce4fe6b054cff839dcc to your computer and use it in GitHub Desktop.
Save CaCTuCaTu4ECKuu/8c5c8b4717e27ce4fe6b054cff839dcc to your computer and use it in GitHub Desktop.
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