public InMemoryCommandBus(InMemoryCommandBusConfiguration?configuration = null,
                                  IScopeFactory?scopeFactory = null)
        {
            if (scopeFactory != null)
            {
                _scope = scopeFactory.CreateScope();
            }

            _logger =
                _scope?.Resolve <ILoggerFactory>()?.CreateLogger <InMemoryCommandBus>()
                ??
                new LoggerFactory(new[] { new DebugLoggerProvider() }).CreateLogger <InMemoryCommandBus>();
            _config = configuration ?? InMemoryCommandBusConfiguration.Default;
        }
        internal InMemoryCommandBus(InMemoryCommandBusConfiguration configuration = null,
                                    IScopeFactory scopeFactory = null)
        {
            if (scopeFactory != null)
            {
                _scope = scopeFactory.CreateScope();
            }

            _logger =
                _scope?.Resolve <ILoggerFactory>()?.CreateLogger <InMemoryCommandBus>()
                ??
                new LoggerFactory().CreateLogger <InMemoryCommandBus>();
            _config = configuration ?? InMemoryCommandBusConfiguration.Default;
        }
        /// <summary>
        /// Dispatch command and context to all handlers.
        /// </summary>
        /// <param name="command">Command to dispatch.</param>
        /// <param name="context">Context associated to command</param>
        public async Task <Result> DispatchAsync(ICommand command, ICommandContext context = null)
        {
            var commandTypeName = command.GetType().FullName;

            _logger.LogInformation($"InMemoryCommandBus : Beginning of dispatching a command of type {commandTypeName}");
            var commandTasks = new List <Task <Result> >();

            _config = _config ?? InMemoryCommandBusConfiguration.Default;
            var ifClause = _config.IfClauses.FirstOrDefault(i => i.Key == command.GetType()).Value;

            if (ifClause?.Invoke(command) == false)
            {
                _logger.LogInformation($"InMemoryCommandBus : If condition for command type {commandTypeName} has returned false.");
                return(Result.Ok());
            }
            var handlers = TryGetHandlerFromIoCContainer(command);

            if (!handlers.Any())
            {
                _logger.LogInformation($"InMemoryCommandBus : Handler for command type {commandTypeName} not found in Ioc container, trying to get it from CoreDispatcher.");
                handlers = TryGetHandlersInstancesFromCoreDispatcher(command);
            }
            if (!handlers.Any())
            {
                _logger.LogInformation($"InMemoryCommandBus : Handler for command type {commandTypeName} not found in CoreDispatcher, trying to instantiate if by reflection.");
                handlers = TryGetHandlersInstancesByReflection(command);
            }
            if (!handlers.Any())
            {
                _logger.LogWarning($"InMemoryCommandBus : No handlers for command type {commandTypeName} were found.");
                _config.OnNoHandlerFounds?.Invoke(command, context);
                return(Result.Fail($"No handlers for command type {commandTypeName} were found."));
            }
            bool manyHandlersAndShouldWait = false;

            if (handlers.Skip(1).Any())
            {
                if (!_config.CommandAllowMultipleHandlers.Any(t => new TypeEqualityComparer().Equals(t.CommandType, command.GetType())))
                {
                    return(Result.Fail($"the command of type {commandTypeName} have multiple handlers within the same process. " +
                                       "If this is expected, you should update your configuration to allow multiple handlers for this specific command type, altough this is not recommended."));
                }
                else
                {
                    manyHandlersAndShouldWait
                        = _config.CommandAllowMultipleHandlers.FirstOrDefault(t => new TypeEqualityComparer().Equals(t.CommandType, command.GetType()))?.ShouldWait ?? false;
                }
            }
            var cmdHandlers = handlers.ToList();

            if (cmdHandlers.Count > 1)
            {
                cmdHandlers = cmdHandlers.OrderByDescending(h => h.GetType().GetCustomAttribute <HandlerPriorityAttribute>()?.Priority ?? 0).ToList();
            }
            foreach (var handler in cmdHandlers)
            {
                _logger.LogInformation($"InMemoryCommandBus : Invocation of handler of type {handlers.GetType().FullName}");
                var handlerType = handler.GetType();
                var method      = handlerType.GetMethods()
                                  .First(m => m.Name == nameof(ICommandHandler <ICommand> .HandleAsync));
                try
                {
                    if (manyHandlersAndShouldWait)
                    {
                        var result = await((Task <Result>)method.Invoke(handler, new object[] { command, context })).ConfigureAwait(false);
                        commandTasks.Add(Task.FromResult(result));
                    }
                    else
                    {
                        var t = (Task <Result>)method.Invoke(handler, new object[] { command, context });
                        commandTasks.Add(t);
                    }
                }
                catch (Exception e)
                {
                    _logger.LogErrorMultilines($"InMemoryCommandBus.DispatchAsync() : Exception when trying to dispatch command {commandTypeName} to handler {handler.GetType().FullName}",
                                               e.ToString());
                    if (handlerType.IsDefined(typeof(CriticalHandlerAttribute)))
                    {
                        Result r = Result.Fail($"Critical handler {handlerType.FullName} has failed, so next ones will not be called");
                        commandTasks.Add(Task.FromResult(r));
                        break;
                    }
                }
            }

            if (!manyHandlersAndShouldWait)
            {
                await Task.WhenAll(commandTasks).ConfigureAwait(false);
            }
            if (commandTasks.Count == 1)
            {
                return(commandTasks[0].Result);
            }
            return(Result.Ok().Combine(commandTasks.Select(t => t.Result).ToArray()));
        }