using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using RabbitMQ.Client.Events; using xCommons.Extensions; using xEventService.Configuration; using xEventService.Extensions; using xEventService.Interfaces; using xEventService.Models; using xExceptions.Constants; namespace xEventService.Providers { public sealed class XEventBus : IXEventBus { // #region Properties ... private readonly List events; private readonly ILogger logger; private readonly IServiceScopeFactory serviceScopeFactory; private readonly XEventServiceConfiguration configuration; private readonly IDictionary> handlers; #endregion // #region Constructors ... public XEventBus ( ILoggerFactory loggerFactory, IServiceScopeFactory serviceScopeFactory, XEventServiceConfiguration configuration ) { // this.configuration = configuration; this.serviceScopeFactory = serviceScopeFactory; // events = new List (); handlers = new Dictionary> (); logger = loggerFactory.CreateLogger (); } #endregion // #region Abstracts ... public bool Publish (T @event) where T : XEvent { // // Validate Args ... if (@event.IsNull ()) { return false; } // // Prepare Channel ... XChannel channel = null; // // Extract event name ... var exchangeName = @event.GetType ().Name; // // Do Action ... try { // // Get XChannel Model ... channel = configuration.CreateChannel (exchangeName); // // Publish Command ... channel.Publish (message: @event); // // Return Result ... return true; } catch (Exception ex) { // logger.LogError ($"XEventService Exception: Publishing Failed {exchangeName}/{ex.Message} ..."); // return false; } finally { // if (!channel.IsNull ()) { channel.Dispose (); } } } public bool Subscribe () where TEvent : XEvent where THandler : IXEventHandler { // // Extract required data ... var eventType = typeof (TEvent); var eventName = eventType.Name; var handlerType = typeof (THandler); // // Check event exists in list or not ... if (!events.Contains (eventType)) { // // Add it if not exists ... events.Add (eventType); } // // Check handlers Dictionary contains list or not ... if (!handlers.ContainsKey (eventName)) { handlers.Add (eventName, new List ()); } // // Check handlers Subscribed before or not ... var isHandlerSubscribed = handlers[eventName] .Any (h => h.GetType () == handlerType); if (isHandlerSubscribed) { // logger.LogError ($"XEventService Exception: Duplicate Event Handler Subscription {eventName}/{handlerType.Name} ..."); // return false; } // // Register Handler ... handlers[eventName].Add (handlerType); // // Do Consuming Handler ... ConsumeEvent (); // return true; } #endregion // #region Private ... /// /// Consume Specific Event ... /// /// private void ConsumeEvent () where TEvent : XEvent { // // Extract required data ... var eventType = typeof (TEvent); var eventName = eventType.Name; // // Create ChannelObject ... var channel = new XChannel ( exchangeName: eventName, configuration: configuration, dispatchConsumersAsync: true ); // // Create Consumer ... var consumer = new AsyncEventingBasicConsumer (channel.Channel); // // Set Consumer Delegate ... consumer.Received += XConsumerReceivedDelegate; // // Consume Async Consumer ... channel.ConsumeAsync (consumer); } /// /// this Delegate method calls when a message recieved ... /// private async Task XConsumerReceivedDelegate ( object sender, BasicDeliverEventArgs ea ) { // // Generate Required Data ... var eventName = ea.Exchange; var json = Encoding.UTF8 .GetString ( ea.Body .ToArray () ); // // Do Processing Event ... try { // // Process Event in non Blocking Tasks ... await ProcessEvent (eventName, json) .ConfigureAwait (false); } catch (Exception ex) { // logger.LogError ($"XEventService Exception: Processing Event failed {eventName}/{ex.Message} ..."); // XException.ActionFailed.Throw (); } finally { // var channel = ((EventingBasicConsumer) sender).Model; channel.BasicAck ( deliveryTag: ea.DeliveryTag, multiple: false ); } } /// /// Here we must call registered Event Handlers to Handle the Event ... /// private async Task ProcessEvent ( string eventName, string json ) { // // Check Handler Exists for current Event ... var isExistsHandler = handlers.ContainsKey (eventName); if (!isExistsHandler) { return; } // // Using Service Scope Factory ... using (var scope = serviceScopeFactory.CreateScope ()) { // // Get Handler Subscriptions ... var subscriptions = handlers[eventName]; foreach (var subscription in subscriptions) { // // Inject Handler from Dependency Injections ... var handler = scope.ServiceProvider.GetService (subscription); if (handler.IsNull ()) { // logger.LogInformation ($"XEventService: there is no handler Registered in DependencyInjection for {eventName}/{subscription.Name}"); // continue; } // // Retrieve Event Type ... var eventType = events.SingleOrDefault (t => t.Name == eventName); var @event = json.FromJSON (eventType); var conreteType = typeof (IXEventHandler<>).MakeGenericType (eventType); // // Call EventHandler 'HandleAsync' method ... await ((Task) conreteType .GetMethod (nameof (IXEventHandler.HandleAsync)) .Invoke (handler, new object[] { @event })) .ConfigureAwait (true); } } } #endregion } }