From 328d30f33bd3113302d48e3f91032fbf1869b45b Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Mon, 12 Oct 2015 19:18:42 +0700 Subject: [PATCH] Fixes #414, patch for Simple Injector container initializer so it will be compatible with both Simple Injector 2.x and 3.x. Also fixed lifestyles so they don't have conflicts. --- .../MvcSiteMapProviderContainerInitializer.cs | 167 ++++++++++++++---- 1 file changed, 137 insertions(+), 30 deletions(-) diff --git a/src/MvcSiteMapProvider/CodeAsConfiguration/SimpleInjector/DI/SimpleInjector/MvcSiteMapProviderContainerInitializer.cs b/src/MvcSiteMapProvider/CodeAsConfiguration/SimpleInjector/DI/SimpleInjector/MvcSiteMapProviderContainerInitializer.cs index 64662d86..6723ca5a 100644 --- a/src/MvcSiteMapProvider/CodeAsConfiguration/SimpleInjector/DI/SimpleInjector/MvcSiteMapProviderContainerInitializer.cs +++ b/src/MvcSiteMapProvider/CodeAsConfiguration/SimpleInjector/DI/SimpleInjector/MvcSiteMapProviderContainerInitializer.cs @@ -6,7 +6,6 @@ using System.Web.Hosting; using System.Web.Mvc; using SimpleInjector; -using SimpleInjector.Extensions; using MvcSiteMapProvider; using MvcSiteMapProvider.Builder; using MvcSiteMapProvider.Caching; @@ -39,7 +38,7 @@ public static void SetUp(Container container) // Extension to allow resolution of arrays by GetAllInstances (natively based on IEnumerable). // source from: https://simpleinjector.codeplex.com/wikipage?title=CollectionRegistrationExtensions - AllowToResolveArraysAndLists(container); + container.AllowToResolveArraysAndLists(); var currentAssembly = typeof(MvcSiteMapProviderContainerInitializer).Assembly; var siteMapProviderAssembly = typeof(SiteMaps).Assembly; @@ -59,6 +58,7 @@ public static void SetUp(Container container) // Example: // typeof(SiteMap), // typeof(SiteMapNodeVisibilityProviderStrategy) + typeof(IMvcContextFactory) }; var multipleImplementationTypes = new Type[] { @@ -70,7 +70,7 @@ public static void SetUp(Container container) // Matching type name (I[TypeName] = [TypeName]) or matching type name + suffix Adapter (I[TypeName] = [TypeName]Adapter) // and not decorated with the [ExcludeFromAutoRegistrationAttribute]. CommonConventions.RegisterDefaultConventions( - (interfaceType, implementationType) => container.RegisterSingle(interfaceType, implementationType), + (interfaceType, implementationType) => container.Register(interfaceType, implementationType), new Assembly[] { siteMapProviderAssembly }, allAssemblies, excludeTypes, @@ -78,63 +78,71 @@ public static void SetUp(Container container) // Multiple implementations of strategy based extension points (and not decorated with [ExcludeFromAutoRegistrationAttribute]). CommonConventions.RegisterAllImplementationsOfInterfaceSingle( - (interfaceType, implementationTypes) => container.RegisterAll(interfaceType, implementationTypes), + (interfaceType, implementationTypes) => container.RegisterCollection(interfaceType, implementationTypes), multipleImplementationTypes, allAssemblies, excludeTypes, string.Empty); - container.Register(); + container.RegisterMvcController(); + + container.Register(Lifestyle.Singleton); // Visibility Providers - container.RegisterSingle(() => + container.Register(() => new SiteMapNodeVisibilityProviderStrategy( - container.GetAllInstances().ToArray(), string.Empty)); + container.GetAllInstances().ToArray(), string.Empty), + Lifestyle.Singleton); // Pass in the global controllerBuilder reference - container.RegisterSingle(() => ControllerBuilder.Current); + container.Register(() => ControllerBuilder.Current, Lifestyle.Singleton); - container.RegisterSingle(() => + container.Register(() => new ControllerTypeResolverFactory( new string[0], container.GetInstance(), - container.GetInstance())); + container.GetInstance()), + Lifestyle.Singleton); // Configure Security - container.RegisterAll(typeof(AuthorizeAttributeAclModule), typeof(XmlRolesAclModule)); - container.RegisterSingle(() => new CompositeAclModule(container.GetAllInstances().ToArray())); + container.RegisterCollection(typeof(IAclModule), new Type[] { typeof(AuthorizeAttributeAclModule), typeof(XmlRolesAclModule) }); + container.Register(() => new CompositeAclModule(container.GetAllInstances().ToArray()), Lifestyle.Singleton); // Setup cache #if NET35 - container.RegisterSingleOpenGeneric(typeof(ICacheProvider<>), typeof(AspNetCacheProvider<>)); - container.RegisterSingle(() => new AspNetFileCacheDependency(absoluteFileName)); + container.RegisterOpenGeneric(typeof(ICacheProvider<>), typeof(AspNetCacheProvider<>), Lifestyle.Singleton); + container.Register(() => new AspNetFileCacheDependency(absoluteFileName), Lifestyle.Singleton); #else - container.RegisterSingle(() => System.Runtime.Caching.MemoryCache.Default); - container.RegisterSingleOpenGeneric(typeof(ICacheProvider<>), typeof(RuntimeCacheProvider<>)); - container.RegisterSingle(() => new RuntimeFileCacheDependency(absoluteFileName)); + container.Register(() => System.Runtime.Caching.MemoryCache.Default, Lifestyle.Singleton); + container.RegisterOpenGeneric(typeof(ICacheProvider<>), typeof(RuntimeCacheProvider<>), Lifestyle.Singleton); + container.Register(() => new RuntimeFileCacheDependency(absoluteFileName), Lifestyle.Singleton); #endif - container.RegisterSingle(() => new CacheDetails(absoluteCacheExpiration, TimeSpan.MinValue, container.GetInstance())); + container.Register(() => new CacheDetails(absoluteCacheExpiration, TimeSpan.MinValue, container.GetInstance()), Lifestyle.Singleton); // Configure the visitors - container.RegisterSingle(); + container.Register(Lifestyle.Singleton); // Prepare for the sitemap node providers - container.RegisterSingle(() => new ReservedAttributeNameProvider(new string[0])); - container.RegisterSingle(() => new FileXmlSource(absoluteFileName)); + container.Register(() => new ReservedAttributeNameProvider(new string[0]), Lifestyle.Singleton); + container.Register(() => new FileXmlSource(absoluteFileName), Lifestyle.Singleton); // Register the sitemap node providers - container.RegisterSingle(() => container.GetInstance() - .Create(container.GetInstance())); - container.RegisterSingle(() => container.GetInstance() - .Create(includeAssembliesForScan)); + container.Register(() => container.GetInstance() + .Create(container.GetInstance()), + Lifestyle.Singleton); + container.Register(() => container.GetInstance() + .Create(includeAssembliesForScan), + Lifestyle.Singleton); // Register the sitemap builders - container.RegisterSingle(() => container.GetInstance() - .Create(new CompositeSiteMapNodeProvider(container.GetInstance(), container.GetInstance()))); + container.Register(() => container.GetInstance() + .Create(new CompositeSiteMapNodeProvider(container.GetInstance(), container.GetInstance())), + Lifestyle.Singleton); - container.RegisterAll( + container.RegisterCollection( ResolveISiteMapBuilderSets(container, securityTrimmingEnabled, enableLocalization, visibilityAffectsDescendants, useTitleIfDescriptionNotProvided)); - container.RegisterSingle(() => new SiteMapBuilderSetStrategy(container.GetAllInstances().ToArray())); + container.Register(() => new SiteMapBuilderSetStrategy(container.GetAllInstances().ToArray()), + Lifestyle.Singleton); } private static IEnumerable ResolveISiteMapBuilderSets( @@ -149,8 +157,11 @@ private static IEnumerable ResolveISiteMapBuilderSets( container.GetInstance(), container.GetInstance()); } + } - private static void AllowToResolveArraysAndLists(Container container) + public static class ContainerExtensions + { + public static void AllowToResolveArraysAndLists(this Container container) { container.ResolveUnregisteredType += (sender, e) => { @@ -180,5 +191,101 @@ private static void RegisterArrayResolver(UnregisteredTypeEventArgs e, Container var arrayExpression = Expression.Call(arrayMethod, enumerableExpression); e.Register(arrayExpression); } + + // Extension methods for cross-version support of Simple Injector 2.x and 3.x. + + // This will succeed on 2.x and will be bypassed on 3.x + public static void RegisterCollection(this Container container, Type serviceType, IEnumerable serviceTypes) + { + // container.RegisterAll(serviceType, serviceTypes); + var method = container.GetType().GetMethod( + "RegisterAll", + BindingFlags.Instance | BindingFlags.Public, + null, + new Type[] { typeof(Type), typeof(IEnumerable) }, + null); + + if (method != null) + { + method.Invoke(container, new object[] { serviceType, serviceTypes }); + } + } + + // This will succeed on 2.x and will be bypassed on 3.x + public static void RegisterCollection(this Container container, IEnumerable containerUncontrolledCollection) where TService : class + { + // container.RegisterAll(containerUncontrolledCollection); + var method = container.GetType().GetMethods() + .Where(mi => mi.Name == "RegisterAll") + .Select(mi => new { M = mi, P = mi.GetParameters(), A = mi.GetGenericArguments() }) + .Where(x => x.A.Length == 1 + && x.P.Length == 1 + && x.P[0].Name == "collection") + .Select(x => x.M) + .FirstOrDefault(); + + if (method != null) + { + var genericMethod = method.MakeGenericMethod(new Type[] { typeof(TService) }); + genericMethod.Invoke(container, new object[] { containerUncontrolledCollection }); + } + } + + // This will work on both 2.x and 3.x. On 2.x this will override the default implementation because it is in the same namespace as the caller. + public static void RegisterOpenGeneric(this Container container, Type openGenericServiceType, Type openGenericImplementation, Lifestyle lifestyle) + { + bool isSimpleInjector2 = false; + var openGenericExtensionType = container.GetType().Assembly.GetType("SimpleInjector.Extensions.OpenGenericRegistrationExtensions"); + if (openGenericExtensionType != null) + { + // Attempt to find the method to invoke + // OpenGenericRegistrationExtensions.RegisterOpenGeneric(container, openGenericServiceType, openGenericImplementation, lifestyle); + var method = openGenericExtensionType.GetMethod( + "RegisterOpenGeneric", + BindingFlags.Static | BindingFlags.Public, + null, + new Type[] { typeof(Container), typeof(Type), typeof(Type), typeof(Lifestyle) }, + null); + + if (method != null && !Attribute.IsDefined(method, typeof(ObsoleteAttribute))) + { + // This is SimpleInjector 2 - Invoke the method + isSimpleInjector2 = true; + method.Invoke(null, new object[] { container, openGenericServiceType, openGenericImplementation, lifestyle }); + } + } + + if (!isSimpleInjector2) + { + container.Register(openGenericServiceType, openGenericImplementation, lifestyle); + } + } + + public static void RegisterMvcController(this Container container) where TService: IController + { + var registration = Lifestyle.Transient.CreateRegistration(typeof(IController), typeof(TService), container); + container.AddRegistration(typeof(TService), registration); + + // This will run if using SimpleInjector 3.x + Type diagnosticTypeType = container.GetType().Assembly.GetType("SimpleInjector.Diagnostics.DiagnosticType"); + if (diagnosticTypeType != null) + { + var method = registration.GetType().GetMethod( + "SuppressDiagnosticWarning", + BindingFlags.Instance | BindingFlags.Public, + null, + new Type[] { diagnosticTypeType, typeof(string) }, + null); + + if (method != null) + { + object disposableTransientComponent = Enum.Parse(diagnosticTypeType, "DisposableTransientComponent", true); + + method.Invoke(registration, new object[] { + disposableTransientComponent, + "MVC's DefaultControllerFactory disposes the controller when the web request ends." }); + } + } + } } }