diff --git a/src/Orchard.Tests/Events/EventTests.cs b/src/Orchard.Tests/Events/EventTests.cs index b6bee29ec..8aee2b481 100644 --- a/src/Orchard.Tests/Events/EventTests.cs +++ b/src/Orchard.Tests/Events/EventTests.cs @@ -20,8 +20,15 @@ namespace Orchard.Tests.Events { var builder = new ContainerBuilder(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterInstance(_eventHandler).As(); + + builder.RegisterType() + .Named(typeof(ITestEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler)) + .Named(typeof(IEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler)) + .WithMetadata("Interfaces", typeof(StubEventHandler2).GetInterfaces().ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase)); + builder.RegisterInstance(_eventHandler) + .Named(typeof(ITestEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler)) + .Named(typeof(IEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler)) + .WithMetadata("Interfaces", typeof(StubEventHandler).GetInterfaces().ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase)); _container = builder.Build(); _eventBus = _container.Resolve(); diff --git a/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs b/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs index f6ae16df2..abae9e5dd 100644 --- a/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs +++ b/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs @@ -81,7 +81,13 @@ namespace Orchard.Environment.ShellBuilders { } if (typeof(IEventHandler).IsAssignableFrom(item.Type)) { - registration = registration.As(typeof(IEventHandler)); + var interfaces = item.Type.GetInterfaces(); + foreach (var interfaceType in interfaces) { + // Register named instance for each interface, for efficient filtering withing event bus + registration = registration.Named(interfaceType.Name.ToLowerInvariant(), typeof (IEventHandler)); + } + // Keep interfaces in metadata for performance + registration = registration.WithMetadata("Interfaces", interfaces.ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase)); } foreach (var parameter in item.Parameters) { diff --git a/src/Orchard/Events/DefaultOrchardEventBus.cs b/src/Orchard/Events/DefaultOrchardEventBus.cs index 3433bb9ac..7e924f93e 100644 --- a/src/Orchard/Events/DefaultOrchardEventBus.cs +++ b/src/Orchard/Events/DefaultOrchardEventBus.cs @@ -4,16 +4,19 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Autofac.Features.Indexed; +using Autofac.Features.Metadata; using Orchard.Exceptions; using Orchard.Localization; namespace Orchard.Events { public class DefaultOrchardEventBus : IEventBus { - private readonly Func> _eventHandlers; + private readonly IIndex>> _eventHandlers; private readonly IExceptionPolicy _exceptionPolicy; - private static readonly ConcurrentDictionary _interfaceMethodsCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary>> _delegateCache = new ConcurrentDictionary>>(); - public DefaultOrchardEventBus(Func> eventHandlers, IExceptionPolicy exceptionPolicy) { + public DefaultOrchardEventBus(IIndex>> eventHandlers, IExceptionPolicy exceptionPolicy) + { _eventHandlers = eventHandlers; _exceptionPolicy = exceptionPolicy; T = NullLocalizer.Instance; @@ -34,7 +37,7 @@ namespace Orchard.Events { string interfaceName = parameters[0]; string methodName = parameters[1]; - var eventHandlers = _eventHandlers(); + var eventHandlers = _eventHandlers[interfaceName.ToLowerInvariant()]; foreach (var eventHandler in eventHandlers) { IEnumerable returnValue; if (TryNotifyHandler(eventHandler, messageName, interfaceName, methodName, eventData, out returnValue)) { @@ -47,7 +50,7 @@ namespace Orchard.Events { } } - private bool TryNotifyHandler(IEventHandler eventHandler, string messageName, string interfaceName, string methodName, IDictionary eventData, out IEnumerable returnValue) { + private bool TryNotifyHandler(Meta eventHandler, string messageName, string interfaceName, string methodName, IDictionary eventData, out IEnumerable returnValue) { try { return TryInvoke(eventHandler, interfaceName, methodName, eventData, out returnValue); } @@ -61,31 +64,34 @@ namespace Orchard.Events { } } - private static bool TryInvoke(IEventHandler eventHandler, string interfaceName, string methodName, IDictionary arguments, out IEnumerable returnValue) { - Type type = eventHandler.GetType(); - foreach (var interfaceType in type.GetInterfaces()) { - if (String.Equals(interfaceType.Name, interfaceName, StringComparison.OrdinalIgnoreCase)) { - return TryInvokeMethod(eventHandler, interfaceType, methodName, arguments, out returnValue); - } + private static bool TryInvoke(Meta eventHandler, string interfaceName, string methodName, IDictionary arguments, out IEnumerable returnValue) { + var matchingInterfaces = ((ILookup)eventHandler.Metadata["Interfaces"])[interfaceName]; + foreach (var interfaceType in matchingInterfaces) { + return TryInvokeMethod(eventHandler.Value, interfaceType, methodName, arguments, out returnValue); } returnValue = null; return false; } private static bool TryInvokeMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary arguments, out IEnumerable returnValue) { - MethodInfo method = _interfaceMethodsCache.GetOrAdd(String.Concat(eventHandler.GetType().FullName + "_" + interfaceType.Name, "_", methodName, "_", String.Join("_", arguments.Keys)), GetMatchingMethod(eventHandler, interfaceType, methodName, arguments)); + var key = String.Concat(eventHandler.GetType().FullName + "_" + interfaceType.Name, "_", methodName, "_", String.Join("_", arguments.Keys)); + var cachedDelegate = _delegateCache.GetOrAdd(key, k => { + var method = GetMatchingMethod(eventHandler, interfaceType, methodName, arguments); + return method != null + ? Tuple.Create(method.GetParameters(), DelegateHelper.CreateDelegate(interfaceType, method)) + : null; + }); + + if (cachedDelegate != null) { + var args = cachedDelegate.Item1.Select(methodParameter => arguments[methodParameter.Name]).ToArray(); + var result = cachedDelegate.Item2(eventHandler, args); - if (method != null) { - var parameters = new List(); - foreach (var methodParameter in method.GetParameters()) { - parameters.Add(arguments[methodParameter.Name]); - } - var result = method.Invoke(eventHandler, parameters.ToArray()); returnValue = result as IEnumerable; if (returnValue == null && result != null) returnValue = new[] { result }; - return true; + return true; } + returnValue = null; return false; } diff --git a/src/Orchard/Events/DelegateHelper.cs b/src/Orchard/Events/DelegateHelper.cs new file mode 100644 index 000000000..cf3803828 --- /dev/null +++ b/src/Orchard/Events/DelegateHelper.cs @@ -0,0 +1,174 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace Orchard.Events { + public static class DelegateHelper { + public static Func CreateDelegate(MethodInfo method) { + return CreateDelegate(typeof(T), method); + } + + /// + /// Creates a strongly-typed dynamic delegate that represents a given method on a given target type. + /// + /// + /// Provided method must be valid for the type provided as targetType. + /// + /// Type of the delegate target (first argument) object. Needs to be assignable from the type provided as targetType parameter. + /// Delegate target type. + /// Method that the delegate represents. + /// Strongly-typed delegate representing the given method. + /// First argument is the target of the delegate. + /// Second argument describes call arguments. + /// + public static Func CreateDelegate(Type targetType, MethodInfo method) { + var parameters = method.ReturnType == typeof(void) + ? new[] { targetType }.Concat(method.GetParameters().Select(p => p.ParameterType)).ToArray() + : new[] { targetType }.Concat(method.GetParameters().Select(p => p.ParameterType)).Concat(new[] { method.ReturnType }).ToArray(); + + // First fetch the generic form + MethodInfo genericHelper = method.ReturnType == typeof(void) + ? typeof(DelegateHelper).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).First(m => m.Name == "BuildAction" && m.GetGenericArguments().Count() == parameters.Length) + : typeof(DelegateHelper).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).First(m => m.Name == "BuildFunc" && m.GetGenericArguments().Count() == parameters.Length); + + // Now supply the type arguments + MethodInfo constructedHelper = genericHelper.MakeGenericMethod(parameters); + + // Now call it. The null argument is because it's a static method. + object ret = constructedHelper.Invoke(null, new object[] { method }); + return (Func)ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8]); + return ret; + } + + static Func BuildFunc(MethodInfo method) { + var func = (Func)Delegate.CreateDelegate(typeof(Func), method); + Func ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8], (T10)p[9]); + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8]); return null; }; + return ret; + } + + static Func BuildAction(MethodInfo method) { + var func = (Action)Delegate.CreateDelegate(typeof(Action), method); + Func ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8], (T10)p[9]); return null; }; + return ret; + } + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 6e59cbfec..3dbaa2ba1 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -227,6 +227,7 @@ +