From 7e7ceb067c8f90d7b0c5192126e4a44883300b77 Mon Sep 17 00:00:00 2001 From: "B. Nordli" Date: Fri, 20 Jan 2017 15:50:32 +0100 Subject: [PATCH] Ensure Initialize is run only once in CommandHandlerInvoker #686 Original issue: ProCoSys/Bifrost#20 --- Source/Bifrost.Specs/Bifrost.Specs.csproj | 1 + ...n_receiving_asynchronous_initialization.cs | 36 +++++++++++++++ .../Bifrost/Commands/CommandHandlerInvoker.cs | 45 +++++++++++++------ 3 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 Source/Bifrost.Specs/Commands/for_CommandHandlerInvoker/when_receiving_asynchronous_initialization.cs diff --git a/Source/Bifrost.Specs/Bifrost.Specs.csproj b/Source/Bifrost.Specs/Bifrost.Specs.csproj index daa7374e2..bdd6eb812 100644 --- a/Source/Bifrost.Specs/Bifrost.Specs.csproj +++ b/Source/Bifrost.Specs/Bifrost.Specs.csproj @@ -106,6 +106,7 @@ + diff --git a/Source/Bifrost.Specs/Commands/for_CommandHandlerInvoker/when_receiving_asynchronous_initialization.cs b/Source/Bifrost.Specs/Commands/for_CommandHandlerInvoker/when_receiving_asynchronous_initialization.cs new file mode 100644 index 000000000..27b017133 --- /dev/null +++ b/Source/Bifrost.Specs/Commands/for_CommandHandlerInvoker/when_receiving_asynchronous_initialization.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using Bifrost.Commands; +using Machine.Specifications; +using Moq; +using It = Machine.Specifications.It; + +namespace Bifrost.Specs.Commands.for_CommandHandlerInvoker +{ + [Subject(Subjects.handling_commands)] + public class when_receiving_asynchronous_initialization : given.a_command_handler_invoker_with_no_command_handlers + { + protected static bool result; + + Because of = () => + { + var command = new Command(); + var thread = new Thread(() => invoker.TryHandle(command)); + + type_discoverer_mock + .Setup(t => t.FindMultiple()) + .Callback( + () => + { + thread.Start(); + Thread.Sleep(50); + }) + .Returns(new Type[0]); + result = invoker.TryHandle(command); + thread.Join(); + }; + + It should_initialize_only_once = () => + type_discoverer_mock.Verify(m => m.FindMultiple(), Times.Once); + } +} diff --git a/Source/Bifrost/Commands/CommandHandlerInvoker.cs b/Source/Bifrost/Commands/CommandHandlerInvoker.cs index 17c5edcfe..0c342dfa3 100644 --- a/Source/Bifrost/Commands/CommandHandlerInvoker.cs +++ b/Source/Bifrost/Commands/CommandHandlerInvoker.cs @@ -24,6 +24,7 @@ public class CommandHandlerInvoker : ICommandHandlerInvoker readonly ITypeDiscoverer _discoverer; readonly IContainer _container; readonly Dictionary _commandHandlers = new Dictionary(); + readonly object _initializationLock = new object(); bool _initialized; /// @@ -38,40 +39,56 @@ public CommandHandlerInvoker(ITypeDiscoverer discoverer, IContainer container) _initialized = false; } - private void Initialize() + void EnsureInitialized() + { + if (_initialized) + { + return; + } + + lock (_initializationLock) + { + if (!_initialized) + { + Initialize(); + _initialized = true; + } + } + } + + void Initialize() { var handlers = _discoverer.FindMultiple(); handlers.ForEach(Register); - _initialized = true; } /// - /// Register a command handler explicitly + /// Register a command handler explicitly /// /// /// - /// The registration process will look into the handler and find methods that + /// The registration process will look into the handler and find methods that /// are called Handle() and takes a command as parameter /// public void Register(Type handlerType) { - var allMethods = handlerType.GetRuntimeMethods().Where(m => m.IsPublic || !m.IsStatic); - var query = from m in allMethods - where m.Name.Equals(HandleMethodName) && - m.GetParameters().Length == 1 && - typeof(ICommand) - .GetTypeInfo().IsAssignableFrom(m.GetParameters()[0].ParameterType.GetTypeInfo()) - select m; + var handleMethods = handlerType + .GetRuntimeMethods() + .Where(m => m.IsPublic || !m.IsStatic) + .Where(m => m.Name.Equals(HandleMethodName)) + .Where(m => m.GetParameters().Length == 1) + .Where(m => typeof(ICommand).GetTypeInfo().IsAssignableFrom(m.GetParameters()[0].ParameterType)); - foreach (var method in query) + foreach (var method in handleMethods) + { _commandHandlers[method.GetParameters()[0].ParameterType] = method; + } } #pragma warning disable 1591 // Xml Comments public bool TryHandle(ICommand command) { - if( !_initialized) - Initialize(); + EnsureInitialized(); var commandType = command.GetType(); if (_commandHandlers.ContainsKey(commandType))