From 13a4d5c81cd2db5acde59dbedca02b0bbb68a6b6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 21 Jan 2015 12:48:08 +1100 Subject: [PATCH] WIP - have added LightInject as a fast and tiny IoC container that is embedded. Have updated all required SingleObjectResolverBase and non lazy ManyObjectResolverBase to use a Container implementation. Have updated the boot managers to use IoC to instantiate all their requirements. This is so much nicer now by using IoC to ctor all of the objects in these resolvers we can get ctor injection OOTB so no more singletons. Need to create resolver to support the lazy resolver with IoC next. Updated IContentFinders, IThumbnailProviders to use ctor injection. --- src/Umbraco.Core/CoreBootManager.cs | 308 +- .../CultureDictionaryFactoryResolver.cs | 29 +- src/Umbraco.Core/LightInject/LightInject.cs | 6067 +++++++++++++++++ .../LightInject/LightInjectExtensions.cs | 1015 +++ src/Umbraco.Core/Logging/LoggerResolver.cs | 6 + .../PublishedContentModelFactoryResolver.cs | 25 +- .../ApplicationEventsResolver.cs | 277 +- .../ContainerManyObjectsResolver.cs | 109 + .../ContainerSingleObjectResolver.cs | 123 + .../LazyManyObjectsResolverbase.cs | 48 +- .../ManyObjectsResolverBase.cs | 738 +- .../ObjectResolution/ResolverBase.cs | 7 + .../SingleObjectResolverBase.cs | 6 +- src/Umbraco.Core/PluginManager.cs | 4 +- .../Profiling/ProfilerResolver.cs | 3 + src/Umbraco.Core/Profiling/WebProfiler.cs | 13 +- .../PropertyEditors/PropertyEditorResolver.cs | 6 - .../Standalone/StandaloneCoreBootManager.cs | 16 +- .../Strategies/ManifestWatcherHandler.cs | 31 + src/Umbraco.Core/StringExtensions.cs | 2 +- .../Strings/DefaultShortStringHelper.cs | 12 +- .../Strings/ShortStringHelperResolver.cs | 59 +- .../Strings/UrlSegmentProviderResolver.cs | 27 +- .../Sync/ConfigServerRegistrar.cs | 4 +- .../Sync/ServerMessengerResolver.cs | 26 +- .../Sync/ServerRegistrarResolver.cs | 25 +- src/Umbraco.Core/Umbraco.Core.csproj | 5 + src/Umbraco.Core/UmbracoApplicationBase.cs | 18 +- src/Umbraco.Core/packages.config | 1 + .../BootManagers/CoreBootManagerTests.cs | 254 +- .../Routing/ContentFinderByAliasTests.cs | 4 +- .../ContentFinderByAliasWithDomainsTests.cs | 4 +- .../Routing/ContentFinderByIdTests.cs | 4 +- .../ContentFinderByNiceUrlAndTemplateTests.cs | 4 +- .../Routing/ContentFinderByNiceUrlTests.cs | 8 +- .../ContentFinderByNiceUrlWithDomainsTests.cs | 6 +- .../Routing/DomainsAndCulturesTests.cs | 8 +- .../Routing/UrlsWithNestedDomains.cs | 3 +- .../Strings/CmsHelperCasingTests.cs | 2 +- .../Strings/DefaultShortStringHelperTests.cs | 48 +- .../UmbracoExamine/ExamineBaseTest.cs | 2 +- .../LightInject/LightInjectExtensions.cs | 45 + .../LightInject/Mvc/LightInject.Mvc.cs | 203 + .../LightInject/Web/LightInject.Web.cs | 164 + .../LightInject/WebApi/LightInject.WebApi.cs | 235 + .../ImageThumbnailProvider.cs | 13 +- .../ThumbnailProvidersResolver.cs | 9 +- .../Mvc/DefaultRenderMvcControllerResolver.cs | 15 - .../PublishedCache/PublishedCachesResolver.cs | 29 +- .../PublishedContentCache.cs | 4 + .../Routing/ContentFinderByIdPath.cs | 15 +- .../Routing/ContentFinderByLegacy404.cs | 15 +- .../Routing/ContentFinderByNiceUrl.cs | 15 +- .../ContentFinderByNiceUrlAndTemplate.cs | 59 +- .../Routing/ContentFinderByProfile.cs | 57 +- .../Routing/ContentFinderByUrlAlias.cs | 9 +- .../Routing/ContentFinderResolver.cs | 15 +- .../ContentLastChanceFinderResolver.cs | 79 +- .../Routing/SiteDomainHelperResolver.cs | 65 +- src/Umbraco.Web/Scheduling/Scheduler.cs | 7 +- .../Standalone/StandaloneBootManager.cs | 18 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 + src/Umbraco.Web/UmbracoApplication.cs | 22 - src/Umbraco.Web/WebBootManager.cs | 150 +- src/Umbraco.Web/packages.config | 3 + 65 files changed, 9433 insertions(+), 1174 deletions(-) create mode 100644 src/Umbraco.Core/LightInject/LightInject.cs create mode 100644 src/Umbraco.Core/LightInject/LightInjectExtensions.cs create mode 100644 src/Umbraco.Core/ObjectResolution/ContainerManyObjectsResolver.cs create mode 100644 src/Umbraco.Core/ObjectResolution/ContainerSingleObjectResolver.cs create mode 100644 src/Umbraco.Core/Strategies/ManifestWatcherHandler.cs create mode 100644 src/Umbraco.Web/LightInject/LightInjectExtensions.cs create mode 100644 src/Umbraco.Web/LightInject/Mvc/LightInject.Mvc.cs create mode 100644 src/Umbraco.Web/LightInject/Web/LightInject.Web.cs create mode 100644 src/Umbraco.Web/LightInject/WebApi/LightInject.WebApi.cs diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 4e55bb86a9..58e55b91ae 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -1,10 +1,12 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Web; +using System.Threading.Tasks; using AutoMapper; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.LightInject; using Umbraco.Core.Logging; using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models.PublishedContent; @@ -12,22 +14,20 @@ using Umbraco.Core.ObjectResolution; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Migrations; -using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix; -using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Profiling; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Publishing; using Umbraco.Core.Macros; using Umbraco.Core.Services; using Umbraco.Core.Sync; using Umbraco.Core.Strings; -using MigrationsVersionFourNineZero = Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero; + namespace Umbraco.Core { + /// /// A bootstrapper for the Umbraco application which initializes all objects for the Core of the application /// @@ -36,31 +36,32 @@ namespace Umbraco.Core /// public class CoreBootManager : IBootManager { - private ProfilingLogger _profilingLogger; + internal ServiceContainer Container { get; private set; } + private ServiceContainer _appStartupEvtContainer; + protected ProfilingLogger ProfilingLogger { get; private set; } private DisposableTimer _timer; + protected PluginManager PluginManager { get; private set; } + private CacheHelper _cacheHelper; + private bool _isInitialized = false; private bool _isStarted = false; private bool _isComplete = false; - private readonly IServiceProvider _serviceProvider = new ActivatorServiceProvider(); private readonly UmbracoApplicationBase _umbracoApplication; - protected ApplicationContext ApplicationContext { get; set; } - protected CacheHelper ApplicationCache { get; set; } - protected PluginManager PluginManager { get; private set; } + + protected ApplicationContext ApplicationContext { get; private set; } protected UmbracoApplicationBase UmbracoApplication { get { return _umbracoApplication; } } - protected IServiceProvider ServiceProvider - { - get { return _serviceProvider; } - } + protected IServiceProvider ServiceProvider { get; private set; } public CoreBootManager(UmbracoApplicationBase umbracoApplication) { if (umbracoApplication == null) throw new ArgumentNullException("umbracoApplication"); _umbracoApplication = umbracoApplication; + Container = new ServiceContainer(); } public virtual IBootManager Initialize() @@ -68,57 +69,47 @@ namespace Umbraco.Core if (_isInitialized) throw new InvalidOperationException("The boot manager has already been initialized"); - InitializeLoggerResolver(); - InitializeProfilerResolver(); + //Create logger/profiler, and their resolvers, these are special resolvers that can be resolved before frozen so we can start logging + var logger = CreateLogger(); + LoggerResolver.Current = new LoggerResolver(logger) {CanResolveBeforeFrozen = true}; + var profiler = CreateProfiler(); + ProfilerResolver.Current = new ProfilerResolver(profiler) {CanResolveBeforeFrozen = true}; + ProfilingLogger = new ProfilingLogger(logger, profiler); - _profilingLogger = new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler); + _timer = ProfilingLogger.DebugDuration("Umbraco application starting", "Umbraco application startup complete"); - _timer = _profilingLogger.DebugDuration("Umbraco application starting", "Umbraco application startup complete"); + //create the plugin manager + //TODO: this is currently a singleton but it would be better if it weren't. Unfortunately the only way to get + // rid of this singleton would be to put it into IoC and then use the ServiceLocator pattern. + _cacheHelper = CreateApplicationCache(); + ServiceProvider = new ActivatorServiceProvider(); + PluginManager.Current = PluginManager = new PluginManager(ServiceProvider, _cacheHelper.RuntimeCache, ProfilingLogger, true); - CreateApplicationCache(); + //build up IoC + Container = BuildContainer(); - //create and set the plugin manager (I'd much prefer to not use this singleton anymore but many things are using it unfortunately and - // the way that it is setup, there must only ever be one per app so without IoC it would be hard to make this not a singleton) - PluginManager = new PluginManager(ServiceProvider, ApplicationCache.RuntimeCache, _profilingLogger); - PluginManager.Current = PluginManager; + //set the singleton resolved from the core container + ApplicationContext.Current = ApplicationContext = Container.GetInstance(); - //Create the legacy prop-eds mapping + //TODO: Remove these for v8! LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors(); LegacyParameterEditorAliasConverter.CreateMappingsForCoreEditors(); - - //create database and service contexts for the app context - var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, LoggerResolver.Current.Logger); + //TODO: Make this as part of the db ctor! Database.Mapper = new PetaPocoMapper(); - - var dbContext = new DatabaseContext( - dbFactory, - LoggerResolver.Current.Logger, - SqlSyntaxProviders.CreateDefault(LoggerResolver.Current.Logger)); - - //initialize the DatabaseContext - dbContext.Initialize(); - var serviceContext = new ServiceContext( - new RepositoryFactory(ApplicationCache, LoggerResolver.Current.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()), - new PetaPocoUnitOfWorkProvider(dbFactory), - new FileUnitOfWorkProvider(), - new PublishingStrategy(), - ApplicationCache, - LoggerResolver.Current.Logger); - - CreateApplicationContext(dbContext, serviceContext); - - InitializeApplicationEventsResolver(); + //Create a 'child'container which is a copy of all of the current registrations and begin a sub scope for it + // this child container will be used to manage the application event handler instances and the scope will be + // completed at the end of the boot process to allow garbage collection + _appStartupEvtContainer = Container.CreateChildContainer(); + _appStartupEvtContainer.BeginScope(); + _appStartupEvtContainer.RegisterCollection(PluginManager.ResolveApplicationStartupHandlers()); + ConfigureServices(Container); InitializeResolvers(); - - - InitializeModelMappers(); //now we need to call the initialize methods - ApplicationEventsResolver.Current.ApplicationEventHandlers - .ForEach(x => x.OnApplicationInitialized(UmbracoApplication, ApplicationContext)); + Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x => x.OnApplicationInitialized(UmbracoApplication, ApplicationContext)); _isInitialized = true; @@ -126,28 +117,76 @@ namespace Umbraco.Core } /// - /// Creates and assigns the application context singleton + /// Build the core container which contains all core things requird to build an app context /// - /// - /// - protected virtual void CreateApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext) + private ServiceContainer BuildContainer() { - //create the ApplicationContext - ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext, ApplicationCache, _profilingLogger); + var container = new ServiceContainer(); + container.Register(factory => UmbracoConfig.For.UmbracoSettings()); + container.Register(factory => _cacheHelper, new PerContainerLifetime()); + container.Register(factory => _cacheHelper.RuntimeCache, new PerContainerLifetime()); + container.Register(factory => ProfilingLogger.Logger, new PerContainerLifetime()); + container.Register(factory => ProfilingLogger.Profiler, new PerContainerLifetime()); + container.Register(factory => ProfilingLogger, new PerContainerLifetime()); + container.Register(); + container.Register(factory => PluginManager, new PerContainerLifetime()); + container.Register(factory => new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, factory.GetInstance())); + container.Register(factory => GetDbContext(factory), new PerContainerLifetime()); + container.Register(factory => SqlSyntaxProviders.CreateDefault(factory.GetInstance())); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(factory => new ServiceContext( + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance())); + container.Register(new PerContainerLifetime()); + + return container; } /// - /// Creates and assigns the ApplicationCache based on a new instance of System.Web.Caching.Cache + /// Called to customize the IoC container /// - protected virtual void CreateApplicationCache() + /// + internal virtual void ConfigureServices(ServiceContainer container) + { + container.Register(factory => FileSystemProviderManager.Current.GetFileSystemProvider()); + } + + /// + /// Creates and initializes the db context when IoC requests it + /// + /// + /// + private DatabaseContext GetDbContext(IServiceFactory container) + { + var dbCtx = new DatabaseContext( + container.GetInstance(), + container.GetInstance(), + container.GetInstance()); + + //when it's first created we need to initialize it + dbCtx.Initialize(); + return dbCtx; + } + + /// + /// Creates the ApplicationCache based on a new instance of System.Web.Caching.Cache + /// + protected virtual CacheHelper CreateApplicationCache() { var cacheHelper = new CacheHelper( - new ObjectCacheRuntimeCacheProvider(), - new StaticCacheProvider(), + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), //we have no request based cache when not running in web-based context - new NullCacheProvider()); + new NullCacheProvider()); - ApplicationCache = cacheHelper; + return cacheHelper; } /// @@ -161,7 +200,8 @@ namespace Umbraco.Core { Mapper.Initialize(configuration => { - foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType()) + //foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType()) + foreach (var m in _appStartupEvtContainer.GetAllInstances().OfType()) { m.ConfigureMappings(configuration, ApplicationContext); } @@ -169,50 +209,19 @@ namespace Umbraco.Core } /// - /// Special method to initialize the LoggerResolver + /// Creates the application's ILogger /// - protected virtual void InitializeLoggerResolver() + protected virtual ILogger CreateLogger() { - LoggerResolver.Current = new LoggerResolver(Logger.CreateWithDefaultLog4NetConfiguration()) - { - //This is another special resolver that needs to be resolvable before resolution is frozen - //since it is used for profiling the application startup - CanResolveBeforeFrozen = true - }; + return Logger.CreateWithDefaultLog4NetConfiguration(); } /// - /// Special method to initialize the ProfilerResolver + /// Creates the application's IProfiler /// - protected virtual void InitializeProfilerResolver() + protected virtual IProfiler CreateProfiler() { - //By default we'll initialize the Log profiler (in the web project, we'll override with the web profiler) - ProfilerResolver.Current = new ProfilerResolver(new LogProfiler(LoggerResolver.Current.Logger)) - { - //This is another special resolver that needs to be resolvable before resolution is frozen - //since it is used for profiling the application startup - CanResolveBeforeFrozen = true - }; - } - - /// - /// Special method to initialize the ApplicationEventsResolver and any modifications required for it such - /// as adding custom types to the resolver. - /// - protected virtual void InitializeApplicationEventsResolver() - { - //find and initialize the application startup handlers, we need to initialize this resolver here because - //it is a special resolver where they need to be instantiated first before any other resolvers in order to bind to - //events and to call their events during bootup. - //ApplicationStartupHandler.RegisterHandlers(); - //... and set the special flag to let us resolve before frozen resolution - ApplicationEventsResolver.Current = new ApplicationEventsResolver( - ServiceProvider, - LoggerResolver.Current.Logger, - PluginManager.ResolveApplicationStartupHandlers()) - { - CanResolveBeforeFrozen = true - }; + return new LogProfiler(ProfilingLogger.Logger); } /// @@ -223,7 +232,7 @@ namespace Umbraco.Core /// Absolute protected virtual void InitializeApplicationRootPath(string rootPath) { - Umbraco.Core.IO.IOHelper.SetRootDirectory(rootPath); + IO.IOHelper.SetRootDirectory(rootPath); } /// @@ -238,8 +247,7 @@ namespace Umbraco.Core throw new InvalidOperationException("The boot manager has already been initialized"); //call OnApplicationStarting of each application events handler - ApplicationEventsResolver.Current.ApplicationEventHandlers - .ForEach(x => x.OnApplicationStarting(UmbracoApplication, ApplicationContext)); + Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x => x.OnApplicationStarting(UmbracoApplication, ApplicationContext)); if (afterStartup != null) { @@ -264,11 +272,10 @@ namespace Umbraco.Core FreezeResolution(); //call OnApplicationStarting of each application events handler - ApplicationEventsResolver.Current.ApplicationEventHandlers - .ForEach(x => x.OnApplicationStarted(UmbracoApplication, ApplicationContext)); + Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x => x.OnApplicationStarted(UmbracoApplication, ApplicationContext)); - //Now, startup all of our legacy startup handler - ApplicationEventsResolver.Current.InstantiateLegacyStartupHandlers(); + //end the current scope which was created to intantiate all of the startup handlers + _appStartupEvtContainer.EndCurrentScope(); if (afterComplete != null) { @@ -298,12 +305,12 @@ namespace Umbraco.Core /// protected virtual void InitializeResolvers() { - PropertyEditorResolver.Current = new PropertyEditorResolver(ServiceProvider, LoggerResolver.Current.Logger, () => PluginManager.ResolvePropertyEditors()); - ParameterEditorResolver.Current = new ParameterEditorResolver(ServiceProvider, LoggerResolver.Current.Logger, () => PluginManager.ResolveParameterEditors()); + PropertyEditorResolver.Current = new PropertyEditorResolver(ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolvePropertyEditors()); + ParameterEditorResolver.Current = new ParameterEditorResolver(ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveParameterEditors()); //setup the validators resolver with our predefined validators ValidatorsResolver.Current = new ValidatorsResolver( - ServiceProvider, LoggerResolver.Current.Logger, new[] + ServiceProvider, ProfilingLogger.Logger, new[] { new Lazy(() => typeof (RequiredManifestValueValidator)), new Lazy(() => typeof (RegexValidator)), @@ -313,62 +320,103 @@ namespace Umbraco.Core }); //by default we'll use the standard configuration based sync - ServerRegistrarResolver.Current = new ServerRegistrarResolver( - new ConfigServerRegistrar()); + ServerRegistrarResolver.Current = new ServerRegistrarResolver(Container, typeof(ConfigServerRegistrar)); //by default (outside of the web) we'll use the default server messenger without //supplying a username/password, this will automatically disable distributed calls // .. we'll override this in the WebBootManager - ServerMessengerResolver.Current = new ServerMessengerResolver( - new DefaultServerMessenger()); + ServerMessengerResolver.Current = new ServerMessengerResolver(Container, typeof (DefaultServerMessenger)); MappingResolver.Current = new MappingResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveAssignedMapperTypes()); - + //RepositoryResolver.Current = new RepositoryResolver( // new RepositoryFactory(ApplicationCache)); CacheRefreshersResolver.Current = new CacheRefreshersResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveCacheRefreshers()); - + MacroFieldEditorsResolver.Current = new MacroFieldEditorsResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveMacroRenderings()); PackageActionsResolver.Current = new PackageActionsResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolvePackageActions()); ActionsResolver.Current = new ActionsResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveActions()); //the database migration objects MigrationResolver.Current = new MigrationResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveTypes()); // need to filter out the ones we dont want!! PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, PluginManager.ResolveTypes()); // use the new DefaultShortStringHelper - ShortStringHelperResolver.Current = new ShortStringHelperResolver( - //new LegacyShortStringHelper()); - new DefaultShortStringHelper().WithDefaultConfig()); + ShortStringHelperResolver.Current = new ShortStringHelperResolver(Container, + factory => new DefaultShortStringHelper(factory.GetInstance()).WithDefaultConfig()); UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver( - ServiceProvider, LoggerResolver.Current.Logger, + Container, ProfilingLogger.Logger, typeof(DefaultUrlSegmentProvider)); // by default, no factory is activated - PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(); + PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(Container); } + + ///// + ///// An IoC lifetime that will dispose instances at the end of the bootup sequence + ///// + //private class BootManagerLifetime : ILifetime + //{ + // public BootManagerLifetime(UmbracoApplicationBase appBase) + // { + // appBase.ApplicationStarted += appBase_ApplicationStarted; + // } + + // void appBase_ApplicationStarted(object sender, EventArgs e) + // { + // throw new NotImplementedException(); + // } + + // private object _instance; + + // /// + // /// Returns a service instance according to the specific lifetime characteristics. + // /// + // /// The function delegate used to create a new service instance. + // /// The of the current service request. + // /// The requested services instance. + // public object GetInstance(Func createInstance, Scope scope) + // { + // if (_instance == null) + // { + // _instance = createInstance(); + + // var disposable = _instance as IDisposable; + // if (disposable != null) + // { + // if (scope == null) + // { + // throw new InvalidOperationException("Attempt to create an disposable object without a current scope."); + // } + // scope.TrackInstance(disposable); + // } + + // } + // return createInstance; + // } + //} } } diff --git a/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs b/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs index 80abfb0eaf..8d744c90a6 100644 --- a/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs +++ b/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs @@ -1,3 +1,6 @@ +using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.Dictionary @@ -5,14 +8,34 @@ namespace Umbraco.Core.Dictionary /// /// Resolves the current CultureDictionaryFactory /// - public sealed class CultureDictionaryFactoryResolver : SingleObjectResolverBase + public sealed class CultureDictionaryFactoryResolver : ContainerSingleObjectResolver { - internal CultureDictionaryFactoryResolver(ICultureDictionaryFactory factory) + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal CultureDictionaryFactoryResolver(IServiceContainer container, Type implementationType) + : base(container, implementationType) + { + } + + internal CultureDictionaryFactoryResolver(ICultureDictionaryFactory factory) : base(factory) { } - /// + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal CultureDictionaryFactoryResolver(IServiceContainer container, Expression> implementationType) + : base(container, implementationType) + { + } + + /// /// Can be used by developers at runtime to set their ICultureDictionaryFactory at app startup /// /// diff --git a/src/Umbraco.Core/LightInject/LightInject.cs b/src/Umbraco.Core/LightInject/LightInject.cs new file mode 100644 index 0000000000..5077aa251c --- /dev/null +++ b/src/Umbraco.Core/LightInject/LightInject.cs @@ -0,0 +1,6067 @@ +/********************************************************************************* + The MIT License (MIT) + + Copyright (c) 2014 bernhard.richter@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +****************************************************************************** + LightInject version 3.0.2.2 + http://www.lightinject.net/ + http://twitter.com/bernhardrichter +******************************************************************************/ +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "No inheritance")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Single source file deployment.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Custom header.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "All public members are documented.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Performance")] + +namespace Umbraco.Core.LightInject +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.IO; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using System.Reflection.Emit; + using System.Runtime.CompilerServices; + using System.Runtime.Remoting.Messaging; + using System.Text; + using System.Text.RegularExpressions; + using System.Threading; + + /// + /// Defines a set of methods used to register services into the service container. + /// + internal interface IServiceRegistry + { + /// + /// Gets a list of instances that represents the + /// registered services. + /// + IEnumerable AvailableServices { get; } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + void Register(Type serviceType, Type implementingType); + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The instance that controls the lifetime of the registered service. + void Register(Type serviceType, Type implementingType, ILifetime lifetime); + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + void Register(Type serviceType, Type implementingType, string serviceName); + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + /// The instance that controls the lifetime of the registered service. + void Register(Type serviceType, Type implementingType, string serviceName, ILifetime lifetime); + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + void Register() where TImplementation : TService; + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The instance that controls the lifetime of the registered service. + void Register(ILifetime lifetime) where TImplementation : TService; + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + void Register(string serviceName) where TImplementation : TService; + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + /// The instance that controls the lifetime of the registered service. + void Register(string serviceName, ILifetime lifetime) where TImplementation : TService; + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + void RegisterInstance(TService instance); + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + /// The name of the service. + void RegisterInstance(TService instance, string serviceName); + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + void RegisterInstance(Type serviceType, object instance); + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + /// The name of the service. + void RegisterInstance(Type serviceType, object instance, string serviceName); + + /// + /// Registers a concrete type as a service. + /// + /// The service type to register. + void Register(); + + /// + /// Registers a concrete type as a service. + /// + /// The service type to register. + /// The instance that controls the lifetime of the registered service. + void Register(ILifetime lifetime); + + /// + /// Registers a concrete type as a service. + /// + /// The concrete type to register. + void Register(Type serviceType); + + /// + /// Registers a concrete type as a service. + /// + /// The concrete type to register. + /// The instance that controls the lifetime of the registered service. + void Register(Type serviceType, ILifetime lifetime); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// A factory delegate used to create the instance. + void Register(Expression> factory); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The parameter type. + /// The service type to register. + /// A factory delegate used to create the instance. + void Register(Expression> factory); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The parameter type. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + void Register(Expression> factory, string serviceName); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + void Register(Expression> factory); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + void Register(Expression> factory, string serviceName); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + void Register(Expression> factory); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + void Register(Expression> factory, string serviceName); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + void Register(Expression> factory); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + void Register(Expression> factory, string serviceName); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// The lambdaExpression that describes the dependencies of the service. + /// The instance that controls the lifetime of the registered service. + void Register(Expression> factory, ILifetime lifetime); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// The lambdaExpression that describes the dependencies of the service. + /// The name of the service. + void Register(Expression> factory, string serviceName); + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// The lambdaExpression that describes the dependencies of the service. + /// The name of the service. + /// The instance that controls the lifetime of the registered service. + void Register(Expression> factory, string serviceName, ILifetime lifetime); + + /// + /// Registers a custom factory delegate used to create services that is otherwise unknown to the service container. + /// + /// Determines if the service can be created by the delegate. + /// Creates a service instance according to the predicate. + void RegisterFallback(Func predicate, Func factory); + + /// + /// Registers a custom factory delegate used to create services that is otherwise unknown to the service container. + /// + /// Determines if the service can be created by the delegate. + /// Creates a service instance according to the predicate. + /// The instance that controls the lifetime of the registered service. + void RegisterFallback(Func predicate, Func factory, ILifetime lifetime); + + /// + /// Registers a service based on a instance. + /// + /// The instance that contains service metadata. + void Register(ServiceRegistration serviceRegistration); + + /// + /// Registers composition roots from the given . + /// + /// The assembly to be scanned for services. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + void RegisterAssembly(Assembly assembly); + + /// + /// Registers services from the given . + /// + /// The assembly to be scanned for services. + /// A function delegate that determines if a service implementation should be registered. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + void RegisterAssembly(Assembly assembly, Func shouldRegister); + + /// + /// Registers services from the given . + /// + /// The assembly to be scanned for services. + /// The instance that controls the lifetime of the registered service. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + void RegisterAssembly(Assembly assembly, Func lifetime); + + /// + /// Registers services from the given . + /// + /// The assembly to be scanned for services. + /// The factory that controls the lifetime of the registered service. + /// A function delegate that determines if a service implementation should be registered. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + void RegisterAssembly(Assembly assembly, Func lifetimeFactory, Func shouldRegister); + + /// + /// Registers services from the given type. + /// + /// The type of to register from. + void RegisterFrom() where TCompositionRoot : ICompositionRoot, new(); + + /// + /// Registers composition roots from assemblies in the base directory that matches the . + /// + /// The search pattern used to filter the assembly files. + void RegisterAssembly(string searchPattern); + + /// + /// Decorates the with the given . + /// + /// The target service type. + /// The decorator type used to decorate the . + /// A function delegate that determines if the + /// should be applied to the target . + void Decorate(Type serviceType, Type decoratorType, Func predicate); + + /// + /// Decorates the with the given . + /// + /// The target service type. + /// The decorator type used to decorate the . + void Decorate(Type serviceType, Type decoratorType); + + /// + /// Decorates the with the given . + /// + /// The target service type. + /// The decorator type used to decorate the . + void Decorate() where TDecorator : TService; + + /// + /// Decorates the using the given decorator . + /// + /// The target service type. + /// A factory delegate used to create a decorator instance. + void Decorate(Expression> factory); + + /// + /// Registers a decorator based on a instance. + /// + /// The instance that contains the decorator metadata. + void Decorate(DecoratorRegistration decoratorRegistration); + + /// + /// Allows a registered service to be overridden by another . + /// + /// A function delegate that is used to determine the service that should be + /// overridden using the returned from the . + /// The factory delegate used to create a that overrides + /// the incoming . + void Override( + Func serviceSelector, + Func serviceRegistrationFactory); + } + + /// + /// Defines a set of methods used to retrieve service instances. + /// + internal interface IServiceFactory + { + /// + /// Starts a new . + /// + /// + Scope BeginScope(); + + /// + /// Ends the current . + /// + void EndCurrentScope(); + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The requested service instance. + object GetInstance(Type serviceType); + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The arguments to be passed to the target instance. + /// The requested service instance. + object GetInstance(Type serviceType, object[] arguments); + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The arguments to be passed to the target instance. + /// The requested service instance. + object GetInstance(Type serviceType, string serviceName, object[] arguments); + + /// + /// Gets a named instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance. + object GetInstance(Type serviceType, string serviceName); + + /// + /// Gets an instance of the given type. + /// + /// The type of the requested service. + /// The requested service instance. + TService GetInstance(); + + /// + /// Gets a named instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance. + TService GetInstance(string serviceName); + + /// + /// Gets an instance of the given . + /// + /// The type of the argument. + /// The type of the requested service. + /// The argument value. + /// The requested service instance. + TService GetInstance(T value); + + /// + /// Gets an instance of the given . + /// + /// The type of the parameter. + /// The type of the requested service. + /// The argument value. + /// The name of the requested service. + /// The requested service instance. + TService GetInstance(T value, string serviceName); + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The requested service instance. + TService GetInstance(T1 arg1, T2 arg2); + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The name of the requested service. + /// The requested service instance. + TService GetInstance(T1 arg1, T2 arg2, string serviceName); + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The requested service instance. + TService GetInstance(T1 arg1, T2 arg2, T3 arg3); + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The name of the requested service. + /// The requested service instance. + TService GetInstance(T1 arg1, T2 arg2, T3 arg3, string serviceName); + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The fourth argument value. + /// The requested service instance. + TService GetInstance(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The fourth argument value. + /// The name of the requested service. + /// The requested service instance. + TService GetInstance(T1 arg1, T2 arg2, T3 arg3, T4 arg4, string serviceName); + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The requested service instance if available, otherwise null. + object TryGetInstance(Type serviceType); + + /// + /// Gets a named instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance if available, otherwise null. + object TryGetInstance(Type serviceType, string serviceName); + + /// + /// Tries to get an instance of the given type. + /// + /// The type of the requested service. + /// The requested service instance if available, otherwise default(T). + TService TryGetInstance(); + + /// + /// Tries to get an instance of the given type. + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance if available, otherwise default(T). + TService TryGetInstance(string serviceName); + + /// + /// Gets all instances of the given . + /// + /// The type of services to resolve. + /// A list that contains all implementations of the . + IEnumerable GetAllInstances(Type serviceType); + + /// + /// Gets all instances of type . + /// + /// The type of services to resolve. + /// A list that contains all implementations of the type. + IEnumerable GetAllInstances(); + + /// + /// Creates an instance of a concrete class. + /// + /// The type of class for which to create an instance. + /// An instance of . + /// The concrete type will be registered if not already registered with the container. + TService Create() where TService : class; + + /// + /// Creates an instance of a concrete class. + /// + /// The type of class for which to create an instance. + /// An instance of the . + object Create(Type serviceType); + } + + /// + /// Represents an inversion of control container. + /// + internal interface IServiceContainer : IServiceRegistry, IServiceFactory, IDisposable + { + /// + /// Gets or sets the that is responsible + /// for providing the used to manage scopes. + /// + IScopeManagerProvider ScopeManagerProvider { get; set; } + + /// + /// Returns true if the container can create the requested service, otherwise false. + /// + /// The of the service. + /// The name of the service. + /// true if the container can create the requested service, otherwise false. + bool CanGetInstance(Type serviceType, string serviceName); + + /// + /// Injects the property dependencies for a given . + /// + /// The target instance for which to inject its property dependencies. + /// The with its property dependencies injected. + object InjectProperties(object instance); + } + + /// + /// Represents a class that manages the lifetime of a service instance. + /// + internal interface ILifetime + { + /// + /// Returns a service instance according to the specific lifetime characteristics. + /// + /// The function delegate used to create a new service instance. + /// The of the current service request. + /// The requested services instance. + object GetInstance(Func createInstance, Scope scope); + } + + /// + /// Represents a class that acts as a composition root for an instance. + /// + internal interface ICompositionRoot + { + /// + /// Composes services by adding services to the . + /// + /// The target . + void Compose(IServiceRegistry serviceRegistry); + } + + /// + /// Represents a class that extracts a set of types from an . + /// + internal interface ITypeExtractor + { + /// + /// Extracts types found in the given . + /// + /// The for which to extract types. + /// A set of types found in the given . + Type[] Execute(Assembly assembly); + } + + /// + /// Represents a class that is responsible for selecting injectable properties. + /// + internal interface IPropertySelector + { + /// + /// Selects properties that represents a dependency from the given . + /// + /// The for which to select the properties. + /// A list of injectable properties. + IEnumerable Execute(Type type); + } + + /// + /// Represents a class that is responsible for selecting the property dependencies for a given . + /// + internal interface IPropertyDependencySelector + { + /// + /// Selects the property dependencies for the given . + /// + /// The for which to select the property dependencies. + /// A list of instances that represents the property + /// dependencies for the given . + IEnumerable Execute(Type type); + } + + /// + /// Represents a class that is responsible for selecting the constructor dependencies for a given . + /// + internal interface IConstructorDependencySelector + { + /// + /// Selects the constructor dependencies for the given . + /// + /// The for which to select the constructor dependencies. + /// A list of instances that represents the constructor + /// dependencies for the given . + IEnumerable Execute(ConstructorInfo constructor); + } + + /// + /// Represents a class that is capable of building a instance + /// based on a . + /// + internal interface IConstructionInfoBuilder + { + /// + /// Returns a instance based on the given . + /// + /// The for which to return a instance. + /// A instance that describes how to create a service instance. + ConstructionInfo Execute(Registration registration); + } + + /// + /// Represents a class that keeps track of a instance for each . + /// + internal interface IConstructionInfoProvider + { + /// + /// Gets a instance for the given . + /// + /// The for which to get a instance. + /// The instance that describes how to create an instance of the given . + ConstructionInfo GetConstructionInfo(Registration registration); + + /// + /// Invalidates the and causes new instances + /// to be created when the method is called. + /// + void Invalidate(); + } + + /// + /// Represents a class that builds a instance based on a . + /// + internal interface ILambdaConstructionInfoBuilder + { + /// + /// Parses the and returns a instance. + /// + /// The to parse. + /// A instance. + ConstructionInfo Execute(LambdaExpression lambdaExpression); + } + + /// + /// Represents a class that builds a instance based on the implementing . + /// + internal interface ITypeConstructionInfoBuilder + { + /// + /// Analyzes the and returns a instance. + /// + /// The that represents the implementing type to analyze. + /// A instance. + ConstructionInfo Execute(Registration registration); + } + + /// + /// Represents a class that selects the constructor to be used for creating a new service instance. + /// + internal interface IConstructorSelector + { + /// + /// Selects the constructor to be used when creating a new instance of the . + /// + /// The for which to return a . + /// A instance that represents the constructor to be used + /// when creating a new instance of the . + ConstructorInfo Execute(Type implementingType); + } + + /// + /// Represents a class that is responsible loading a set of assemblies based on the given search pattern. + /// + internal interface IAssemblyLoader + { + /// + /// Loads a set of assemblies based on the given . + /// + /// The search pattern to use. + /// A list of assemblies based on the given . + IEnumerable Load(string searchPattern); + } + + /// + /// Represents a class that is capable of scanning an assembly and register services into an instance. + /// + internal interface IAssemblyScanner + { + /// + /// Scans the target and registers services found within the assembly. + /// + /// The to scan. + /// The target instance. + /// The instance that controls the lifetime of the registered service. + /// A function delegate that determines if a service implementation should be registered. + void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func lifetime, Func shouldRegister); + + /// + /// Scans the target and executes composition roots found within the . + /// + /// The to scan. + /// The target instance. + void Scan(Assembly assembly, IServiceRegistry serviceRegistry); + } + + /// + /// Represents a class that is responsible for instantiating and executing an . + /// + internal interface ICompositionRootExecutor + { + /// + /// Creates an instance of the and executes the method. + /// + /// The concrete type to be instantiated and executed. + void Execute(Type compositionRootType); + } + + /// + /// Represents an abstraction of the class that provides information + /// about the currently on the stack. + /// + internal interface IEmitter + { + /// + /// Gets the currently on the stack. + /// + Type StackType { get; } + + /// + /// Gets a list containing each to be emitted into the dynamic method. + /// + List Instructions { get; } + + /// + /// Puts the specified instruction onto the stream of instructions. + /// + /// The Microsoft Intermediate Language (MSIL) instruction to be put onto the stream. + void Emit(OpCode code); + + /// + /// Puts the specified instruction and numerical argument onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be put onto the stream. + /// The numerical argument pushed onto the stream immediately after the instruction. + void Emit(OpCode code, int arg); + + /// + /// Puts the specified instruction and numerical argument onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be put onto the stream. + /// The numerical argument pushed onto the stream immediately after the instruction. + void Emit(OpCode code, sbyte arg); + + /// + /// Puts the specified instruction and numerical argument onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be put onto the stream. + /// The numerical argument pushed onto the stream immediately after the instruction. + void Emit(OpCode code, byte arg); + + /// + /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream followed by the metadata token for the given type. + /// + /// The MSIL instruction to be put onto the stream. + /// A representing the type metadata token. + void Emit(OpCode code, Type type); + + /// + /// Puts the specified instruction and metadata token for the specified constructor onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be emitted onto the stream. + /// A representing a constructor. + void Emit(OpCode code, ConstructorInfo constructor); + + /// + /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream followed by the index of the given local variable. + /// + /// The MSIL instruction to be emitted onto the stream. + /// A local variable. + void Emit(OpCode code, LocalBuilder localBuilder); + + /// + /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream followed by the metadata token for the given method. + /// + /// The MSIL instruction to be emitted onto the stream. + /// A representing a method. + void Emit(OpCode code, MethodInfo methodInfo); + + /// + /// Declares a local variable of the specified type. + /// + /// A object that represents the type of the local variable. + /// The declared local variable. + LocalBuilder DeclareLocal(Type type); + } + + /// + /// Represents a dynamic method skeleton for emitting the code needed to resolve a service instance. + /// + internal interface IMethodSkeleton + { + /// + /// Gets the for the this dynamic method. + /// + /// The for this dynamic method. + IEmitter GetEmitter(); + + /// + /// Completes the dynamic method and creates a delegate that can be used to execute it. + /// + /// A delegate type whose signature matches that of the dynamic method. + /// A delegate of the specified type, which can be used to execute the dynamic method. + Delegate CreateDelegate(Type delegateType); + } + + /// + /// Represents a class that is capable of providing the current . + /// + internal interface IScopeManagerProvider + { + /// + /// Returns the that is responsible for managing scopes. + /// + /// The that is responsible for managing scopes. + ScopeManager GetScopeManager(); + } + + /// + /// Extends the class. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class ExpressionExtensions + { + /// + /// Flattens the into an . + /// + /// The target . + /// The represented as a list of sub expressions. + public static IEnumerable AsEnumerable(this Expression expression) + { + var flattener = new ExpressionTreeFlattener(); + return flattener.Flatten(expression); + } + + private class ExpressionTreeFlattener : ExpressionVisitor + { + private readonly ICollection nodes = new Collection(); + + public IEnumerable Flatten(Expression expression) + { + Visit(expression); + return nodes; + } + + public override Expression Visit(Expression node) + { + nodes.Add(node); + return base.Visit(node); + } + } + } + + /// + /// Extends the class. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class ImmutableHashTreeExtensions + { + /// + /// Searches for a using the given . + /// + /// The type of the key. + /// The type of the value. + /// The target . + /// The key of the to get. + /// If found, the with the given , otherwise the default . + public static TValue Search(this ImmutableHashTree tree, TKey key) + { + int hashCode = key.GetHashCode(); + + while (tree.Height != 0 && tree.HashCode != hashCode) + { + tree = hashCode < tree.HashCode ? tree.Left : tree.Right; + } + + if (!tree.IsEmpty && (ReferenceEquals(tree.Key, key) || Equals(tree.Key, key))) + { + return tree.Value; + } + + if (tree.Duplicates.Items.Length > 0) + { + foreach (var keyValue in tree.Duplicates.Items) + { + if (ReferenceEquals(keyValue.Key, key) || Equals(keyValue.Key, key)) + { + return keyValue.Value; + } + } + } + + return default(TValue); + } + + /// + /// Adds a new element to the . + /// + /// The type of the key. + /// The type of the value. + /// The target . + /// The key to be associated with the value. + /// The value to be added to the tree. + /// A new that contains the new key/value pair. + public static ImmutableHashTree Add(this ImmutableHashTree tree, TKey key, TValue value) + { + if (tree.IsEmpty) + { + return new ImmutableHashTree(key, value, tree, tree); + } + + int hashCode = key.GetHashCode(); + + if (hashCode > tree.HashCode) + { + return AddToRightBranch(tree, key, value); + } + + if (hashCode < tree.HashCode) + { + return AddToLeftBranch(tree, key, value); + } + + return new ImmutableHashTree(key, value, tree); + } + + private static ImmutableHashTree AddToLeftBranch(ImmutableHashTree tree, TKey key, TValue value) + { + return new ImmutableHashTree(tree.Key, tree.Value, tree.Left.Add(key, value), tree.Right); + } + + private static ImmutableHashTree AddToRightBranch(ImmutableHashTree tree, TKey key, TValue value) + { + return new ImmutableHashTree(tree.Key, tree.Value, tree.Left, tree.Right.Add(key, value)); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class LazyTypeExtensions + { + private static readonly ThreadSafeDictionary Constructors = new ThreadSafeDictionary(); + + public static ConstructorInfo GetLazyConstructor(this Type type) + { + return Constructors.GetOrAdd(type, GetConstructor); + } + + private static ConstructorInfo GetConstructor(Type type) + { + Type closedGenericLazyType = typeof(Lazy<>).MakeGenericType(type); + return closedGenericLazyType.GetConstructor(new[] { type.GetFuncType() }); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class EnumerableTypeExtensions + { + private static readonly ThreadSafeDictionary EnumerableTypes = new ThreadSafeDictionary(); + + public static Type GetEnumerableType(this Type returnType) + { + return EnumerableTypes.GetOrAdd(returnType, CreateEnumerableType); + } + + private static Type CreateEnumerableType(Type type) + { + return typeof(IEnumerable<>).MakeGenericType(type); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class FuncTypeExtensions + { + private static readonly ThreadSafeDictionary FuncTypes = new ThreadSafeDictionary(); + + public static Type GetFuncType(this Type returnType) + { + return FuncTypes.GetOrAdd(returnType, CreateFuncType); + } + + private static Type CreateFuncType(Type type) + { + return typeof(Func<>).MakeGenericType(type); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class LifetimeHelper + { + static LifetimeHelper() + { + GetInstanceMethod = typeof(ILifetime).GetMethod("GetInstance"); + GetCurrentScopeMethod = typeof(ScopeManager).GetProperty("CurrentScope").GetGetMethod(); + GetScopeManagerMethod = typeof(IScopeManagerProvider).GetMethod("GetScopeManager"); + } + + public static MethodInfo GetInstanceMethod { get; private set; } + + public static MethodInfo GetCurrentScopeMethod { get; private set; } + + public static MethodInfo GetScopeManagerMethod { get; private set; } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class DelegateTypeExtensions + { + private static readonly MethodInfo OpenGenericGetInstanceMethodInfo = + typeof(IServiceFactory).GetMethod("GetInstance", new Type[] { }); + + private static readonly ThreadSafeDictionary GetInstanceMethods = + new ThreadSafeDictionary(); + + public static Delegate CreateGetInstanceDelegate(this Type serviceType, IServiceFactory serviceFactory) + { + Type delegateType = serviceType.GetFuncType(); + MethodInfo getInstanceMethod = GetInstanceMethods.GetOrAdd(serviceType, CreateGetInstanceMethod); + return getInstanceMethod.CreateDelegate(delegateType, serviceFactory); + } + + private static MethodInfo CreateGetInstanceMethod(Type type) + { + return OpenGenericGetInstanceMethodInfo.MakeGenericMethod(type); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class NamedDelegateTypeExtensions + { + private static readonly MethodInfo CreateInstanceDelegateMethodInfo = + typeof(NamedDelegateTypeExtensions).GetPrivateStaticMethod("CreateInstanceDelegate"); + + private static readonly ThreadSafeDictionary CreateInstanceDelegateMethods = + new ThreadSafeDictionary(); + + public static Delegate CreateNamedGetInstanceDelegate(this Type serviceType, string serviceName, IServiceFactory factory) + { + MethodInfo createInstanceDelegateMethodInfo = CreateInstanceDelegateMethods.GetOrAdd( + serviceType, + CreateClosedGenericCreateInstanceDelegateMethod); + + return (Delegate)createInstanceDelegateMethodInfo.Invoke(null, new object[] { factory, serviceName }); + } + + private static MethodInfo CreateClosedGenericCreateInstanceDelegateMethod(Type type) + { + return CreateInstanceDelegateMethodInfo.MakeGenericMethod(type); + } + + // ReSharper disable UnusedMember.Local + private static Func CreateInstanceDelegate(IServiceFactory factory, string serviceName) + // ReSharper restore UnusedMember.Local + { + return () => factory.GetInstance(serviceName); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class ReflectionHelper + { + private static readonly Lazy> GetInstanceWithParametersMethods; + + static ReflectionHelper() + { + GetInstanceWithParametersMethods = CreateLazyGetInstanceWithParametersMethods(); + } + + public static MethodInfo GetGetInstanceWithParametersMethod(Type serviceType) + { + return GetInstanceWithParametersMethods.Value.GetOrAdd(serviceType, CreateGetInstanceWithParametersMethod); + } + + public static Delegate CreateGetNamedInstanceWithParametersDelegate(IServiceFactory factory, Type delegateType, string serviceName) + { + Type[] genericTypeArguments = delegateType.GetGenericTypeArguments(); + var openGenericMethod = + typeof(ReflectionHelper).GetPrivateStaticMethods() + .Single( + m => + m.GetGenericArguments().Length == genericTypeArguments.Length + && m.Name == "CreateGenericGetNamedParameterizedInstanceDelegate"); + var closedGenericMethod = openGenericMethod.MakeGenericMethod(genericTypeArguments); + return (Delegate)closedGenericMethod.Invoke(null, new object[] { factory, serviceName }); + } + + private static Lazy> CreateLazyGetInstanceWithParametersMethods() + { + return new Lazy>( + () => new ThreadSafeDictionary()); + } + + private static MethodInfo CreateGetInstanceWithParametersMethod(Type serviceType) + { + Type[] genericTypeArguments = serviceType.GetGenericTypeArguments(); + MethodInfo openGenericMethod = + typeof(IServiceFactory).GetMethods().Single(m => m.Name == "GetInstance" + && m.GetGenericArguments().Length == genericTypeArguments.Length && m.GetParameters().All(p => p.Name != "serviceName")); + + MethodInfo closedGenericMethod = openGenericMethod.MakeGenericMethod(genericTypeArguments); + + return closedGenericMethod; + } + + // ReSharper disable UnusedMember.Local + private static Func CreateGenericGetNamedParameterizedInstanceDelegate(IServiceFactory factory, string serviceName) + // ReSharper restore UnusedMember.Local + { + return arg => factory.GetInstance(arg, serviceName); + } + + // ReSharper disable UnusedMember.Local + private static Func CreateGenericGetNamedParameterizedInstanceDelegate(IServiceFactory factory, string serviceName) + // ReSharper restore UnusedMember.Local + { + return (arg1, arg2) => factory.GetInstance(arg1, arg2, serviceName); + } + + // ReSharper disable UnusedMember.Local + private static Func CreateGenericGetNamedParameterizedInstanceDelegate(IServiceFactory factory, string serviceName) + // ReSharper restore UnusedMember.Local + { + return (arg1, arg2, arg3) => factory.GetInstance(arg1, arg2, arg3, serviceName); + } + + // ReSharper disable UnusedMember.Local + private static Func CreateGenericGetNamedParameterizedInstanceDelegate(IServiceFactory factory, string serviceName) + // ReSharper restore UnusedMember.Local + { + return (arg1, arg2, arg3, arg4) => factory.GetInstance(arg1, arg2, arg3, arg4, serviceName); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class TypeHelper + { + public static Type[] GetGenericTypeArguments(this Type type) + { + return type.GetGenericArguments(); + } + + public static bool IsClass(this Type type) + { + return type.IsClass; + } + + public static bool IsAbstract(this Type type) + { + return type.IsAbstract; + } + + public static bool IsNestedPrivate(this Type type) + { + return type.IsNestedPrivate; + } + + public static bool IsGenericType(this Type type) + { + return type.IsGenericType; + } + + public static bool ContainsGenericParameters(this Type type) + { + return type.ContainsGenericParameters; + } + + public static Type GetBaseType(this Type type) + { + return type.BaseType; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.IsGenericTypeDefinition; + } + + public static Assembly GetAssembly(this Type type) + { + return type.Assembly; + } + + public static bool IsValueType(this Type type) + { + return type.IsValueType; + } + + public static MethodInfo GetMethodInfo(this Delegate del) + { + return del.Method; + } + + public static MethodInfo GetPrivateMethod(this Type type, string name) + { + return type.GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic); + } + + public static MethodInfo GetPrivateStaticMethod(this Type type, string name) + { + return type.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic); + } + + public static MethodInfo[] GetPrivateStaticMethods(this Type type) + { + return type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic); + } + + public static IEnumerable GetCustomAttributes(this Assembly assembly, Type attributeType) + { + return assembly.GetCustomAttributes(attributeType, false).Cast(); + } + + public static bool IsEnumerableOfT(this Type serviceType) + { + return serviceType.IsGenericType() && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>); + } + + public static bool IsListOfT(this Type serviceType) + { + return serviceType.IsGenericType() && serviceType.GetGenericTypeDefinition() == typeof(IList<>); + } + + public static bool IsCollectionOfT(this Type serviceType) + { + return serviceType.IsGenericType() && serviceType.GetGenericTypeDefinition() == typeof(ICollection<>); + } + + public static bool IsReadOnlyCollectionOfT(this Type serviceType) + { + return serviceType.IsGenericType() && serviceType.GetGenericTypeDefinition() == typeof(IReadOnlyCollection<>); + } + + public static bool IsReadOnlyListOfT(this Type serviceType) + { + return serviceType.IsGenericType() && serviceType.GetGenericTypeDefinition() == typeof(IReadOnlyList<>); + } + + public static bool IsLazy(this Type serviceType) + { + return serviceType.IsGenericType() && serviceType.GetGenericTypeDefinition() == typeof(Lazy<>); + } + + public static bool IsFunc(this Type serviceType) + { + return serviceType.IsGenericType() && serviceType.GetGenericTypeDefinition() == typeof(Func<>); + } + + public static bool IsFuncWithParameters(this Type serviceType) + { + if (!serviceType.IsGenericType()) + { + return false; + } + + Type genericTypeDefinition = serviceType.GetGenericTypeDefinition(); + + return genericTypeDefinition == typeof(Func<,>) || genericTypeDefinition == typeof(Func<,,>) + || genericTypeDefinition == typeof(Func<,,,>) || genericTypeDefinition == typeof(Func<,,,,>); + } + + public static bool IsClosedGeneric(this Type serviceType) + { + return serviceType.IsGenericType() && !serviceType.IsGenericTypeDefinition(); + } + + public static Type GetElementType(Type type) + { + if (type.IsGenericType() && type.GetGenericTypeArguments().Count() == 1) + { + return type.GetGenericTypeArguments()[0]; + } + + return type.GetElementType(); + } + } + + /// + /// Extends the interface with a set of methods + /// that optimizes and simplifies emitting MSIL instructions. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class EmitterExtensions + { + /// + /// Performs a cast or unbox operation if the current is + /// different from the given . + /// + /// The target . + /// The requested stack type. + public static void UnboxOrCast(this IEmitter emitter, Type type) + { + if (!type.IsAssignableFrom(emitter.StackType)) + { + emitter.Emit(type.IsValueType() ? OpCodes.Unbox_Any : OpCodes.Castclass, type); + } + } + + /// + /// Pushes a constant value onto the evaluation stack. + /// + /// The target . + /// The index of the constant value to be pushed onto the stack. + /// The requested stack type. + public static void PushConstant(this IEmitter emitter, int index, Type type) + { + emitter.PushConstant(index); + emitter.UnboxOrCast(type); + } + + /// + /// Pushes a constant value onto the evaluation stack as a object reference. + /// + /// The target . + /// The index of the constant value to be pushed onto the stack. + public static void PushConstant(this IEmitter emitter, int index) + { + emitter.PushArgument(0); + emitter.Push(index); + emitter.PushArrayElement(); + } + + /// + /// Pushes the element containing an object reference at a specified index onto the stack. + /// + /// The target . + public static void PushArrayElement(this IEmitter emitter) + { + emitter.Emit(OpCodes.Ldelem_Ref); + } + + /// + /// Pushes the arguments associated with a service request onto the stack. + /// The arguments are found as an array in the last element of the constants array + /// that is passed into the dynamic method. + /// + /// The target . + /// A list of instances that + /// represent the arguments to be pushed onto the stack. + public static void PushArguments(this IEmitter emitter, ParameterInfo[] parameters) + { + var argumentArray = emitter.DeclareLocal(typeof(object[])); + emitter.Emit(OpCodes.Ldarg_0); + emitter.Emit(OpCodes.Ldarg_0); + emitter.Emit(OpCodes.Ldlen); + emitter.Emit(OpCodes.Conv_I4); + emitter.Emit(OpCodes.Ldc_I4_1); + emitter.Emit(OpCodes.Sub); + emitter.Emit(OpCodes.Ldelem_Ref); + emitter.Emit(OpCodes.Castclass, typeof(object[])); + emitter.Emit(OpCodes.Stloc, argumentArray); + + for (int i = 0; i < parameters.Length; i++) + { + emitter.Emit(OpCodes.Ldloc, argumentArray); + emitter.Emit(OpCodes.Ldc_I4, i); + emitter.Emit(OpCodes.Ldelem_Ref); + emitter.Emit( + parameters[i].ParameterType.IsValueType() ? OpCodes.Unbox_Any : OpCodes.Castclass, + parameters[i].ParameterType); + } + } + + /// + /// Calls a late-bound method on an object, pushing the return value onto the stack. + /// + /// The target . + /// The that represents the method to be called. + public static void Call(this IEmitter emitter, MethodInfo methodInfo) + { + emitter.Emit(OpCodes.Callvirt, methodInfo); + } + + /// + /// Pushes a new instance onto the stack. + /// + /// The target . + /// The that represent the object to be created. + public static void New(this IEmitter emitter, ConstructorInfo constructorInfo) + { + emitter.Emit(OpCodes.Newobj, constructorInfo); + } + + /// + /// Pushes the given onto the stack. + /// + /// The target . + /// The to be pushed onto the stack. + public static void Push(this IEmitter emitter, LocalBuilder localBuilder) + { + int index = localBuilder.LocalIndex; + switch (index) + { + case 0: + emitter.Emit(OpCodes.Ldloc_0); + return; + case 1: + emitter.Emit(OpCodes.Ldloc_1); + return; + case 2: + emitter.Emit(OpCodes.Ldloc_2); + return; + case 3: + emitter.Emit(OpCodes.Ldloc_3); + return; + } + + if (index <= 255) + { + emitter.Emit(OpCodes.Ldloc_S, (byte)index); + } + else + { + emitter.Emit(OpCodes.Ldloc, index); + } + } + + /// + /// Pushes an argument with the given onto the stack. + /// + /// The target . + /// The index of the argument to be pushed onto the stack. + public static void PushArgument(this IEmitter emitter, int index) + { + switch (index) + { + case 0: + emitter.Emit(OpCodes.Ldarg_0); + return; + case 1: + emitter.Emit(OpCodes.Ldarg_1); + return; + case 2: + emitter.Emit(OpCodes.Ldarg_2); + return; + case 3: + emitter.Emit(OpCodes.Ldarg_3); + return; + } + + if (index <= 255) + { + emitter.Emit(OpCodes.Ldarg_S, (byte)index); + } + else + { + emitter.Emit(OpCodes.Ldarg, index); + } + } + + /// + /// Stores the value currently on top of the stack in the given . + /// + /// The target . + /// The for which the value is to be stored. + public static void Store(this IEmitter emitter, LocalBuilder localBuilder) + { + int index = localBuilder.LocalIndex; + switch (index) + { + case 0: + emitter.Emit(OpCodes.Stloc_0); + return; + case 1: + emitter.Emit(OpCodes.Stloc_1); + return; + case 2: + emitter.Emit(OpCodes.Stloc_2); + return; + case 3: + emitter.Emit(OpCodes.Stloc_3); + return; + } + + if (index <= 255) + { + emitter.Emit(OpCodes.Stloc_S, (byte)index); + } + else + { + emitter.Emit(OpCodes.Stloc, index); + } + } + + /// + /// Pushes a new array of the given onto the stack. + /// + /// The target . + /// The element of the new array. + public static void PushNewArray(this IEmitter emitter, Type elementType) + { + emitter.Emit(OpCodes.Newarr, elementType); + } + + /// + /// Pushes an value onto the stack. + /// + /// The target . + /// The value to be pushed onto the stack. + public static void Push(this IEmitter emitter, int value) + { + switch (value) + { + case 0: + emitter.Emit(OpCodes.Ldc_I4_0); + return; + case 1: + emitter.Emit(OpCodes.Ldc_I4_1); + return; + case 2: + emitter.Emit(OpCodes.Ldc_I4_2); + return; + case 3: + emitter.Emit(OpCodes.Ldc_I4_3); + return; + case 4: + emitter.Emit(OpCodes.Ldc_I4_4); + return; + case 5: + emitter.Emit(OpCodes.Ldc_I4_5); + return; + case 6: + emitter.Emit(OpCodes.Ldc_I4_6); + return; + case 7: + emitter.Emit(OpCodes.Ldc_I4_7); + return; + case 8: + emitter.Emit(OpCodes.Ldc_I4_8); + return; + } + + if (value > -129 && value < 128) + { + emitter.Emit(OpCodes.Ldc_I4_S, (sbyte)value); + } + else + { + emitter.Emit(OpCodes.Ldc_I4, value); + } + } + + /// + /// Performs a cast of the value currently on top of the stack to the given . + /// + /// The target . + /// The for which the value will be casted into. + public static void Cast(this IEmitter emitter, Type type) + { + emitter.Emit(OpCodes.Castclass, type); + } + + /// + /// Returns from the current method. + /// + /// The target . + public static void Return(this IEmitter emitter) + { + emitter.Emit(OpCodes.Ret); + } + } + + /// + /// An ultra lightweight service container. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ServiceContainer : IServiceContainer + { + private const string UnresolvedDependencyError = "Unresolved dependency {0}"; + private readonly Func methodSkeletonFactory; + private readonly ServiceRegistry> emitters = new ServiceRegistry>(); + private readonly object lockObject = new object(); + + private readonly Storage constants = new Storage(); + + private readonly Storage factoryRules = new Storage(); + private readonly Stack> dependencyStack = new Stack>(); + + private readonly ServiceRegistry availableServices = new ServiceRegistry(); + + private readonly Storage decorators = new Storage(); + private readonly Storage overrides = new Storage(); + + private readonly Lazy constructionInfoProvider; + private readonly ICompositionRootExecutor compositionRootExecutor; + + private readonly ITypeExtractor compositionRootTypeExtractor; + private ImmutableHashTree> delegates = + ImmutableHashTree>.Empty; + + private ImmutableHashTree, Func> namedDelegates = + ImmutableHashTree, Func>.Empty; + + private ImmutableHashTree> propertyInjectionDelegates = + ImmutableHashTree>.Empty; + + private bool isLocked; + + /// + /// Initializes a new instance of the class. + /// + public ServiceContainer() + { + var concreteTypeExtractor = new CachedTypeExtractor(new ConcreteTypeExtractor()); + compositionRootTypeExtractor = new CachedTypeExtractor(new CompositionRootTypeExtractor()); + compositionRootExecutor = new CompositionRootExecutor(this); + AssemblyScanner = new AssemblyScanner(concreteTypeExtractor, compositionRootTypeExtractor, compositionRootExecutor); + PropertyDependencySelector = new PropertyDependencySelector(new PropertySelector()); + ConstructorDependencySelector = new ConstructorDependencySelector(); + ConstructorSelector = new MostResolvableConstructorSelector(CanGetInstance); + constructionInfoProvider = new Lazy(CreateConstructionInfoProvider); + methodSkeletonFactory = (returnType, parameterTypes) => new DynamicMethodSkeleton(returnType, parameterTypes); + ScopeManagerProvider = new PerThreadScopeManagerProvider(); + AssemblyLoader = new AssemblyLoader(); + } + + /// + /// Gets or sets the that is responsible + /// for providing the used to manage scopes. + /// + public IScopeManagerProvider ScopeManagerProvider { get; set; } + + /// + /// Gets or sets the instance that + /// is responsible for selecting the property dependencies for a given type. + /// + public IPropertyDependencySelector PropertyDependencySelector { get; set; } + + /// + /// Gets or sets the instance that + /// is responsible for selecting the constructor dependencies for a given constructor. + /// + public IConstructorDependencySelector ConstructorDependencySelector { get; set; } + + /// + /// Gets or sets the instance that is responsible + /// for selecting the constructor to be used when creating new service instances. + /// + public IConstructorSelector ConstructorSelector { get; set; } + + /// + /// Gets or sets the instance that is responsible for scanning assemblies. + /// + public IAssemblyScanner AssemblyScanner { get; set; } + + /// + /// Gets or sets the instance that is responsible for loading assemblies during assembly scanning. + /// + public IAssemblyLoader AssemblyLoader { get; set; } + + /// + /// Gets a list of instances that represents the registered services. + /// + public IEnumerable AvailableServices + { + get + { + return availableServices.Values.SelectMany(t => t.Values); + } + } + + /// + /// Returns true if the container can create the requested service, otherwise false. + /// + /// The of the service. + /// The name of the service. + /// true if the container can create the requested service, otherwise false. + public bool CanGetInstance(Type serviceType, string serviceName) + { + return GetEmitMethod(serviceType, serviceName) != null; + } + + /// + /// Starts a new . + /// + /// + public Scope BeginScope() + { + return ScopeManagerProvider.GetScopeManager().BeginScope(); + } + + /// + /// Ends the current . + /// + public void EndCurrentScope() + { + Scope currentScope = ScopeManagerProvider.GetScopeManager().CurrentScope; + currentScope.Dispose(); + } + + /// + /// Injects the property dependencies for a given . + /// + /// The target instance for which to inject its property dependencies. + /// The with its property dependencies injected. + public object InjectProperties(object instance) + { + var type = instance.GetType(); + + var del = propertyInjectionDelegates.Search(type); + + if (del == null) + { + del = CreatePropertyInjectionDelegate(type); + propertyInjectionDelegates = propertyInjectionDelegates.Add(type, del); + } + + return del(constants.Items, instance); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// The lambdaExpression that describes the dependencies of the service. + /// The name of the service. + /// The instance that controls the lifetime of the registered service. + public void Register(Expression> factory, string serviceName, ILifetime lifetime) + { + RegisterServiceFromLambdaExpression(factory, lifetime, serviceName); + } + + /// + /// Registers a custom factory delegate used to create services that is otherwise unknown to the service container. + /// + /// Determines if the service can be created by the delegate. + /// Creates a service instance according to the predicate. + public void RegisterFallback(Func predicate, Func factory) + { + factoryRules.Add(new FactoryRule { CanCreateInstance = predicate, Factory = factory }); + } + + /// + /// Registers a custom factory delegate used to create services that is otherwise unknown to the service container. + /// + /// Determines if the service can be created by the delegate. + /// Creates a service instance according to the predicate. + /// The instance that controls the lifetime of the registered service. + public void RegisterFallback(Func predicate, Func factory, ILifetime lifetime) + { + factoryRules.Add(new FactoryRule { CanCreateInstance = predicate, Factory = factory, LifeTime = lifetime }); + } + + /// + /// Registers a service based on a instance. + /// + /// The instance that contains service metadata. + public void Register(ServiceRegistration serviceRegistration) + { + var services = GetAvailableServices(serviceRegistration.ServiceType); + var sr = serviceRegistration; + services.AddOrUpdate( + serviceRegistration.ServiceName, + s => AddServiceRegistration(sr), + (k, existing) => UpdateServiceRegistration(existing, sr)); + } + + /// + /// Registers composition roots from the given . + /// + /// The assembly to be scanned for services. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + public void RegisterAssembly(Assembly assembly) + { + Type[] compositionRootTypes = compositionRootTypeExtractor.Execute(assembly); + if (compositionRootTypes.Length == 0) + { + RegisterAssembly(assembly, (serviceType, implementingType) => true); + } + else + { + AssemblyScanner.Scan(assembly, this); + } + } + + /// + /// Registers services from the given . + /// + /// The assembly to be scanned for services. + /// A function delegate that determines if a service implementation should be registered. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + public void RegisterAssembly(Assembly assembly, Func shouldRegister) + { + AssemblyScanner.Scan(assembly, this, () => null, shouldRegister); + } + + /// + /// Registers services from the given . + /// + /// The assembly to be scanned for services. + /// The factory that controls the lifetime of the registered service. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + public void RegisterAssembly(Assembly assembly, Func lifetimeFactory) + { + AssemblyScanner.Scan(assembly, this, lifetimeFactory, (serviceType, implementingType) => true); + } + + /// + /// Registers services from the given . + /// + /// The assembly to be scanned for services. + /// The factory that controls the lifetime of the registered service. + /// A function delegate that determines if a service implementation should be registered. + /// + /// If the target contains an implementation of the interface, this + /// will be used to configure the container. + /// + public void RegisterAssembly(Assembly assembly, Func lifetimeFactory, Func shouldRegister) + { + AssemblyScanner.Scan(assembly, this, lifetimeFactory, shouldRegister); + } + + /// + /// Registers services from the given type. + /// + /// The type of to register from. + public void RegisterFrom() where TCompositionRoot : ICompositionRoot, new() + { + compositionRootExecutor.Execute(typeof(TCompositionRoot)); + } + + /// + /// Registers composition roots from assemblies in the base directory that matches the . + /// + /// The search pattern used to filter the assembly files. + public void RegisterAssembly(string searchPattern) + { + foreach (Assembly assembly in AssemblyLoader.Load(searchPattern)) + { + RegisterAssembly(assembly); + } + } + + /// + /// Decorates the with the given . + /// + /// The target service type. + /// The decorator type used to decorate the . + /// A function delegate that determines if the + /// should be applied to the target . + public void Decorate(Type serviceType, Type decoratorType, Func predicate) + { + var decoratorRegistration = new DecoratorRegistration { ServiceType = serviceType, ImplementingType = decoratorType, CanDecorate = predicate }; + Decorate(decoratorRegistration); + } + + /// + /// Decorates the with the given . + /// + /// The target service type. + /// The decorator type used to decorate the . + public void Decorate(Type serviceType, Type decoratorType) + { + Decorate(serviceType, decoratorType, si => true); + } + + /// + /// Decorates the with the given . + /// + /// The target service type. + /// The decorator type used to decorate the . + public void Decorate() where TDecorator : TService + { + Decorate(typeof(TService), typeof(TDecorator)); + } + + /// + /// Decorates the using the given decorator . + /// + /// The target service type. + /// A factory delegate used to create a decorator instance. + public void Decorate(Expression> factory) + { + var decoratorRegistration = new DecoratorRegistration { FactoryExpression = factory, ServiceType = typeof(TService), CanDecorate = si => true }; + Decorate(decoratorRegistration); + } + + /// + /// Registers a decorator based on a instance. + /// + /// The instance that contains the decorator metadata. + public void Decorate(DecoratorRegistration decoratorRegistration) + { + int index = decorators.Add(decoratorRegistration); + decoratorRegistration.Index = index; + } + + /// + /// Allows a registered service to be overridden by another . + /// + /// A function delegate that is used to determine the service that should be + /// overridden using the returned from the . + /// The factory delegate used to create a that overrides + /// the incoming . + public void Override(Func serviceSelector, Func serviceRegistrationFactory) + { + var serviceOverride = new ServiceOverride + { + CanOverride = serviceSelector, + ServiceRegistrationFactory = serviceRegistrationFactory + }; + overrides.Add(serviceOverride); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The instance that controls the lifetime of the registered service. + public void Register(Type serviceType, Type implementingType, ILifetime lifetime) + { + Register(serviceType, implementingType, string.Empty, lifetime); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + /// The instance that controls the lifetime of the registered service. + public void Register(Type serviceType, Type implementingType, string serviceName, ILifetime lifetime) + { + RegisterService(serviceType, implementingType, lifetime, serviceName); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + public void Register() where TImplementation : TService + { + Register(typeof(TService), typeof(TImplementation)); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The instance that controls the lifetime of the registered service. + public void Register(ILifetime lifetime) where TImplementation : TService + { + Register(typeof(TService), typeof(TImplementation), lifetime); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + public void Register(string serviceName) where TImplementation : TService + { + Register(serviceName, lifetime: null); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + /// The instance that controls the lifetime of the registered service. + public void Register(string serviceName, ILifetime lifetime) where TImplementation : TService + { + Register(typeof(TService), typeof(TImplementation), serviceName, lifetime); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// The lambdaExpression that describes the dependencies of the service. + /// The instance that controls the lifetime of the registered service. + public void Register(Expression> factory, ILifetime lifetime) + { + RegisterServiceFromLambdaExpression(factory, lifetime, string.Empty); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// The lambdaExpression that describes the dependencies of the service. + /// The name of the service. + public void Register(Expression> factory, string serviceName) + { + RegisterServiceFromLambdaExpression(factory, null, serviceName); + } + + /// + /// Registers a concrete type as a service. + /// + /// The service type to register. + public void Register() + { + Register(); + } + + /// + /// Registers a concrete type as a service. + /// + /// The concrete type to register. + public void Register(Type serviceType) + { + Register(serviceType, serviceType); + } + + /// + /// Registers a concrete type as a service. + /// + /// The concrete type to register. + /// The instance that controls the lifetime of the registered service. + public void Register(Type serviceType, ILifetime lifetime) + { + Register(serviceType, serviceType, lifetime); + } + + /// + /// Registers a concrete type as a service. + /// + /// The service type to register. + /// The instance that controls the lifetime of the registered service. + public void Register(ILifetime lifetime) + { + Register(lifetime); + } + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + /// The name of the service. + public void RegisterInstance(TService instance, string serviceName) + { + RegisterInstance(typeof(TService), instance, serviceName); + } + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + public void RegisterInstance(TService instance) + { + RegisterInstance(typeof(TService), instance); + } + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + public void RegisterInstance(Type serviceType, object instance) + { + RegisterInstance(serviceType, instance, string.Empty); + } + + /// + /// Registers the with the given . + /// + /// The service type to register. + /// The instance returned when this service is requested. + /// The name of the service. + public void RegisterInstance(Type serviceType, object instance, string serviceName) + { + RegisterValue(serviceType, instance, serviceName); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The service type to register. + /// The lambdaExpression that describes the dependencies of the service. + public void Register(Expression> factory) + { + RegisterServiceFromLambdaExpression(factory, null, string.Empty); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The parameter type. + /// The service type to register. + /// A factory delegate used to create the instance. + public void Register(Expression> factory) + { + RegisterServiceFromLambdaExpression(factory, null, string.Empty); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The parameter type. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + public void Register(Expression> factory, string serviceName) + { + RegisterServiceFromLambdaExpression(factory, null, serviceName); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + public void Register(Expression> factory) + { + RegisterServiceFromLambdaExpression(factory, null, string.Empty); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + public void Register(Expression> factory, string serviceName) + { + RegisterServiceFromLambdaExpression(factory, null, serviceName); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + public void Register(Expression> factory) + { + RegisterServiceFromLambdaExpression(factory, null, string.Empty); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + public void Register(Expression> factory, string serviceName) + { + RegisterServiceFromLambdaExpression(factory, null, serviceName); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + public void Register(Expression> factory) + { + RegisterServiceFromLambdaExpression(factory, null, string.Empty); + } + + /// + /// Registers the with the that + /// describes the dependencies of the service. + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The service type to register. + /// A factory delegate used to create the instance. + /// The name of the service. + public void Register(Expression> factory, string serviceName) + { + RegisterServiceFromLambdaExpression(factory, null, serviceName); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + /// The name of the service. + public void Register(Type serviceType, Type implementingType, string serviceName) + { + RegisterService(serviceType, implementingType, null, serviceName); + } + + /// + /// Registers the with the . + /// + /// The service type to register. + /// The implementing type. + public void Register(Type serviceType, Type implementingType) + { + RegisterService(serviceType, implementingType, null, string.Empty); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The requested service instance. + public object GetInstance(Type serviceType) + { + var instanceDelegate = delegates.Search(serviceType); + if (instanceDelegate == null) + { + instanceDelegate = CreateDefaultDelegate(serviceType, throwError: true); + } + + return instanceDelegate(constants.Items); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The arguments to be passed to the target instance. + /// The requested service instance. + public object GetInstance(Type serviceType, object[] arguments) + { + var instanceDelegate = delegates.Search(serviceType); + if (instanceDelegate == null) + { + instanceDelegate = CreateDefaultDelegate(serviceType, throwError: true); + } + + object[] constantsWithArguments = constants.Items.Concat(new object[] { arguments }).ToArray(); + + return instanceDelegate(constantsWithArguments); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The arguments to be passed to the target instance. + /// The requested service instance. + public object GetInstance(Type serviceType, string serviceName, object[] arguments) + { + var key = Tuple.Create(serviceType, serviceName); + var instanceDelegate = namedDelegates.Search(key); + if (instanceDelegate == null) + { + instanceDelegate = CreateNamedDelegate(key, throwError: true); + } + + object[] constantsWithArguments = constants.Items.Concat(new object[] { arguments }).ToArray(); + + return instanceDelegate(constantsWithArguments); + } + + /// + /// Gets an instance of the given type. + /// + /// The type of the requested service. + /// The requested service instance. + public TService GetInstance() + { + return (TService)GetInstance(typeof(TService)); + } + + /// + /// Gets a named instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance. + public TService GetInstance(string serviceName) + { + return (TService)GetInstance(typeof(TService), serviceName); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the argument. + /// The type of the requested service. + /// The argument value. + /// The requested service instance. + public TService GetInstance(T value) + { + return (TService)GetInstance(typeof(TService), new object[] { value }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the parameter. + /// The type of the requested service. + /// The argument value. + /// The name of the requested service. + /// The requested service instance. + public TService GetInstance(T value, string serviceName) + { + return (TService)GetInstance(typeof(TService), serviceName, new object[] { value }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The requested service instance. + public TService GetInstance(T1 arg1, T2 arg2) + { + return (TService)GetInstance(typeof(TService), new object[] { arg1, arg2 }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The name of the requested service. + /// The requested service instance. + public TService GetInstance(T1 arg1, T2 arg2, string serviceName) + { + return (TService)GetInstance(typeof(TService), serviceName, new object[] { arg1, arg2 }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The requested service instance. + public TService GetInstance(T1 arg1, T2 arg2, T3 arg3) + { + return (TService)GetInstance(typeof(TService), new object[] { arg1, arg2, arg3 }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The name of the requested service. + /// The requested service instance. + public TService GetInstance(T1 arg1, T2 arg2, T3 arg3, string serviceName) + { + return (TService)GetInstance(typeof(TService), serviceName, new object[] { arg1, arg2, arg3 }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The fourth argument value. + /// The requested service instance. + public TService GetInstance(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + return (TService)GetInstance(typeof(TService), new object[] { arg1, arg2, arg3, arg4 }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the first parameter. + /// The type of the second parameter. + /// The type of the third parameter. + /// The type of the fourth parameter. + /// The type of the requested service. + /// The first argument value. + /// The second argument value. + /// The third argument value. + /// The fourth argument value. + /// The name of the requested service. + /// The requested service instance. + public TService GetInstance(T1 arg1, T2 arg2, T3 arg3, T4 arg4, string serviceName) + { + return (TService)GetInstance(typeof(TService), serviceName, new object[] { arg1, arg2, arg3, arg4 }); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The requested service instance if available, otherwise null. + public object TryGetInstance(Type serviceType) + { + var instanceDelegate = delegates.Search(serviceType); + if (instanceDelegate == null) + { + instanceDelegate = CreateDefaultDelegate(serviceType, throwError: false); + } + + return instanceDelegate(constants.Items); + } + + /// + /// Gets a named instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance if available, otherwise null. + public object TryGetInstance(Type serviceType, string serviceName) + { + var key = Tuple.Create(serviceType, serviceName); + var instanceDelegate = namedDelegates.Search(key); + if (instanceDelegate == null) + { + instanceDelegate = CreateNamedDelegate(key, throwError: false); + } + + return instanceDelegate(constants.Items); + } + + /// + /// Tries to get an instance of the given type. + /// + /// The type of the requested service. + /// The requested service instance if available, otherwise default(T). + public TService TryGetInstance() + { + return (TService)TryGetInstance(typeof(TService)); + } + + /// + /// Tries to get an instance of the given type. + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance if available, otherwise default(T). + public TService TryGetInstance(string serviceName) + { + return (TService)TryGetInstance(typeof(TService), serviceName); + } + + /// + /// Gets a named instance of the given . + /// + /// The type of the requested service. + /// The name of the requested service. + /// The requested service instance. + public object GetInstance(Type serviceType, string serviceName) + { + var key = Tuple.Create(serviceType, serviceName); + var instanceDelegate = namedDelegates.Search(key); + if (instanceDelegate == null) + { + instanceDelegate = CreateNamedDelegate(key, throwError: true); + } + + return instanceDelegate(constants.Items); + } + + /// + /// Gets all instances of the given . + /// + /// The type of services to resolve. + /// A list that contains all implementations of the . + public IEnumerable GetAllInstances(Type serviceType) + { + return (IEnumerable)GetInstance(serviceType.GetEnumerableType()); + } + + /// + /// Gets all instances of type . + /// + /// The type of services to resolve. + /// A list that contains all implementations of the type. + public IEnumerable GetAllInstances() + { + return GetInstance>(); + } + + /// + /// Creates an instance of a concrete class. + /// + /// The type of class for which to create an instance. + /// An instance of . + /// The concrete type will be registered if not already registered with the container. + public TService Create() where TService : class + { + Register(typeof(TService)); + return GetInstance(); + } + + /// + /// Creates an instance of a concrete class. + /// + /// The type of class for which to create an instance. + /// An instance of the . + public object Create(Type serviceType) + { + Register(serviceType); + return GetInstance(serviceType); + } + + /// + /// Disposes any services registered using the . + /// + public void Dispose() + { + var disposableLifetimeInstances = availableServices.Values.SelectMany(t => t.Values) + .Where(sr => sr.Lifetime != null) + .Select(sr => sr.Lifetime) + .Where(lt => lt is IDisposable).Cast(); + foreach (var disposableLifetimeInstance in disposableLifetimeInstances) + { + disposableLifetimeInstance.Dispose(); + } + } + + /// + /// Invalidates the container and causes the compiler to "recompile". + /// + public void Invalidate() + { + Interlocked.Exchange(ref delegates, ImmutableHashTree>.Empty); + Interlocked.Exchange(ref namedDelegates, ImmutableHashTree, Func>.Empty); + Interlocked.Exchange(ref propertyInjectionDelegates, ImmutableHashTree>.Empty); + constants.Clear(); + constructionInfoProvider.Value.Invalidate(); + isLocked = false; + } + + private static void EmitNewArray(IList> emitMethods, Type elementType, IEmitter emitter) + { + LocalBuilder array = emitter.DeclareLocal(elementType.MakeArrayType()); + emitter.Push(emitMethods.Count); + emitter.PushNewArray(elementType); + emitter.Store(array); + + for (int index = 0; index < emitMethods.Count; index++) + { + emitter.Push(array); + emitter.Push(index); + emitMethods[index](emitter); + emitter.UnboxOrCast(elementType); + emitter.Emit(OpCodes.Stelem, elementType); + } + + emitter.Push(array); + } + + private static ILifetime CloneLifeTime(ILifetime lifetime) + { + return lifetime == null ? null : (ILifetime)Activator.CreateInstance(lifetime.GetType()); + } + + private static ConstructorDependency GetConstructorDependencyThatRepresentsDecoratorTarget( + DecoratorRegistration decoratorRegistration, ConstructionInfo constructionInfo) + { + var constructorDependency = + constructionInfo.ConstructorDependencies.FirstOrDefault( + cd => + cd.ServiceType == decoratorRegistration.ServiceType + || (cd.ServiceType.IsLazy() + && cd.ServiceType.GetGenericTypeArguments()[0] == decoratorRegistration.ServiceType)); + return constructorDependency; + } + + private static DecoratorRegistration CreateClosedGenericDecoratorRegistration( + ServiceRegistration serviceRegistration, DecoratorRegistration openGenericDecorator) + { + Type implementingType = openGenericDecorator.ImplementingType; + Type[] genericTypeArguments = serviceRegistration.ServiceType.GetGenericTypeArguments(); + Type closedGenericDecoratorType = implementingType.MakeGenericType(genericTypeArguments); + + var decoratorInfo = new DecoratorRegistration + { + ServiceType = serviceRegistration.ServiceType, + ImplementingType = closedGenericDecoratorType, + CanDecorate = openGenericDecorator.CanDecorate, + Index = openGenericDecorator.Index + }; + return decoratorInfo; + } + + private static Type TryMakeGenericType(Type implementingType, Type[] closedGenericArguments) + { + try + { + return implementingType.MakeGenericType(closedGenericArguments); + } + catch (Exception) + { + return null; + } + } + + private void EmitEnumerable(IList> serviceEmitters, Type elementType, IEmitter emitter) + { + EmitNewArray(serviceEmitters, elementType, emitter); + } + + private Func CreatePropertyInjectionDelegate(Type concreteType) + { + lock (lockObject) + { + IMethodSkeleton methodSkeleton = methodSkeletonFactory(typeof(object), new[] { typeof(object[]), typeof(object) }); + ConstructionInfo constructionInfo = GetContructionInfoForConcreteType(concreteType); + var emitter = methodSkeleton.GetEmitter(); + emitter.PushArgument(1); + emitter.Cast(concreteType); + try + { + EmitPropertyDependencies(constructionInfo, emitter); + } + catch (Exception) + { + dependencyStack.Clear(); + throw; + } + + emitter.Return(); + + isLocked = true; + + return (Func)methodSkeleton.CreateDelegate(typeof(Func)); + } + } + + private ConstructionInfo GetContructionInfoForConcreteType(Type concreteType) + { + var serviceRegistration = GetServiceRegistrationForConcreteType(concreteType); + return GetConstructionInfo(serviceRegistration); + } + + private ServiceRegistration GetServiceRegistrationForConcreteType(Type concreteType) + { + var services = GetAvailableServices(concreteType); + return services.GetOrAdd(string.Empty, s => CreateServiceRegistrationBasedOnConcreteType(concreteType)); + } + + private ServiceRegistration CreateServiceRegistrationBasedOnConcreteType(Type type) + { + var serviceRegistration = new ServiceRegistration + { + ServiceType = type, + ImplementingType = type, + ServiceName = string.Empty, + IgnoreConstructorDependencies = true + }; + return serviceRegistration; + } + + private ConstructionInfoProvider CreateConstructionInfoProvider() + { + return new ConstructionInfoProvider(CreateConstructionInfoBuilder()); + } + + private ConstructionInfoBuilder CreateConstructionInfoBuilder() + { + return new ConstructionInfoBuilder(() => new LambdaConstructionInfoBuilder(), CreateTypeConstructionInfoBuilder); + } + + private TypeConstructionInfoBuilder CreateTypeConstructionInfoBuilder() + { + return new TypeConstructionInfoBuilder(ConstructorSelector, ConstructorDependencySelector, PropertyDependencySelector); + } + + private Func CreateDynamicMethodDelegate(Action serviceEmitter) + { + var methodSkeleton = methodSkeletonFactory(typeof(object), new[] { typeof(object[]) }); + IEmitter emitter = methodSkeleton.GetEmitter(); + serviceEmitter(emitter); + if (emitter.StackType.IsValueType()) + { + emitter.Emit(OpCodes.Box, emitter.StackType); + } + + Instruction lastInstruction = emitter.Instructions.Last(); + + if (lastInstruction.Code == OpCodes.Castclass) + { + emitter.Instructions.Remove(lastInstruction); + } + + emitter.Return(); + + isLocked = true; + + return (Func)methodSkeleton.CreateDelegate(typeof(Func)); + } + + private Func WrapAsFuncDelegate(Func instanceDelegate) + { + return () => instanceDelegate(constants.Items); + } + + private Action GetEmitMethod(Type serviceType, string serviceName) + { + Action emitMethod = GetRegisteredEmitMethod(serviceType, serviceName); + + if (emitMethod == null) + { + emitMethod = TryGetFallbackEmitMethod(serviceType, serviceName); + } + + if (emitMethod == null) + { + AssemblyScanner.Scan(serviceType.GetAssembly(), this); + emitMethod = GetRegisteredEmitMethod(serviceType, serviceName); + } + + if (emitMethod == null) + { + emitMethod = TryGetFallbackEmitMethod(serviceType, serviceName); + } + + return CreateEmitMethodWrapper(emitMethod, serviceType, serviceName); + } + + private Action TryGetFallbackEmitMethod(Type serviceType, string serviceName) + { + Action emitMethod = null; + var rule = factoryRules.Items.FirstOrDefault(r => r.CanCreateInstance(serviceType, serviceName)); + if (rule != null) + { + emitMethod = CreateServiceEmitterBasedOnFactoryRule(rule, serviceType, serviceName); + UpdateEmitMethod(serviceType, serviceName, emitMethod); + } + + return emitMethod; + } + + private Action CreateEmitMethodWrapper(Action emitter, Type serviceType, string serviceName) + { + if (emitter == null) + { + return null; + } + + return ms => + { + if (dependencyStack.Contains(emitter)) + { + throw new InvalidOperationException( + string.Format("Recursive dependency detected: ServiceType:{0}, ServiceName:{1}]", serviceType, serviceName)); + } + + dependencyStack.Push(emitter); + try + { + emitter(ms); + } + finally + { + if (dependencyStack.Count > 0) + { + dependencyStack.Pop(); + } + } + }; + } + + private Action GetRegisteredEmitMethod(Type serviceType, string serviceName) + { + Action emitMethod; + var registrations = GetEmitMethods(serviceType); + registrations.TryGetValue(serviceName, out emitMethod); + return emitMethod ?? CreateEmitMethodForUnknownService(serviceType, serviceName); + } + + private void UpdateEmitMethod(Type serviceType, string serviceName, Action emitMethod) + { + if (emitMethod != null) + { + GetEmitMethods(serviceType).AddOrUpdate(serviceName, s => emitMethod, (s, m) => emitMethod); + } + } + + private ServiceRegistration AddServiceRegistration(ServiceRegistration serviceRegistration) + { + var emitDelegate = ResolveEmitMethod(serviceRegistration); + GetEmitMethods(serviceRegistration.ServiceType).TryAdd(serviceRegistration.ServiceName, emitDelegate); + return serviceRegistration; + } + + private ServiceRegistration UpdateServiceRegistration(ServiceRegistration existingRegistration, ServiceRegistration newRegistration) + { + if (existingRegistration.IsReadOnly || isLocked) + { + return existingRegistration; + } + + Invalidate(); + Action emitMethod = ResolveEmitMethod(newRegistration); + + var serviceEmitters = GetEmitMethods(newRegistration.ServiceType); + serviceEmitters[newRegistration.ServiceName] = emitMethod; + return newRegistration; + } + + private void EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter) + { + var serviceOverrides = overrides.Items.Where(so => so.CanOverride(serviceRegistration)).ToArray(); + foreach (var serviceOverride in serviceOverrides) + { + serviceRegistration = serviceOverride.ServiceRegistrationFactory(this, serviceRegistration); + } + + var serviceDecorators = GetDecorators(serviceRegistration); + if (serviceDecorators.Length > 0) + { + EmitDecorators(serviceRegistration, serviceDecorators, emitter, dm => EmitNewInstance(serviceRegistration, dm)); + } + else + { + EmitNewInstance(serviceRegistration, emitter); + } + } + + private DecoratorRegistration[] GetDecorators(ServiceRegistration serviceRegistration) + { + var registeredDecorators = decorators.Items.Where(d => d.ServiceType == serviceRegistration.ServiceType).ToList(); + + registeredDecorators.AddRange(GetOpenGenericDecoratorRegistrations(serviceRegistration)); + registeredDecorators.AddRange(GetDeferredDecoratorRegistrations(serviceRegistration)); + return registeredDecorators.OrderBy(d => d.Index).ToArray(); + } + + private IEnumerable GetOpenGenericDecoratorRegistrations( + ServiceRegistration serviceRegistration) + { + var registrations = new List(); + if (serviceRegistration.ServiceType.IsGenericType()) + { + var openGenericServiceType = serviceRegistration.ServiceType.GetGenericTypeDefinition(); + var openGenericDecorators = decorators.Items.Where(d => d.ServiceType == openGenericServiceType); + registrations.AddRange( + openGenericDecorators.Select( + openGenericDecorator => + CreateClosedGenericDecoratorRegistration(serviceRegistration, openGenericDecorator))); + } + + return registrations; + } + + private IEnumerable GetDeferredDecoratorRegistrations( + ServiceRegistration serviceRegistration) + { + var registrations = new List(); + + var deferredDecorators = + decorators.Items.Where(ds => ds.CanDecorate(serviceRegistration) && ds.HasDeferredImplementingType); + foreach (var deferredDecorator in deferredDecorators) + { + var decoratorRegistration = new DecoratorRegistration + { + ServiceType = serviceRegistration.ServiceType, + ImplementingType = + deferredDecorator.ImplementingTypeFactory(this, serviceRegistration), + CanDecorate = sr => true, + Index = deferredDecorator.Index + }; + registrations.Add(decoratorRegistration); + } + + return registrations; + } + + private void EmitNewDecoratorInstance(DecoratorRegistration decoratorRegistration, IEmitter emitter, Action pushInstance) + { + ConstructionInfo constructionInfo = GetConstructionInfo(decoratorRegistration); + var constructorDependency = GetConstructorDependencyThatRepresentsDecoratorTarget( + decoratorRegistration, constructionInfo); + + if (constructorDependency != null) + { + constructorDependency.IsDecoratorTarget = true; + } + + if (constructionInfo.FactoryDelegate != null) + { + EmitNewDecoratorUsingFactoryDelegate(constructionInfo.FactoryDelegate, emitter, pushInstance); + } + else + { + EmitNewInstanceUsingImplementingType(emitter, constructionInfo, pushInstance); + } + } + + private void EmitNewDecoratorUsingFactoryDelegate(Delegate factoryDelegate, IEmitter emitter, Action pushInstance) + { + var factoryDelegateIndex = constants.Add(factoryDelegate); + var serviceFactoryIndex = constants.Add(this); + Type funcType = factoryDelegate.GetType(); + emitter.PushConstant(factoryDelegateIndex, funcType); + emitter.PushConstant(serviceFactoryIndex, typeof(IServiceFactory)); + pushInstance(emitter); + MethodInfo invokeMethod = funcType.GetMethod("Invoke"); + emitter.Emit(OpCodes.Callvirt, invokeMethod); + } + + private void EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter) + { + if (serviceRegistration.Value != null) + { + int index = constants.Add(serviceRegistration.Value); + Type serviceType = serviceRegistration.ServiceType; + emitter.PushConstant(index, serviceType); + } + else + { + var constructionInfo = GetConstructionInfo(serviceRegistration); + + if (constructionInfo.FactoryDelegate != null) + { + EmitNewInstanceUsingFactoryDelegate(constructionInfo.FactoryDelegate, emitter); + } + else + { + EmitNewInstanceUsingImplementingType(emitter, constructionInfo, null); + } + } + } + + private void EmitDecorators(ServiceRegistration serviceRegistration, IEnumerable serviceDecorators, IEmitter emitter, Action decoratorTargetEmitMethod) + { + foreach (DecoratorRegistration decorator in serviceDecorators) + { + if (!decorator.CanDecorate(serviceRegistration)) + { + continue; + } + + Action currentDecoratorTargetEmitter = decoratorTargetEmitMethod; + DecoratorRegistration currentDecorator = decorator; + decoratorTargetEmitMethod = e => EmitNewDecoratorInstance(currentDecorator, e, currentDecoratorTargetEmitter); + } + + decoratorTargetEmitMethod(emitter); + } + + private void EmitNewInstanceUsingImplementingType(IEmitter emitter, ConstructionInfo constructionInfo, Action decoratorTargetEmitMethod) + { + EmitConstructorDependencies(constructionInfo, emitter, decoratorTargetEmitMethod); + emitter.Emit(OpCodes.Newobj, constructionInfo.Constructor); + EmitPropertyDependencies(constructionInfo, emitter); + } + + private void EmitNewInstanceUsingFactoryDelegate(Delegate factoryDelegate, IEmitter emitter) + { + var factoryDelegateIndex = constants.Add(factoryDelegate); + var serviceFactoryIndex = constants.Add(this); + Type funcType = factoryDelegate.GetType(); + emitter.PushConstant(factoryDelegateIndex, funcType); + emitter.PushConstant(serviceFactoryIndex, typeof(IServiceFactory)); + if (factoryDelegate.GetMethodInfo().GetParameters().Length > 2) + { + var parameters = factoryDelegate.GetMethodInfo().GetParameters().Skip(2).ToArray(); + emitter.PushArguments(parameters); + } + + MethodInfo invokeMethod = funcType.GetMethod("Invoke"); + emitter.Call(invokeMethod); + } + + private void EmitConstructorDependencies(ConstructionInfo constructionInfo, IEmitter emitter, Action decoratorTargetEmitter) + { + foreach (ConstructorDependency dependency in constructionInfo.ConstructorDependencies) + { + if (!dependency.IsDecoratorTarget) + { + EmitConstructorDependency(emitter, dependency); + } + else + { + if (dependency.ServiceType.IsLazy()) + { + Action instanceEmitter = decoratorTargetEmitter; + decoratorTargetEmitter = CreateEmitMethodBasedOnLazyServiceRequest( + dependency.ServiceType, t => CreateTypedInstanceDelegate(instanceEmitter, t)); + } + + decoratorTargetEmitter(emitter); + } + } + } + + private Delegate CreateTypedInstanceDelegate(Action emitter, Type serviceType) + { + var openGenericMethod = GetType().GetPrivateMethod("CreateGenericDynamicMethodDelegate"); + var closedGenericMethod = openGenericMethod.MakeGenericMethod(serviceType); + var del = WrapAsFuncDelegate(CreateDynamicMethodDelegate(emitter)); + return (Delegate)closedGenericMethod.Invoke(this, new object[] { del }); + } + + // ReSharper disable UnusedMember.Local + private Func CreateGenericDynamicMethodDelegate(Func del) + // ReSharper restore UnusedMember.Local + { + return () => (T)del(); + } + + private void EmitConstructorDependency(IEmitter emitter, Dependency dependency) + { + var emitMethod = GetEmitMethodForDependency(dependency); + + try + { + emitMethod(emitter); + emitter.UnboxOrCast(dependency.ServiceType); + } + catch (InvalidOperationException ex) + { + throw new InvalidOperationException(string.Format(UnresolvedDependencyError, dependency), ex); + } + } + + private void EmitPropertyDependency(IEmitter emitter, PropertyDependency propertyDependency, LocalBuilder instanceVariable) + { + var propertyDependencyEmitMethod = GetEmitMethodForDependency(propertyDependency); + + if (propertyDependencyEmitMethod == null) + { + return; + } + + emitter.Push(instanceVariable); + propertyDependencyEmitMethod(emitter); + emitter.UnboxOrCast(propertyDependency.ServiceType); + emitter.Call(propertyDependency.Property.GetSetMethod()); + } + + private Action GetEmitMethodForDependency(Dependency dependency) + { + if (dependency.FactoryExpression != null) + { + return skeleton => EmitDependencyUsingFactoryExpression(skeleton, dependency); + } + + Action emitter = GetEmitMethod(dependency.ServiceType, dependency.ServiceName); + if (emitter == null) + { + emitter = GetEmitMethod(dependency.ServiceType, dependency.Name); + if (emitter == null && dependency.IsRequired) + { + throw new InvalidOperationException(string.Format(UnresolvedDependencyError, dependency)); + } + } + + return emitter; + } + + private void EmitDependencyUsingFactoryExpression(IEmitter emitter, Dependency dependency) + { + var parameterExpression = (ParameterExpression)dependency.FactoryExpression.AsEnumerable().FirstOrDefault(e => e is ParameterExpression && e.Type == typeof(IServiceFactory)); + + if (parameterExpression != null) + { + var lambda = Expression.Lambda(dependency.FactoryExpression, new[] { parameterExpression }).Compile(); + MethodInfo methodInfo = lambda.GetType().GetMethod("Invoke"); + emitter.PushConstant(constants.Add(lambda), lambda.GetType()); + emitter.PushConstant(constants.Add(this), typeof(IServiceFactory)); + emitter.Call(methodInfo); + } + else + { + var lambda = Expression.Lambda(dependency.FactoryExpression, new ParameterExpression[] { }).Compile(); + MethodInfo methodInfo = lambda.GetType().GetMethod("Invoke"); + emitter.PushConstant(constants.Add(lambda), lambda.GetType()); + emitter.Call(methodInfo); + } + } + + private void EmitPropertyDependencies(ConstructionInfo constructionInfo, IEmitter emitter) + { + if (constructionInfo.PropertyDependencies.Count == 0) + { + return; + } + + LocalBuilder instanceVariable = emitter.DeclareLocal(constructionInfo.ImplementingType); + emitter.Store(instanceVariable); + foreach (var propertyDependency in constructionInfo.PropertyDependencies) + { + EmitPropertyDependency(emitter, propertyDependency, instanceVariable); + } + + emitter.Push(instanceVariable); + } + + private Action CreateEmitMethodForUnknownService(Type serviceType, string serviceName) + { + Action emitter = null; + if (serviceType.IsLazy()) + { + emitter = CreateEmitMethodBasedOnLazyServiceRequest(serviceType, t => t.CreateGetInstanceDelegate(this)); + } + else if (serviceType.IsFuncWithParameters()) + { + emitter = CreateEmitMethodBasedParameterizedFuncRequest(serviceType, serviceName); + } + else if (serviceType.IsFunc()) + { + emitter = CreateEmitMethodBasedOnFuncServiceRequest(serviceType, serviceName); + } + else if (serviceType.IsEnumerableOfT()) + { + emitter = CreateEmitMethodForEnumerableServiceServiceRequest(serviceType); + } + else if (serviceType.IsArray) + { + emitter = CreateEmitMethodForArrayServiceRequest(serviceType); + } + else if (serviceType.IsReadOnlyCollectionOfT() || serviceType.IsReadOnlyListOfT()) + { + emitter = CreateEmitMethodForReadOnlyCollectionServiceRequest(serviceType); + } + else if (serviceType.IsListOfT()) + { + emitter = CreateEmitMethodForListServiceRequest(serviceType); + } + else if (serviceType.IsCollectionOfT()) + { + emitter = CreateEmitMethodForListServiceRequest(serviceType); + } + else if (CanRedirectRequestForDefaultServiceToSingleNamedService(serviceType, serviceName)) + { + emitter = CreateServiceEmitterBasedOnSingleNamedInstance(serviceType); + } + else if (serviceType.IsClosedGeneric()) + { + emitter = CreateEmitMethodBasedOnClosedGenericServiceRequest(serviceType, serviceName); + } + + UpdateEmitMethod(serviceType, serviceName, emitter); + + return emitter; + } + + private Action CreateEmitMethodBasedOnFuncServiceRequest(Type serviceType, string serviceName) + { + Delegate getInstanceDelegate; + var returnType = serviceType.GetGenericTypeArguments().Single(); + if (string.IsNullOrEmpty(serviceName)) + { + getInstanceDelegate = returnType.CreateGetInstanceDelegate(this); + } + else + { + getInstanceDelegate = returnType.CreateNamedGetInstanceDelegate(serviceName, this); + } + + var constantIndex = constants.Add(getInstanceDelegate); + return e => e.PushConstant(constantIndex, serviceType); + } + + private Action CreateEmitMethodBasedParameterizedFuncRequest(Type serviceType, string serviceName) + { + Delegate getInstanceDelegate; + if (string.IsNullOrEmpty(serviceName)) + { + getInstanceDelegate = CreateGetInstanceWithParametersDelegate(serviceType); + } + else + { + getInstanceDelegate = ReflectionHelper.CreateGetNamedInstanceWithParametersDelegate( + this, + serviceType, + serviceName); + } + + var constantIndex = constants.Add(getInstanceDelegate); + return e => e.PushConstant(constantIndex, serviceType); + } + + private Delegate CreateGetInstanceWithParametersDelegate(Type serviceType) + { + var getInstanceMethod = ReflectionHelper.GetGetInstanceWithParametersMethod(serviceType); + return getInstanceMethod.CreateDelegate(serviceType, this); + } + + private Action CreateServiceEmitterBasedOnFactoryRule(FactoryRule rule, Type serviceType, string serviceName) + { + var serviceRegistration = new ServiceRegistration { ServiceType = serviceType, ServiceName = serviceName, Lifetime = CloneLifeTime(rule.LifeTime) }; + ParameterExpression serviceFactoryParameterExpression = Expression.Parameter(typeof(IServiceFactory)); + ConstantExpression serviceRequestConstantExpression = Expression.Constant(new ServiceRequest(serviceType, serviceName, this)); + ConstantExpression delegateConstantExpression = Expression.Constant(rule.Factory); + Type delegateType = typeof(Func<,>).MakeGenericType(typeof(IServiceFactory), serviceType); + UnaryExpression convertExpression = Expression.Convert( + Expression.Invoke(delegateConstantExpression, serviceRequestConstantExpression), serviceType); + + LambdaExpression lambdaExpression = Expression.Lambda(delegateType, convertExpression, serviceFactoryParameterExpression); + serviceRegistration.FactoryExpression = lambdaExpression; + + if (rule.LifeTime != null) + { + return methodSkeleton => EmitLifetime(serviceRegistration, ms => EmitNewInstanceWithDecorators(serviceRegistration, ms), methodSkeleton); + } + + return methodSkeleton => EmitNewInstanceWithDecorators(serviceRegistration, methodSkeleton); + } + + private Action CreateEmitMethodForArrayServiceRequest(Type serviceType) + { + Action enumerableEmitter = CreateEmitMethodForEnumerableServiceServiceRequest(serviceType); + return enumerableEmitter; + } + + private Action CreateEmitMethodForListServiceRequest(Type serviceType) + { + // Note replace this with getEmitMethod(); + Action enumerableEmitter = CreateEmitMethodForEnumerableServiceServiceRequest(serviceType); + + MethodInfo openGenericToArrayMethod = typeof(Enumerable).GetMethod("ToList"); + MethodInfo closedGenericToListMethod = openGenericToArrayMethod.MakeGenericMethod(TypeHelper.GetElementType(serviceType)); + return ms => + { + enumerableEmitter(ms); + ms.Emit(OpCodes.Call, closedGenericToListMethod); + }; + } + + private Action CreateEmitMethodForReadOnlyCollectionServiceRequest(Type serviceType) + { + Type elementType = TypeHelper.GetElementType(serviceType); + Type closedGenericReadOnlyCollectionType = typeof(ReadOnlyCollection<>).MakeGenericType(elementType); + ConstructorInfo constructorInfo = closedGenericReadOnlyCollectionType.GetConstructors()[0]; + + Action listEmitMethod = CreateEmitMethodForListServiceRequest(serviceType); + + return emitter => + { + listEmitMethod(emitter); + emitter.New(constructorInfo); + }; + } + + private void EnsureEmitMethodsForOpenGenericTypesAreCreated(Type actualServiceType) + { + var openGenericServiceType = actualServiceType.GetGenericTypeDefinition(); + var openGenericServiceEmitters = GetAvailableServices(openGenericServiceType); + foreach (var openGenericEmitterEntry in openGenericServiceEmitters.Keys) + { + GetRegisteredEmitMethod(actualServiceType, openGenericEmitterEntry); + } + } + + private Action CreateEmitMethodBasedOnLazyServiceRequest(Type serviceType, Func valueFactoryDelegate) + { + Type actualServiceType = serviceType.GetGenericTypeArguments()[0]; + Type funcType = actualServiceType.GetFuncType(); + ConstructorInfo lazyConstructor = actualServiceType.GetLazyConstructor(); + Delegate getInstanceDelegate = valueFactoryDelegate(actualServiceType); + var constantIndex = constants.Add(getInstanceDelegate); + + return emitter => + { + emitter.PushConstant(constantIndex, funcType); + emitter.New(lazyConstructor); + }; + } + + private ServiceRegistration GetOpenGenericServiceRegistration(Type openGenericServiceType, string serviceName) + { + var services = GetAvailableServices(openGenericServiceType); + if (services.Count == 0) + { + return null; + } + + ServiceRegistration openGenericServiceRegistration; + services.TryGetValue(serviceName, out openGenericServiceRegistration); + if (openGenericServiceRegistration == null && string.IsNullOrEmpty(serviceName) && services.Count == 1) + { + return services.First().Value; + } + + return openGenericServiceRegistration; + } + + private Action CreateEmitMethodBasedOnClosedGenericServiceRequest(Type closedGenericServiceType, string serviceName) + { + Type openGenericServiceType = closedGenericServiceType.GetGenericTypeDefinition(); + ServiceRegistration openGenericServiceRegistration = + GetOpenGenericServiceRegistration(openGenericServiceType, serviceName); + + if (openGenericServiceRegistration == null) + { + return null; + } + + Type[] closedGenericArguments = closedGenericServiceType.GetGenericTypeArguments(); + + Type closedGenericImplementingType = TryMakeGenericType( + openGenericServiceRegistration.ImplementingType, + closedGenericArguments); + + if (closedGenericImplementingType == null) + { + return null; + } + + var serviceRegistration = new ServiceRegistration + { + ServiceType = closedGenericServiceType, + ImplementingType = + closedGenericImplementingType, + ServiceName = serviceName, + Lifetime = + CloneLifeTime( + openGenericServiceRegistration + .Lifetime) + }; + Register(serviceRegistration); + return GetEmitMethod(serviceRegistration.ServiceType, serviceRegistration.ServiceName); + } + + private Action CreateEmitMethodForEnumerableServiceServiceRequest(Type serviceType) + { + Type actualServiceType = TypeHelper.GetElementType(serviceType); + if (actualServiceType.IsGenericType()) + { + EnsureEmitMethodsForOpenGenericTypesAreCreated(actualServiceType); + } + + var emitMethods = emitters.Where(kv => actualServiceType.IsAssignableFrom(kv.Key)).SelectMany(kv => kv.Value.Values).ToList(); + + if (dependencyStack.Count > 0 && emitMethods.Contains(dependencyStack.Peek())) + { + emitMethods.Remove(dependencyStack.Peek()); + } + + return e => EmitEnumerable(emitMethods, actualServiceType, e); + } + + private Action CreateServiceEmitterBasedOnSingleNamedInstance(Type serviceType) + { + return GetEmitMethod(serviceType, GetEmitMethods(serviceType).First().Key); + } + + private bool CanRedirectRequestForDefaultServiceToSingleNamedService(Type serviceType, string serviceName) + { + return string.IsNullOrEmpty(serviceName) && GetEmitMethods(serviceType).Count == 1; + } + + private ConstructionInfo GetConstructionInfo(Registration registration) + { + return constructionInfoProvider.Value.GetConstructionInfo(registration); + } + + private ThreadSafeDictionary> GetEmitMethods(Type serviceType) + { + return emitters.GetOrAdd(serviceType, s => new ThreadSafeDictionary>(StringComparer.CurrentCultureIgnoreCase)); + } + + private ThreadSafeDictionary GetAvailableServices(Type serviceType) + { + return availableServices.GetOrAdd(serviceType, s => new ThreadSafeDictionary(StringComparer.CurrentCultureIgnoreCase)); + } + + private void RegisterService(Type serviceType, Type implementingType, ILifetime lifetime, string serviceName) + { + var serviceRegistration = new ServiceRegistration { ServiceType = serviceType, ImplementingType = implementingType, ServiceName = serviceName, Lifetime = lifetime }; + Register(serviceRegistration); + } + + private Action ResolveEmitMethod(ServiceRegistration serviceRegistration) + { + if (serviceRegistration.Lifetime == null) + { + return methodSkeleton => EmitNewInstanceWithDecorators(serviceRegistration, methodSkeleton); + } + + return methodSkeleton => EmitLifetime(serviceRegistration, ms => EmitNewInstanceWithDecorators(serviceRegistration, ms), methodSkeleton); + } + + private void EmitLifetime(ServiceRegistration serviceRegistration, Action emitMethod, IEmitter emitter) + { + if (serviceRegistration.Lifetime is PerContainerLifetime) + { + Func instanceDelegate = + WrapAsFuncDelegate(CreateDynamicMethodDelegate(emitMethod)); + var instance = serviceRegistration.Lifetime.GetInstance(instanceDelegate, null); + var instanceIndex = constants.Add(instance); + emitter.PushConstant(instanceIndex, instance.GetType()); + } + else + { + int instanceDelegateIndex = CreateInstanceDelegateIndex(emitMethod); + int lifetimeIndex = CreateLifetimeIndex(serviceRegistration.Lifetime); + int scopeManagerProviderIndex = CreateScopeManagerProviderIndex(); + var getInstanceMethod = LifetimeHelper.GetInstanceMethod; + emitter.PushConstant(lifetimeIndex, typeof(ILifetime)); + emitter.PushConstant(instanceDelegateIndex, typeof(Func)); + emitter.PushConstant(scopeManagerProviderIndex, typeof(IScopeManagerProvider)); + emitter.Call(LifetimeHelper.GetScopeManagerMethod); + emitter.Call(LifetimeHelper.GetCurrentScopeMethod); + emitter.Call(getInstanceMethod); + } + } + + private int CreateScopeManagerProviderIndex() + { + return constants.Add(ScopeManagerProvider); + } + + private int CreateInstanceDelegateIndex(Action emitMethod) + { + return constants.Add(WrapAsFuncDelegate(CreateDynamicMethodDelegate(emitMethod))); + } + + private int CreateLifetimeIndex(ILifetime lifetime) + { + return constants.Add(lifetime); + } + + private Func CreateDefaultDelegate(Type serviceType, bool throwError) + { + var instanceDelegate = CreateDelegate(serviceType, string.Empty, throwError); + if (instanceDelegate == null) + { + return c => null; + } + + Interlocked.Exchange(ref delegates, delegates.Add(serviceType, instanceDelegate)); + return instanceDelegate; + } + + private Func CreateNamedDelegate(Tuple key, bool throwError) + { + var instanceDelegate = CreateDelegate(key.Item1, key.Item2, throwError); + if (instanceDelegate == null) + { + return c => null; + } + + Interlocked.Exchange(ref namedDelegates, namedDelegates.Add(key, instanceDelegate)); + return instanceDelegate; + } + + private Func CreateDelegate(Type serviceType, string serviceName, bool throwError) + { + lock (lockObject) + { + var serviceEmitter = GetEmitMethod(serviceType, serviceName); + if (serviceEmitter == null && throwError) + { + throw new InvalidOperationException( + string.Format("Unable to resolve type: {0}, service name: {1}", serviceType, serviceName)); + } + + if (serviceEmitter != null) + { + try + { + return CreateDynamicMethodDelegate(serviceEmitter); + } + catch (InvalidOperationException ex) + { + dependencyStack.Clear(); + throw new InvalidOperationException( + string.Format("Unable to resolve type: {0}, service name: {1}", serviceType, serviceName), + ex); + } + } + + return null; + } + } + + private void RegisterValue(Type serviceType, object value, string serviceName) + { + var serviceRegistration = new ServiceRegistration { ServiceType = serviceType, ServiceName = serviceName, Value = value, Lifetime = new PerContainerLifetime() }; + Register(serviceRegistration); + } + + private void RegisterServiceFromLambdaExpression( + LambdaExpression factory, ILifetime lifetime, string serviceName) + { + var serviceRegistration = new ServiceRegistration { ServiceType = typeof(TService), FactoryExpression = factory, ServiceName = serviceName, Lifetime = lifetime }; + Register(serviceRegistration); + } + + private class Storage + { + public T[] Items = new T[0]; + + private readonly object lockObject = new object(); + + public int Add(T value) + { + int index = Array.IndexOf(Items, value); + if (index == -1) + { + return TryAddValue(value); + } + + return index; + } + + public void Clear() + { + lock (lockObject) + { + Items = new T[0]; + } + } + + private int TryAddValue(T value) + { + lock (lockObject) + { + int index = Array.IndexOf(Items, value); + if (index == -1) + { + index = AddValue(value); + } + + return index; + } + } + + private int AddValue(T value) + { + int index = Items.Length; + T[] snapshot = CreateSnapshot(); + snapshot[index] = value; + Items = snapshot; + return index; + } + + private T[] CreateSnapshot() + { + var snapshot = new T[Items.Length + 1]; + Array.Copy(Items, snapshot, Items.Length); + return snapshot; + } + } + + private class DynamicMethodSkeleton : IMethodSkeleton + { + private IEmitter emitter; + private DynamicMethod dynamicMethod; + + public DynamicMethodSkeleton(Type returnType, Type[] parameterTypes) + { + CreateDynamicMethod(returnType, parameterTypes); + } + + public IEmitter GetEmitter() + { + return emitter; + } + + public Delegate CreateDelegate(Type delegateType) + { + return dynamicMethod.CreateDelegate(delegateType); + } + + private void CreateDynamicMethod(Type returnType, Type[] parameterTypes) + { + dynamicMethod = new DynamicMethod( + "DynamicMethod", returnType, parameterTypes, typeof(ServiceContainer).Module, true); + emitter = new Emitter(dynamicMethod.GetILGenerator(), parameterTypes); + } + } + + private class ServiceRegistry : ThreadSafeDictionary> + { + } + + private class FactoryRule + { + public Func CanCreateInstance { get; set; } + + public Func Factory { get; set; } + + public ILifetime LifeTime { get; set; } + } + + private class ServiceOverride + { + public Func CanOverride { get; set; } + + public Func ServiceRegistrationFactory { get; set; } + } + } + + /// + /// A that provides a per thread. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PerThreadScopeManagerProvider : IScopeManagerProvider + { + private readonly ThreadLocal scopeManagers = + new ThreadLocal(() => new ScopeManager()); + + /// + /// Returns the that is responsible for managing scopes. + /// + /// The that is responsible for managing scopes. + public ScopeManager GetScopeManager() + { + return scopeManagers.Value; + } + } + + /// + /// A that provides a per + /// . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PerLogicalCallContextScopeManagerProvider : IScopeManagerProvider + { + private readonly LogicalThreadStorage scopeManagers = + new LogicalThreadStorage(() => new ScopeManager()); + + /// + /// Returns the that is responsible for managing scopes. + /// + /// The that is responsible for managing scopes. + public ScopeManager GetScopeManager() + { + return scopeManagers.Value; + } + } + + /// + /// A thread safe dictionary. + /// + /// The type of the keys in the dictionary. + /// The type of the values in the dictionary. + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ThreadSafeDictionary : ConcurrentDictionary + { + /// + /// Initializes a new instance of the class. + /// + public ThreadSafeDictionary() + { + } + + /// + /// Initializes a new instance of the class using the + /// given . + /// + /// The implementation to use when comparing keys + public ThreadSafeDictionary(IEqualityComparer comparer) + : base(comparer) + { + } + } + + /// + /// Selects the from a given type that represents the most resolvable constructor. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class MostResolvableConstructorSelector : IConstructorSelector + { + private readonly Func canGetInstance; + + /// + /// Initializes a new instance of the class. + /// + /// A function delegate that determines if a service type can be resolved. + public MostResolvableConstructorSelector(Func canGetInstance) + { + this.canGetInstance = canGetInstance; + } + + /// + /// Selects the constructor to be used when creating a new instance of the . + /// + /// The for which to return a . + /// A instance that represents the constructor to be used + /// when creating a new instance of the . + public ConstructorInfo Execute(Type implementingType) + { + ConstructorInfo[] constructorCandidates = implementingType.GetConstructors(); + if (constructorCandidates.Length == 0) + { + throw new InvalidOperationException("Missing public constructor for Type: " + implementingType.FullName); + } + + if (constructorCandidates.Length == 1) + { + return constructorCandidates[0]; + } + + foreach (var constructorCandidate in constructorCandidates.OrderByDescending(c => c.GetParameters().Count())) + { + ParameterInfo[] parameters = constructorCandidate.GetParameters(); + if (CanCreateParameterDependencies(parameters)) + { + return constructorCandidate; + } + } + + throw new InvalidOperationException("No resolvable constructor found for Type: " + implementingType.FullName); + } + + /// + /// Gets the service name based on the given . + /// + /// The for which to get the service name. + /// The name of the service for the given . + protected virtual string GetServiceName(ParameterInfo parameter) + { + return parameter.Name; + } + + private bool CanCreateParameterDependencies(IEnumerable parameters) + { + return parameters.All(CanCreateParameterDependency); + } + + private bool CanCreateParameterDependency(ParameterInfo parameterInfo) + { + return canGetInstance(parameterInfo.ParameterType, string.Empty) || canGetInstance(parameterInfo.ParameterType, GetServiceName(parameterInfo)); + } + } + + /// + /// Selects the constructor dependencies for a given . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ConstructorDependencySelector : IConstructorDependencySelector + { + /// + /// Selects the constructor dependencies for the given . + /// + /// The for which to select the constructor dependencies. + /// A list of instances that represents the constructor + /// dependencies for the given . + public virtual IEnumerable Execute(ConstructorInfo constructor) + { + return + constructor.GetParameters() + .OrderBy(p => p.Position) + .Select( + p => + new ConstructorDependency + { + ServiceName = string.Empty, + ServiceType = p.ParameterType, + Parameter = p, + IsRequired = true + }); + } + } + + /// + /// Selects the property dependencies for a given . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PropertyDependencySelector : IPropertyDependencySelector + { + /// + /// Initializes a new instance of the class. + /// + /// The that is + /// responsible for selecting a list of injectable properties. + public PropertyDependencySelector(IPropertySelector propertySelector) + { + PropertySelector = propertySelector; + } + + /// + /// Gets the that is responsible for selecting a + /// list of injectable properties. + /// + protected IPropertySelector PropertySelector { get; private set; } + + /// + /// Selects the property dependencies for the given . + /// + /// The for which to select the property dependencies. + /// A list of instances that represents the property + /// dependencies for the given . + public virtual IEnumerable Execute(Type type) + { + return PropertySelector.Execute(type).Select( + p => new PropertyDependency { Property = p, ServiceName = string.Empty, ServiceType = p.PropertyType }); + } + } + + /// + /// Builds a instance based on the implementing . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class TypeConstructionInfoBuilder : ITypeConstructionInfoBuilder + { + private readonly IConstructorSelector constructorSelector; + private readonly IConstructorDependencySelector constructorDependencySelector; + private readonly IPropertyDependencySelector propertyDependencySelector; + + /// + /// Initializes a new instance of the class. + /// + /// The that is responsible + /// for selecting the constructor to be used for constructor injection. + /// The that is + /// responsible for selecting the constructor dependencies for a given . + /// The that is responsible + /// for selecting the property dependencies for a given . + public TypeConstructionInfoBuilder( + IConstructorSelector constructorSelector, + IConstructorDependencySelector constructorDependencySelector, + IPropertyDependencySelector propertyDependencySelector) + { + this.constructorSelector = constructorSelector; + this.constructorDependencySelector = constructorDependencySelector; + this.propertyDependencySelector = propertyDependencySelector; + } + + /// + /// Analyzes the and returns a instance. + /// + /// The that represents the implementing type to analyze. + /// A instance. + public ConstructionInfo Execute(Registration registration) + { + var implementingType = registration.ImplementingType; + var constructionInfo = new ConstructionInfo(); + constructionInfo.ImplementingType = implementingType; + constructionInfo.PropertyDependencies.AddRange(propertyDependencySelector.Execute(implementingType)); + if (!registration.IgnoreConstructorDependencies) + { + constructionInfo.Constructor = constructorSelector.Execute(implementingType); + constructionInfo.ConstructorDependencies.AddRange(constructorDependencySelector.Execute(constructionInfo.Constructor)); + } + + return constructionInfo; + } + } + + /// + /// Keeps track of a instance for each . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ConstructionInfoProvider : IConstructionInfoProvider + { + private readonly IConstructionInfoBuilder constructionInfoBuilder; + private readonly ThreadSafeDictionary cache = new ThreadSafeDictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The that + /// is responsible for building a instance based on a given . + public ConstructionInfoProvider(IConstructionInfoBuilder constructionInfoBuilder) + { + this.constructionInfoBuilder = constructionInfoBuilder; + } + + /// + /// Gets a instance for the given . + /// + /// The for which to get a instance. + /// The instance that describes how to create an instance of the given . + public ConstructionInfo GetConstructionInfo(Registration registration) + { + return cache.GetOrAdd(registration, constructionInfoBuilder.Execute); + } + + /// + /// Invalidates the and causes new instances + /// to be created when the method is called. + /// + public void Invalidate() + { + cache.Clear(); + } + } + + /// + /// Provides a instance + /// that describes how to create a service instance. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ConstructionInfoBuilder : IConstructionInfoBuilder + { + private readonly Lazy lambdaConstructionInfoBuilder; + private readonly Lazy typeConstructionInfoBuilder; + + /// + /// Initializes a new instance of the class. + /// + /// + /// A function delegate used to provide a instance. + /// + /// + /// A function delegate used to provide a instance. + /// + public ConstructionInfoBuilder( + Func lambdaConstructionInfoBuilderFactory, + Func typeConstructionInfoBuilderFactory) + { + typeConstructionInfoBuilder = new Lazy(typeConstructionInfoBuilderFactory); + lambdaConstructionInfoBuilder = new Lazy(lambdaConstructionInfoBuilderFactory); + } + + /// + /// Returns a instance based on the given . + /// + /// The for which to return a instance. + /// A instance that describes how to create a service instance. + public ConstructionInfo Execute(Registration registration) + { + return registration.FactoryExpression != null + ? CreateConstructionInfoFromLambdaExpression(registration.FactoryExpression) + : CreateConstructionInfoFromImplementingType(registration); + } + + private ConstructionInfo CreateConstructionInfoFromLambdaExpression(LambdaExpression lambdaExpression) + { + return lambdaConstructionInfoBuilder.Value.Execute(lambdaExpression); + } + + private ConstructionInfo CreateConstructionInfoFromImplementingType(Registration registration) + { + return typeConstructionInfoBuilder.Value.Execute(registration); + } + } + + /// + /// Parses a into a instance. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class LambdaConstructionInfoBuilder : ILambdaConstructionInfoBuilder + { + /// + /// Parses the and returns a instance. + /// + /// The to parse. + /// A instance. + public ConstructionInfo Execute(LambdaExpression lambdaExpression) + { + if (!CanParse(lambdaExpression)) + { + return CreateConstructionInfoBasedOnLambdaExpression(lambdaExpression); + } + + switch (lambdaExpression.Body.NodeType) + { + case ExpressionType.New: + return CreateConstructionInfoBasedOnNewExpression((NewExpression)lambdaExpression.Body); + case ExpressionType.MemberInit: + return CreateConstructionInfoBasedOnHandleMemberInitExpression((MemberInitExpression)lambdaExpression.Body); + default: + return CreateConstructionInfoBasedOnLambdaExpression(lambdaExpression); + } + } + + private bool CanParse(LambdaExpression lambdaExpression) + { + return lambdaExpression.Parameters.Count <= 1; + } + + private static ConstructionInfo CreateConstructionInfoBasedOnLambdaExpression(LambdaExpression lambdaExpression) + { + return new ConstructionInfo { FactoryDelegate = lambdaExpression.Compile() }; + } + + private static ConstructionInfo CreateConstructionInfoBasedOnNewExpression(NewExpression newExpression) + { + var constructionInfo = CreateConstructionInfo(newExpression); + ParameterInfo[] parameters = newExpression.Constructor.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + ConstructorDependency constructorDependency = CreateConstructorDependency(parameters[i]); + ApplyDependencyDetails(newExpression.Arguments[i], constructorDependency); + constructionInfo.ConstructorDependencies.Add(constructorDependency); + } + + return constructionInfo; + } + + private static ConstructionInfo CreateConstructionInfo(NewExpression newExpression) + { + var constructionInfo = new ConstructionInfo { Constructor = newExpression.Constructor, ImplementingType = newExpression.Constructor.DeclaringType }; + return constructionInfo; + } + + private static ConstructionInfo CreateConstructionInfoBasedOnHandleMemberInitExpression(MemberInitExpression memberInitExpression) + { + var constructionInfo = CreateConstructionInfoBasedOnNewExpression(memberInitExpression.NewExpression); + foreach (MemberBinding memberBinding in memberInitExpression.Bindings) + { + HandleMemberAssignment((MemberAssignment)memberBinding, constructionInfo); + } + + return constructionInfo; + } + + private static void HandleMemberAssignment(MemberAssignment memberAssignment, ConstructionInfo constructionInfo) + { + var propertyDependency = CreatePropertyDependency(memberAssignment); + ApplyDependencyDetails(memberAssignment.Expression, propertyDependency); + constructionInfo.PropertyDependencies.Add(propertyDependency); + } + + private static ConstructorDependency CreateConstructorDependency(ParameterInfo parameterInfo) + { + var constructorDependency = new ConstructorDependency + { + Parameter = parameterInfo, + ServiceType = parameterInfo.ParameterType, + IsRequired = true + }; + return constructorDependency; + } + + private static PropertyDependency CreatePropertyDependency(MemberAssignment memberAssignment) + { + var propertyDependecy = new PropertyDependency + { + Property = (PropertyInfo)memberAssignment.Member, + ServiceType = ((PropertyInfo)memberAssignment.Member).PropertyType + }; + return propertyDependecy; + } + + private static void ApplyDependencyDetails(Expression expression, Dependency dependency) + { + if (RepresentsServiceFactoryMethod(expression)) + { + ApplyDependencyDetailsFromMethodCall((MethodCallExpression)expression, dependency); + } + else + { + ApplyDependecyDetailsFromExpression(expression, dependency); + } + } + + private static bool RepresentsServiceFactoryMethod(Expression expression) + { + return IsMethodCall(expression) && + IsServiceFactoryMethod(((MethodCallExpression)expression).Method); + } + + private static bool IsMethodCall(Expression expression) + { + return expression.NodeType == ExpressionType.Call; + } + + private static bool IsServiceFactoryMethod(MethodInfo methodInfo) + { + return methodInfo.DeclaringType == typeof(IServiceFactory); + } + + private static void ApplyDependecyDetailsFromExpression(Expression expression, Dependency dependency) + { + dependency.FactoryExpression = expression; + dependency.ServiceName = string.Empty; + } + + private static void ApplyDependencyDetailsFromMethodCall(MethodCallExpression methodCallExpression, Dependency dependency) + { + dependency.ServiceType = methodCallExpression.Method.ReturnType; + if (RepresentsGetNamedInstanceMethod(methodCallExpression)) + { + dependency.ServiceName = (string)((ConstantExpression)methodCallExpression.Arguments[0]).Value; + } + else + { + dependency.ServiceName = string.Empty; + } + } + + private static bool RepresentsGetNamedInstanceMethod(MethodCallExpression node) + { + return IsGetInstanceMethod(node.Method) && HasOneArgumentRepresentingServiceName(node); + } + + private static bool IsGetInstanceMethod(MethodInfo methodInfo) + { + return methodInfo.Name == "GetInstance"; + } + + private static bool HasOneArgumentRepresentingServiceName(MethodCallExpression node) + { + return HasOneArgument(node) && IsConstantExpression(node.Arguments[0]); + } + + private static bool HasOneArgument(MethodCallExpression node) + { + return node.Arguments.Count == 1; + } + + private static bool IsConstantExpression(Expression argument) + { + return argument.NodeType == ExpressionType.Constant; + } + } + + /// + /// Contains information about a service request that originates from a rule based service registration. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ServiceRequest + { + /// + /// Initializes a new instance of the class. + /// + /// The of the requested service. + /// The name of the requested service. + /// The to be associated with this . + public ServiceRequest(Type serviceType, string serviceName, IServiceFactory serviceFactory) + { + ServiceType = serviceType; + ServiceName = serviceName; + ServiceFactory = serviceFactory; + } + + /// + /// Gets the service type. + /// + public Type ServiceType { get; private set; } + + /// + /// Gets the service name. + /// + public string ServiceName { get; private set; } + + /// + /// Gets the that is associated with this . + /// + public IServiceFactory ServiceFactory { get; private set; } + } + + /// + /// Base class for concrete registrations within the service container. + /// + internal abstract class Registration + { + /// + /// Gets or sets the service . + /// + public Type ServiceType { get; set; } + + /// + /// Gets or sets a value indicating whether constructor dependencies should be ignored. + /// + public bool IgnoreConstructorDependencies { get; set; } + + /// + /// Gets or sets the that implements the . + /// + public virtual Type ImplementingType { get; set; } + + /// + /// Gets or sets the used to create a service instance. + /// + public LambdaExpression FactoryExpression { get; set; } + } + + /// + /// Contains information about a registered decorator. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class DecoratorRegistration : Registration + { + /// + /// Gets or sets a function delegate that determines if the decorator can decorate the service + /// represented by the supplied . + /// + public Func CanDecorate { get; set; } + + /// + /// Gets or sets a that defers resolving of the decorators implementing type. + /// + public Func ImplementingTypeFactory { get; set; } + + /// + /// Gets or sets the index of this . + /// + public int Index { get; set; } + + /// + /// Gets a value indicating whether this registration has a deferred implementing type. + /// + public bool HasDeferredImplementingType + { + get + { + return ImplementingType == null && FactoryExpression == null; + } + } + } + + /// + /// Contains information about a registered service. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ServiceRegistration : Registration + { + /// + /// Gets or sets the name of the service. + /// + public string ServiceName { get; set; } + + /// + /// Gets or sets the instance that controls the lifetime of the service. + /// + public ILifetime Lifetime { get; set; } + + /// + /// Gets or sets the value that represents the instance of the service. + /// + public object Value { get; set; } + + /// + /// Gets or sets a value indicating whether this can be overridden + /// by another registration. + /// + public bool IsReadOnly { get; set; } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + return ServiceType.GetHashCode() ^ ServiceName.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// True if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . 2 + public override bool Equals(object obj) + { + var other = obj as ServiceRegistration; + if (other == null) + { + return false; + } + + var result = ServiceName == other.ServiceName && ServiceType == other.ServiceType; + return result; + } + } + + /// + /// Contains information about how to create a service instance. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ConstructionInfo + { + /// + /// Initializes a new instance of the class. + /// + public ConstructionInfo() + { + PropertyDependencies = new List(); + ConstructorDependencies = new List(); + } + + /// + /// Gets or sets the implementing type that represents the concrete class to create. + /// + public Type ImplementingType { get; set; } + + /// + /// Gets or sets the that is used to create a service instance. + /// + public ConstructorInfo Constructor { get; set; } + + /// + /// Gets a list of instances that represent + /// the property dependencies for the target service instance. + /// + public List PropertyDependencies { get; private set; } + + /// + /// Gets a list of instances that represent + /// the property dependencies for the target service instance. + /// + public List ConstructorDependencies { get; private set; } + + /// + /// Gets or sets the function delegate to be used to create the service instance. + /// + public Delegate FactoryDelegate { get; set; } + } + + /// + /// Represents a class dependency. + /// + internal abstract class Dependency + { + /// + /// Gets or sets the service of the . + /// + public Type ServiceType { get; set; } + + /// + /// Gets or sets the service name of the . + /// + public string ServiceName { get; set; } + + /// + /// Gets or sets the that represent getting the value of the . + /// + public Expression FactoryExpression { get; set; } + + /// + /// Gets the name of the dependency accessor. + /// + public abstract string Name { get; } + + /// + /// Gets or sets a value indicating whether this dependency is required. + /// + public bool IsRequired { get; set; } + + /// + /// Returns textual information about the dependency. + /// + /// A string that describes the dependency. + public override string ToString() + { + var sb = new StringBuilder(); + return sb.AppendFormat("[Requested dependency: ServiceType:{0}, ServiceName:{1}]", ServiceType, ServiceName).ToString(); + } + } + + /// + /// Represents a property dependency. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PropertyDependency : Dependency + { + /// + /// Gets or sets the that is used to set the property value. + /// + public PropertyInfo Property { get; set; } + + /// + /// Gets the name of the dependency accessor. + /// + public override string Name + { + get + { + return Property.Name; + } + } + + /// + /// Returns textual information about the dependency. + /// + /// A string that describes the dependency. + public override string ToString() + { + return string.Format("[Target Type: {0}], [Property: {1}({2})]", Property.DeclaringType, Property.Name, Property.PropertyType) + ", " + base.ToString(); + } + } + + /// + /// Represents a constructor dependency. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ConstructorDependency : Dependency + { + /// + /// Gets or sets the for this . + /// + public ParameterInfo Parameter { get; set; } + + /// + /// Gets or sets a value indicating whether that this parameter represents + /// the decoration target passed into a decorator instance. + /// + public bool IsDecoratorTarget { get; set; } + + /// + /// Gets the name of the dependency accessor. + /// + public override string Name + { + get + { + return Parameter.Name; + } + } + + /// + /// Returns textual information about the dependency. + /// + /// A string that describes the dependency. + public override string ToString() + { + return string.Format("[Target Type: {0}], [Parameter: {1}({2})]", Parameter.Member.DeclaringType, Parameter.Name, Parameter.ParameterType) + ", " + base.ToString(); + } + } + + /// + /// Ensures that only one instance of a given service can exist within the current . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PerContainerLifetime : ILifetime, IDisposable + { + private readonly object syncRoot = new object(); + private volatile object singleton; + + /// + /// Returns a service instance according to the specific lifetime characteristics. + /// + /// The function delegate used to create a new service instance. + /// The of the current service request. + /// The requested services instance. + public object GetInstance(Func createInstance, Scope scope) + { + if (singleton != null) + { + return singleton; + } + + lock (syncRoot) + { + if (singleton == null) + { + singleton = createInstance(); + } + } + + return singleton; + } + + /// + /// Disposes the service instances managed by this instance. + /// + public void Dispose() + { + var disposable = singleton as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + } + } + + /// + /// Ensures that a new instance is created for each request in addition to tracking disposable instances. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PerRequestLifeTime : ILifetime + { + /// + /// Returns a service instance according to the specific lifetime characteristics. + /// + /// The function delegate used to create a new service instance. + /// The of the current service request. + /// The requested services instance. + public object GetInstance(Func createInstance, Scope scope) + { + var instance = createInstance(); + var disposable = instance as IDisposable; + if (disposable != null) + { + TrackInstance(scope, disposable); + } + + return instance; + } + + private static void TrackInstance(Scope scope, IDisposable disposable) + { + if (scope == null) + { + throw new InvalidOperationException("Attempt to create a disposable instance without a current scope."); + } + + scope.TrackInstance(disposable); + } + } + + /// + /// Ensures that only one service instance can exist within a given . + /// + /// + /// If the service instance implements , + /// it will be disposed when the ends. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PerScopeLifetime : ILifetime + { + private readonly ThreadSafeDictionary instances = new ThreadSafeDictionary(); + + /// + /// Returns the same service instance within the current . + /// + /// The function delegate used to create a new service instance. + /// The of the current service request. + /// The requested services instance. + public object GetInstance(Func createInstance, Scope scope) + { + if (scope == null) + { + throw new InvalidOperationException( + "Attempt to create a scoped instance without a current scope."); + } + + return instances.GetOrAdd(scope, s => CreateScopedInstance(s, createInstance)); + } + + private static void RegisterForDisposal(Scope scope, object instance) + { + var disposable = instance as IDisposable; + if (disposable != null) + { + scope.TrackInstance(disposable); + } + } + + private object CreateScopedInstance(Scope scope, Func createInstance) + { + scope.Completed += OnScopeCompleted; + var instance = createInstance(); + + RegisterForDisposal(scope, instance); + return instance; + } + + private void OnScopeCompleted(object sender, EventArgs e) + { + var scope = (Scope)sender; + scope.Completed -= OnScopeCompleted; + object removedInstance; + instances.TryRemove(scope, out removedInstance); + } + } + + /// + /// Manages a set of instances. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ScopeManager + { + private readonly object syncRoot = new object(); + + private Scope currentScope; + + /// + /// Gets the current . + /// + public Scope CurrentScope + { + get + { + lock (syncRoot) + { + return currentScope; + } + } + } + + /// + /// Starts a new . + /// + /// A new . + public Scope BeginScope() + { + lock (syncRoot) + { + var scope = new Scope(this, currentScope); + if (currentScope != null) + { + currentScope.ChildScope = scope; + } + + currentScope = scope; + return scope; + } + } + + /// + /// Ends the given and updates the property. + /// + /// The scope that is completed. + public void EndScope(Scope scope) + { + lock (syncRoot) + { + if (scope.ChildScope != null) + { + throw new InvalidOperationException("Attempt to end a scope before all child scopes are completed."); + } + + currentScope = scope.ParentScope; + if (currentScope != null) + { + currentScope.ChildScope = null; + } + } + } + } + + /// + /// Represents a scope. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class Scope : IDisposable + { + private readonly IList disposableObjects = new List(); + + private readonly ScopeManager scopeManager; + + /// + /// Initializes a new instance of the class. + /// + /// The that manages this . + /// The parent . + public Scope(ScopeManager scopeManager, Scope parentScope) + { + this.scopeManager = scopeManager; + ParentScope = parentScope; + } + + /// + /// Raised when the is completed. + /// + public event EventHandler Completed; + + /// + /// Gets or sets the parent . + /// + public Scope ParentScope { get; internal set; } + + /// + /// Gets or sets the child . + /// + public Scope ChildScope { get; internal set; } + + /// + /// Registers the so that it is disposed when the scope is completed. + /// + /// The object to register. + public void TrackInstance(IDisposable disposable) + { + disposableObjects.Add(disposable); + } + + /// + /// Disposes all instances tracked by this scope. + /// + public void Dispose() + { + DisposeTrackedInstances(); + OnCompleted(); + } + + private void DisposeTrackedInstances() + { + foreach (var disposableObject in disposableObjects) + { + disposableObject.Dispose(); + } + } + + private void OnCompleted() + { + scopeManager.EndScope(this); + var completedHandler = Completed; + if (completedHandler != null) + { + completedHandler(this, new EventArgs()); + } + } + } + + /// + /// Used at the assembly level to describe the composition root(s) for the target assembly. + /// + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class CompositionRootTypeAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// A that implements the interface. + public CompositionRootTypeAttribute(Type compositionRootType) + { + CompositionRootType = compositionRootType; + } + + /// + /// Gets the that implements the interface. + /// + public Type CompositionRootType { get; private set; } + } + + /// + /// Extracts concrete implementations from an . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class CompositionRootTypeExtractor : ITypeExtractor + { + /// + /// Extracts concrete implementations found in the given . + /// + /// The for which to extract types. + /// A set of concrete implementations found in the given . + public Type[] Execute(Assembly assembly) + { + CompositionRootTypeAttribute[] compositionRootAttributes = + assembly.GetCustomAttributes(typeof(CompositionRootTypeAttribute)) + .Cast().ToArray(); + + if (compositionRootAttributes.Length > 0) + { + return compositionRootAttributes.Select(a => a.CompositionRootType).ToArray(); + } + + return assembly.GetTypes().Where(t => !t.IsAbstract() && typeof(ICompositionRoot).IsAssignableFrom(t)).ToArray(); + } + } + + /// + /// A cache decorator. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class CachedTypeExtractor : ITypeExtractor + { + private readonly ITypeExtractor typeExtractor; + + private readonly ThreadSafeDictionary cache = + new ThreadSafeDictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The target . + public CachedTypeExtractor(ITypeExtractor typeExtractor) + { + this.typeExtractor = typeExtractor; + } + + /// + /// Extracts types found in the given . + /// + /// The for which to extract types. + /// A set of types found in the given . + public Type[] Execute(Assembly assembly) + { + return cache.GetOrAdd(assembly, typeExtractor.Execute); + } + } + + /// + /// Extracts concrete types from an . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class ConcreteTypeExtractor : ITypeExtractor + { + private static readonly List InternalTypes = new List(); + + static ConcreteTypeExtractor() + { + InternalTypes.Add(typeof(LambdaConstructionInfoBuilder)); + InternalTypes.Add(typeof(ConstructorDependency)); + InternalTypes.Add(typeof(PropertyDependency)); + InternalTypes.Add(typeof(ThreadSafeDictionary<,>)); + InternalTypes.Add(typeof(Scope)); + InternalTypes.Add(typeof(PerContainerLifetime)); + InternalTypes.Add(typeof(PerScopeLifetime)); + InternalTypes.Add(typeof(ScopeManager)); + InternalTypes.Add(typeof(ServiceRegistration)); + InternalTypes.Add(typeof(DecoratorRegistration)); + InternalTypes.Add(typeof(ServiceRequest)); + InternalTypes.Add(typeof(Registration)); + InternalTypes.Add(typeof(ServiceContainer)); + InternalTypes.Add(typeof(ConstructionInfo)); + InternalTypes.Add(typeof(AssemblyLoader)); + InternalTypes.Add(typeof(TypeConstructionInfoBuilder)); + InternalTypes.Add(typeof(ConstructionInfoProvider)); + InternalTypes.Add(typeof(ConstructionInfoBuilder)); + InternalTypes.Add(typeof(MostResolvableConstructorSelector)); + InternalTypes.Add(typeof(PerContainerLifetime)); + InternalTypes.Add(typeof(PerContainerLifetime)); + InternalTypes.Add(typeof(PerRequestLifeTime)); + InternalTypes.Add(typeof(PropertySelector)); + InternalTypes.Add(typeof(AssemblyScanner)); + InternalTypes.Add(typeof(ConstructorDependencySelector)); + InternalTypes.Add(typeof(PropertyDependencySelector)); + InternalTypes.Add(typeof(CompositionRootTypeAttribute)); + InternalTypes.Add(typeof(ConcreteTypeExtractor)); + InternalTypes.Add(typeof(CompositionRootExecutor)); + InternalTypes.Add(typeof(CompositionRootTypeExtractor)); + InternalTypes.Add(typeof(CachedTypeExtractor)); + InternalTypes.Add(typeof(ImmutableList<>)); + InternalTypes.Add(typeof(KeyValue<,>)); + InternalTypes.Add(typeof(ImmutableHashTree<,>)); + InternalTypes.Add(typeof(PerThreadScopeManagerProvider)); + InternalTypes.Add(typeof(Emitter)); + InternalTypes.Add(typeof(Instruction)); + InternalTypes.Add(typeof(Instruction<>)); + InternalTypes.Add(typeof(PerLogicalCallContextScopeManagerProvider)); + InternalTypes.Add(typeof(LogicalThreadStorage<>)); + } + + /// + /// Extracts concrete types found in the given . + /// + /// The for which to extract types. + /// A set of concrete types found in the given . + public Type[] Execute(Assembly assembly) + { + return assembly.GetTypes().Where(t => t.IsClass() + && !t.IsNestedPrivate() + && !t.IsAbstract() + && !Equals(t.GetAssembly(), typeof(string).GetAssembly()) + && !IsCompilerGenerated(t)).Except(InternalTypes).ToArray(); + } + + private static bool IsCompilerGenerated(Type type) + { + return type.IsDefined(typeof(CompilerGeneratedAttribute), false); + } + } + + /// + /// A class that is responsible for instantiating and executing an . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class CompositionRootExecutor : ICompositionRootExecutor + { + private readonly IServiceRegistry serviceRegistry; + + private readonly IList executedCompositionRoots = new List(); + + private readonly object syncRoot = new object(); + + /// + /// Initializes a new instance of the class. + /// + /// The to be configured by the . + public CompositionRootExecutor(IServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + /// + /// Creates an instance of the and executes the method. + /// + /// The concrete type to be instantiated and executed. + public void Execute(Type compositionRootType) + { + if (!executedCompositionRoots.Contains(compositionRootType)) + { + lock (syncRoot) + { + if (!executedCompositionRoots.Contains(compositionRootType)) + { + executedCompositionRoots.Add(compositionRootType); + var compositionRoot = (ICompositionRoot)Activator.CreateInstance(compositionRootType); + compositionRoot.Compose(serviceRegistry); + } + } + } + } + } + + /// + /// An assembly scanner that registers services based on the types contained within an . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class AssemblyScanner : IAssemblyScanner + { + private readonly ITypeExtractor concreteTypeExtractor; + private readonly ITypeExtractor compositionRootTypeExtractor; + private readonly ICompositionRootExecutor compositionRootExecutor; + private Assembly currentAssembly; + + /// + /// Initializes a new instance of the class. + /// + /// The that is responsible for + /// extracting concrete types from the assembly being scanned. + /// The that is responsible for + /// extracting implementations from the assembly being scanned. + /// The that is + /// responsible for creating and executing an . + public AssemblyScanner(ITypeExtractor concreteTypeExtractor, ITypeExtractor compositionRootTypeExtractor, ICompositionRootExecutor compositionRootExecutor) + { + this.concreteTypeExtractor = concreteTypeExtractor; + this.compositionRootTypeExtractor = compositionRootTypeExtractor; + this.compositionRootExecutor = compositionRootExecutor; + } + + /// + /// Scans the target and registers services found within the assembly. + /// + /// The to scan. + /// The target instance. + /// The factory that controls the lifetime of the registered service. + /// A function delegate that determines if a service implementation should be registered. + public void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func lifetimeFactory, Func shouldRegister) + { + Type[] concreteTypes = GetConcreteTypes(assembly); + foreach (Type type in concreteTypes) + { + BuildImplementationMap(type, serviceRegistry, lifetimeFactory, shouldRegister); + } + } + + /// + /// Scans the target and executes composition roots found within the . + /// + /// The to scan. + /// The target instance. + public void Scan(Assembly assembly, IServiceRegistry serviceRegistry) + { + Type[] compositionRootTypes = GetCompositionRootTypes(assembly); + if (compositionRootTypes.Length > 0 && !Equals(currentAssembly, assembly)) + { + currentAssembly = assembly; + ExecuteCompositionRoots(compositionRootTypes); + } + } + + private static string GetServiceName(Type serviceType, Type implementingType) + { + string implementingTypeName = implementingType.Name; + string serviceTypeName = serviceType.Name; + if (implementingType.IsGenericTypeDefinition()) + { + var regex = new Regex("((?:[a-z][a-z]+))", RegexOptions.IgnoreCase); + implementingTypeName = regex.Match(implementingTypeName).Groups[1].Value; + serviceTypeName = regex.Match(serviceTypeName).Groups[1].Value; + } + + if (serviceTypeName.Substring(1) == implementingTypeName) + { + implementingTypeName = string.Empty; + } + + return implementingTypeName; + } + + private static IEnumerable GetBaseTypes(Type concreteType) + { + Type baseType = concreteType; + while (baseType != typeof(object) && baseType != null) + { + yield return baseType; + baseType = baseType.GetBaseType(); + } + } + + private void ExecuteCompositionRoots(IEnumerable compositionRoots) + { + foreach (var compositionRoot in compositionRoots) + { + compositionRootExecutor.Execute(compositionRoot); + } + } + + private Type[] GetConcreteTypes(Assembly assembly) + { + return concreteTypeExtractor.Execute(assembly); + } + + private Type[] GetCompositionRootTypes(Assembly assembly) + { + return compositionRootTypeExtractor.Execute(assembly); + } + + private void BuildImplementationMap(Type implementingType, IServiceRegistry serviceRegistry, Func lifetimeFactory, Func shouldRegister) + { + Type[] interfaces = implementingType.GetInterfaces(); + foreach (Type interfaceType in interfaces) + { + if (shouldRegister(interfaceType, implementingType)) + { + RegisterInternal(interfaceType, implementingType, serviceRegistry, lifetimeFactory()); + } + } + + foreach (Type baseType in GetBaseTypes(implementingType)) + { + if (shouldRegister(baseType, implementingType)) + { + RegisterInternal(baseType, implementingType, serviceRegistry, lifetimeFactory()); + } + } + } + + private void RegisterInternal(Type serviceType, Type implementingType, IServiceRegistry serviceRegistry, ILifetime lifetime) + { + if (serviceType.IsGenericType() && serviceType.ContainsGenericParameters()) + { + serviceType = serviceType.GetGenericTypeDefinition(); + } + + serviceRegistry.Register(serviceType, implementingType, GetServiceName(serviceType, implementingType), lifetime); + } + } + + /// + /// Selects the properties that represents a dependency to the target . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PropertySelector : IPropertySelector + { + /// + /// Selects properties that represents a dependency from the given . + /// + /// The for which to select the properties. + /// A list of properties that represents a dependency to the target + public IEnumerable Execute(Type type) + { + return type.GetProperties().Where(IsInjectable).ToList(); + } + + /// + /// Determines if the represents an injectable property. + /// + /// The that describes the target property. + /// true if the property is injectable, otherwise false. + protected virtual bool IsInjectable(PropertyInfo propertyInfo) + { + return !IsReadOnly(propertyInfo); + } + + private static bool IsReadOnly(PropertyInfo propertyInfo) + { + return propertyInfo.GetSetMethod() == null || propertyInfo.GetSetMethod().IsStatic || propertyInfo.GetSetMethod().IsPrivate || propertyInfo.GetIndexParameters().Length > 0; + } + } + + /// + /// Loads all assemblies from the application base directory that matches the given search pattern. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class AssemblyLoader : IAssemblyLoader + { + /// + /// Loads a set of assemblies based on the given . + /// + /// The search pattern to use. + /// A list of assemblies based on the given . + public IEnumerable Load(string searchPattern) + { + string directory = Path.GetDirectoryName(new Uri(typeof(ServiceContainer).Assembly.CodeBase).LocalPath); + if (directory != null) + { + string[] searchPatterns = searchPattern.Split('|'); + foreach (string file in searchPatterns.SelectMany(sp => Directory.GetFiles(directory, sp)).Where(CanLoad)) + { + yield return Assembly.LoadFrom(file); + } + } + } + + /// + /// Indicates if the current represent a file that can be loaded. + /// + /// The name of the target file. + /// true if the file can be loaded, otherwise false. + protected virtual bool CanLoad(string fileName) + { + return true; + } + } + + /// + /// Defines an immutable representation of a key and a value. + /// + /// The type of the key. + /// The type of the value. + internal sealed class KeyValue + { + /// + /// The key of this instance. + /// + public readonly TKey Key; + + /// + /// The key of this instance. + /// + public readonly TValue Value; + + /// + /// Initializes a new instance of the class. + /// + /// The key of this instance. + /// The value of this instance. + public KeyValue(TKey key, TValue value) + { + Key = key; + Value = value; + } + } + + /// + /// Represents a simple "add only" immutable list. + /// + /// The type of items contained in the list. + internal sealed class ImmutableList + { + /// + /// Represents an empty . + /// + public static readonly ImmutableList Empty = new ImmutableList(); + + /// + /// An array that contains the items in the . + /// + public readonly T[] Items; + + /// + /// The number of items in the . + /// + public readonly int Count; + + /// + /// Initializes a new instance of the class. + /// + /// The list from which the previous items are copied. + /// The value to be added to the list. + public ImmutableList(ImmutableList previousList, T value) + { + Items = new T[previousList.Items.Length + 1]; + Array.Copy(previousList.Items, Items, previousList.Items.Length); + Items[Items.Length - 1] = value; + Count = Items.Length; + } + + private ImmutableList() + { + Items = new T[0]; + } + + /// + /// Creates a new that contains the new . + /// + /// The value to be added to the new list. + /// A new that contains the new . + public ImmutableList Add(T value) + { + return new ImmutableList(this, value); + } + } + + /// + /// A balanced binary search tree implemented as an AVL tree. + /// + /// The type of the key. + /// The type of the value. + internal sealed class ImmutableHashTree + { + /// + /// An empty . + /// + public static readonly ImmutableHashTree Empty = new ImmutableHashTree(); + + /// + /// The key of this . + /// + public readonly TKey Key; + + /// + /// The value of this . + /// + public readonly TValue Value; + + /// + /// The list of instances where the + /// has the same hash code as this . + /// + public readonly ImmutableList> Duplicates; + + /// + /// The hash code retrieved from the . + /// + public readonly int HashCode; + + /// + /// The left node of this . + /// + public readonly ImmutableHashTree Left; + + /// + /// The right node of this . + /// + public readonly ImmutableHashTree Right; + + /// + /// The height of this node. + /// + /// + /// An empty node has a height of 0 and a node without children has a height of 1. + /// + public readonly int Height; + + /// + /// Indicates that this is empty. + /// + public readonly bool IsEmpty; + + /// + /// Initializes a new instance of the class + /// and adds a new entry in the list. + /// + /// The key for this node. + /// The value for this node. + /// The that contains existing duplicates. + public ImmutableHashTree(TKey key, TValue value, ImmutableHashTree hashTree) + { + Duplicates = hashTree.Duplicates.Add(new KeyValue(key, value)); + Key = hashTree.Key; + Value = hashTree.Value; + Height = hashTree.Height; + HashCode = hashTree.HashCode; + Left = hashTree.Left; + Right = hashTree.Right; + } + + /// + /// Initializes a new instance of the class. + /// + /// The key for this node. + /// The value for this node. + /// The left node. + /// The right node. + public ImmutableHashTree(TKey key, TValue value, ImmutableHashTree left, ImmutableHashTree right) + { + var balance = left.Height - right.Height; + + if (balance == -2) + { + if (right.IsLeftHeavy()) + { + right = RotateRight(right); + } + + // Rotate left + Key = right.Key; + Value = right.Value; + Left = new ImmutableHashTree(key, value, left, right.Left); + Right = right.Right; + } + else if (balance == 2) + { + if (left.IsRightHeavy()) + { + left = RotateLeft(left); + } + + // Rotate right + Key = left.Key; + Value = left.Value; + Right = new ImmutableHashTree(key, value, left.Right, right); + Left = left.Left; + } + else + { + Key = key; + Value = value; + Left = left; + Right = right; + } + + Height = 1 + Math.Max(Left.Height, Right.Height); + + Duplicates = ImmutableList>.Empty; + + HashCode = Key.GetHashCode(); + } + + private ImmutableHashTree() + { + IsEmpty = true; + Duplicates = ImmutableList>.Empty; + } + + private static ImmutableHashTree RotateLeft(ImmutableHashTree left) + { + return new ImmutableHashTree( + left.Right.Key, + left.Right.Value, + new ImmutableHashTree(left.Key, left.Value, left.Right.Left, left.Left), + left.Right.Right); + } + + private static ImmutableHashTree RotateRight(ImmutableHashTree right) + { + return new ImmutableHashTree( + right.Left.Key, + right.Left.Value, + right.Left.Left, + new ImmutableHashTree(right.Key, right.Value, right.Left.Right, right.Right)); + } + + private bool IsLeftHeavy() + { + return Left.Height > Right.Height; + } + + private bool IsRightHeavy() + { + return Right.Height > Left.Height; + } + } + + /// + /// Represents an MSIL instruction to be emitted into a dynamic method. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class Instruction + { + /// + /// Initializes a new instance of the class. + /// + /// The to be emitted. + /// The action to be performed against an + /// when this is emitted. + public Instruction(OpCode code, Action emitAction) + { + Code = code; + Emit = emitAction; + } + + /// + /// Gets the to be emitted. + /// + public OpCode Code { get; private set; } + + /// + /// Gets the action to be performed against an + /// when this is emitted. + /// + public Action Emit { get; private set; } + + /// + /// Returns the string representation of an . + /// + /// The string representation of an . + public override string ToString() + { + return Code.ToString(); + } + } + + /// + /// Represents an MSIL instruction to be emitted into a dynamic method. + /// + /// The type of argument used in this instruction. + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class Instruction : Instruction + { + /// + /// Initializes a new instance of the class. + /// + /// The to be emitted. + /// The argument be passed along with the given . + /// The action to be performed against an + /// when this is emitted. + public Instruction(OpCode code, T argument, Action emitAction) + : base(code, emitAction) + { + Argument = argument; + } + + /// + /// Gets the argument be passed along with the given . + /// + public T Argument { get; private set; } + + /// + /// Returns the string representation of an . + /// + /// The string representation of an . + public override string ToString() + { + return base.ToString() + " " + Argument; + } + } + + /// + /// An abstraction of the class that provides information + /// about the currently on the stack. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class Emitter : IEmitter + { + private readonly ILGenerator generator; + + private readonly Type[] parameterTypes; + + private readonly Stack stack = new Stack(); + + private readonly List variables = new List(); + + private readonly List instructions = new List(); + + /// + /// Initializes a new instance of the class. + /// + /// The used to emit MSIL instructions. + /// The list of parameter types used by the current dynamic method. + public Emitter(ILGenerator generator, Type[] parameterTypes) + { + this.generator = generator; + this.parameterTypes = parameterTypes; + } + + /// + /// Gets the currently on the stack. + /// + public Type StackType + { + get + { + return stack.Count == 0 ? null : stack.Peek(); + } + } + + /// + /// Gets a list containing each to be emitted into the dynamic method. + /// + public List Instructions + { + get + { + return instructions; + } + } + + /// + /// Puts the specified instruction onto the stream of instructions. + /// + /// The Microsoft Intermediate Language (MSIL) instruction to be put onto the stream. + public void Emit(OpCode code) + { + if (code == OpCodes.Ldarg_0) + { + stack.Push(parameterTypes[0]); + } + else if (code == OpCodes.Ldarg_1) + { + stack.Push(parameterTypes[1]); + } + else if (code == OpCodes.Ldarg_2) + { + stack.Push(parameterTypes[2]); + } + else if (code == OpCodes.Ldarg_3) + { + stack.Push(parameterTypes[3]); + } + else if (code == OpCodes.Ldloc_0) + { + stack.Push(variables[0].LocalType); + } + else if (code == OpCodes.Ldloc_1) + { + stack.Push(variables[1].LocalType); + } + else if (code == OpCodes.Ldloc_2) + { + stack.Push(variables[2].LocalType); + } + else if (code == OpCodes.Ldloc_3) + { + stack.Push(variables[3].LocalType); + } + else if (code == OpCodes.Stloc_0) + { + stack.Pop(); + } + else if (code == OpCodes.Stloc_1) + { + stack.Pop(); + } + else if (code == OpCodes.Stloc_2) + { + stack.Pop(); + } + else if (code == OpCodes.Stloc_3) + { + stack.Pop(); + } + else if (code == OpCodes.Ldelem_Ref) + { + stack.Pop(); + Type arrayType = stack.Pop(); + stack.Push(arrayType.GetElementType()); + } + else if (code == OpCodes.Ldlen) + { + stack.Pop(); + stack.Push(typeof(int)); + } + else if (code == OpCodes.Conv_I4) + { + stack.Pop(); + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_0) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_1) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_2) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_3) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_4) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_5) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_6) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_7) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldc_I4_8) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Sub) + { + stack.Pop(); + stack.Pop(); + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ret) + { + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, il => il.Emit(code))); + if (code == OpCodes.Ret) + { + foreach (var instruction in instructions) + { + instruction.Emit(generator); + } + } + } + + /// + /// Puts the specified instruction and numerical argument onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be put onto the stream. + /// The numerical argument pushed onto the stream immediately after the instruction. + public void Emit(OpCode code, int arg) + { + if (code == OpCodes.Ldc_I4) + { + stack.Push(typeof(int)); + } + else if (code == OpCodes.Ldarg) + { + stack.Push(parameterTypes[arg]); + } + else if (code == OpCodes.Ldloc) + { + stack.Push(variables[arg].LocalType); + } + else if (code == OpCodes.Stloc) + { + stack.Pop(); + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, arg, il => il.Emit(code, arg))); + } + + /// + /// Puts the specified instruction and numerical argument onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be put onto the stream. + /// The numerical argument pushed onto the stream immediately after the instruction. + public void Emit(OpCode code, sbyte arg) + { + if (code == OpCodes.Ldc_I4_S) + { + stack.Push(typeof(sbyte)); + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, arg, il => il.Emit(code, arg))); + } + + /// + /// Puts the specified instruction and numerical argument onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be put onto the stream. + /// The numerical argument pushed onto the stream immediately after the instruction. + public void Emit(OpCode code, byte arg) + { + if (code == OpCodes.Ldloc_S) + { + stack.Push(variables[arg].LocalType); + } + else if (code == OpCodes.Ldarg_S) + { + stack.Push(parameterTypes[arg]); + } + else if (code == OpCodes.Stloc_S) + { + stack.Pop(); + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, arg, il => il.Emit(code, arg))); + } + + /// + /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream followed by the metadata token for the given type. + /// + /// The MSIL instruction to be put onto the stream. + /// A representing the type metadata token. + public void Emit(OpCode code, Type type) + { + if (code == OpCodes.Newarr) + { + stack.Pop(); + stack.Push(type.MakeArrayType()); + } + else if (code == OpCodes.Stelem) + { + stack.Pop(); + stack.Pop(); + stack.Pop(); + } + else if (code == OpCodes.Castclass) + { + stack.Pop(); + stack.Push(type); + } + else if (code == OpCodes.Box) + { + stack.Pop(); + stack.Push(typeof(object)); + } + else if (code == OpCodes.Unbox_Any) + { + stack.Pop(); + stack.Push(type); + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, type, il => il.Emit(code, type))); + } + + /// + /// Puts the specified instruction and metadata token for the specified constructor onto the Microsoft intermediate language (MSIL) stream of instructions. + /// + /// The MSIL instruction to be emitted onto the stream. + /// A representing a constructor. + public void Emit(OpCode code, ConstructorInfo constructor) + { + if (code == OpCodes.Newobj) + { + var parameterCount = constructor.GetParameters().Length; + for (int i = 0; i < parameterCount; i++) + { + stack.Pop(); + } + + stack.Push(constructor.DeclaringType); + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, constructor, il => il.Emit(code, constructor))); + } + + /// + /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream followed by the index of the given local variable. + /// + /// The MSIL instruction to be emitted onto the stream. + /// A local variable. + public void Emit(OpCode code, LocalBuilder localBuilder) + { + if (code == OpCodes.Stloc) + { + stack.Pop(); + } + else if (code == OpCodes.Ldloc) + { + stack.Push(localBuilder.LocalType); + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, localBuilder, il => il.Emit(code, localBuilder))); + } + + /// + /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream followed by the metadata token for the given method. + /// + /// The MSIL instruction to be emitted onto the stream. + /// A representing a method. + public void Emit(OpCode code, MethodInfo methodInfo) + { + if (code == OpCodes.Callvirt || code == OpCodes.Call) + { + var parameterCount = methodInfo.GetParameters().Length; + for (int i = 0; i < parameterCount; i++) + { + stack.Pop(); + } + + if (!methodInfo.IsStatic) + { + stack.Pop(); + } + + if (methodInfo.ReturnType != typeof(void)) + { + stack.Push(methodInfo.ReturnType); + } + } + else + { + throw new NotSupportedException(code.ToString()); + } + + instructions.Add(new Instruction(code, methodInfo, il => il.Emit(code, methodInfo))); + } + + /// + /// Declares a local variable of the specified type. + /// + /// A object that represents the type of the local variable. + /// The declared local variable. + public LocalBuilder DeclareLocal(Type type) + { + var localBuilder = generator.DeclareLocal(type); + variables.Add(localBuilder); + return localBuilder; + } + } + + /// + /// Provides storage per logical thread of execution. + /// + /// The type of the value contained in this . + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class LogicalThreadStorage + { + private readonly Func valueFactory; + + private readonly string key; + + private readonly object lockObject = new object(); + + /// + /// Initializes a new instance of the class. + /// + /// The value factory used to create an instance of . + public LogicalThreadStorage(Func valueFactory) + { + this.valueFactory = valueFactory; + key = Guid.NewGuid().ToString(); + } + + /// + /// Gets the value for the current logical thread of execution. + /// + /// + /// The value for the current logical thread of execution. + /// + public T Value + { + get + { + var holder = (LogicalThreadValue)CallContext.LogicalGetData(key); + if (holder != null) + { + return holder.Value; + } + + lock (lockObject) + { + holder = (LogicalThreadValue)CallContext.LogicalGetData(key); + if (holder == null) + { + holder = new LogicalThreadValue { Value = valueFactory() }; + CallContext.LogicalSetData(key, holder); + } + } + + return holder.Value; + } + } + + [Serializable] + private class LogicalThreadValue : MarshalByRefObject + { + [NonSerialized] + private T value; + + public T Value + { + get + { + return value; + } + + set + { + this.value = value; + } + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/LightInject/LightInjectExtensions.cs b/src/Umbraco.Core/LightInject/LightInjectExtensions.cs new file mode 100644 index 0000000000..f2900b58c9 --- /dev/null +++ b/src/Umbraco.Core/LightInject/LightInjectExtensions.cs @@ -0,0 +1,1015 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.LightInject +{ + internal static class LightInjectExtensions + { + /// + /// In order for LightInject to deal with enumerables of the same type, each one needs to be named individually + /// + /// + /// + /// + /// + public static void RegisterCollection(this IServiceContainer container, IEnumerable implementationTypes) + where TLifetime : ILifetime + { + var i = 0; + foreach (var type in implementationTypes) + { + //This works as of 3.0.2.2: https://github.com/seesharper/LightInject/issues/68#issuecomment-70611055 + // but means that the explicit type is registered, not the implementing type + container.Register(type, Activator.CreateInstance()); + + //NOTE: This doesn't work, but it would be nice if it did (autofac supports thsi) + //container.Register(typeof(TService), type, + // Activator.CreateInstance()); + + //This does work, but requires a unique name per service + //container.Register(typeof(TService), type, + // //need to name it, we'll keep the name tiny + // i.ToString(CultureInfo.InvariantCulture), + // Activator.CreateInstance()); + //i++; + } + } + + /// + /// In order for LightInject to deal with enumerables of the same type, each one needs to be named individually + /// + /// + /// + /// + public static void RegisterCollection(this IServiceContainer container, IEnumerable implementationTypes) + { + var i = 0; + foreach (var type in implementationTypes) + { + //This works as of 3.0.2.2: https://github.com/seesharper/LightInject/issues/68#issuecomment-70611055 + // but means that the explicit type is registered, not the implementing type + container.Register(type); + + //NOTE: This doesn't work, but it would be nice if it did (autofac supports thsi) + //container.Register(typeof(TService), type); + + //This does work, but requires a unique name per service + //container.Register(typeof(TService), type, + // //need to name it, we'll keep the name tiny + // i.ToString(CultureInfo.InvariantCulture)); + //i++; + } + } + + /// + /// Creates a child container from the parent container + /// + /// + /// + public static ServiceContainer CreateChildContainer(this IServiceContainer parentContainer) + { + var child = new ChildContainer(parentContainer); + return child; + } + + private class ChildContainer : ServiceContainer + { + public ChildContainer(IServiceRegistry parentContainer) + { + foreach (var svc in parentContainer.AvailableServices) + { + Register(svc); + } + } + } + + ///// + ///// A container wrapper for 2 containers: Child and Parent + ///// + //private class ChildContainer : IServiceContainer + //{ + // private readonly IServiceContainer _parent; + // private readonly IServiceContainer _current = new ServiceContainer(); + + // public ChildContainer(IServiceContainer parent) + // { + // _parent = parent; + // } + + // /// + // /// Gets a list of instances that represents the + // /// registered services. + // /// + // public IEnumerable AvailableServices + // { + // get { return _current.AvailableServices.Union(_parent.AvailableServices); } + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // public void Register(Type serviceType, Type implementingType) + // { + // _current.Register(serviceType, implementingType); + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // /// The instance that controls the lifetime of the registered service. + // public void Register(Type serviceType, Type implementingType, ILifetime lifetime) + // { + // _current.Register(serviceType, implementingType, lifetime); + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // /// The name of the service. + // public void Register(Type serviceType, Type implementingType, string serviceName) + // { + // _current.Register(serviceType, implementingType, serviceName); + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // /// The name of the service. + // /// The instance that controls the lifetime of the registered service. + // public void Register(Type serviceType, Type implementingType, string serviceName, ILifetime lifetime) + // { + // _current.Register(serviceType, implementingType, serviceName, lifetime); + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // public void Register() where TImplementation : TService + // { + // _current.Register(); + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // /// The instance that controls the lifetime of the registered service. + // public void Register(ILifetime lifetime) where TImplementation : TService + // { + // _current.Register(lifetime); + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // /// The name of the service. + // public void Register(string serviceName) where TImplementation : TService + // { + // _current.Register(serviceName); + // } + + // /// + // /// Registers the with the . + // /// + // /// The service type to register. + // /// The implementing type. + // /// The name of the service. + // /// The instance that controls the lifetime of the registered service. + // public void Register(string serviceName, ILifetime lifetime) where TImplementation : TService + // { + // _current.Register(serviceName, lifetime); + // } + + // /// + // /// Registers the with the given . + // /// + // /// The service type to register. + // /// The instance returned when this service is requested. + // public void RegisterInstance(TService instance) + // { + // _current.RegisterInstance(instance); + // } + + // /// + // /// Registers the with the given . + // /// + // /// The service type to register. + // /// The instance returned when this service is requested. + // /// The name of the service. + // public void RegisterInstance(TService instance, string serviceName) + // { + // _current.RegisterInstance(instance, serviceName); + // } + + // /// + // /// Registers the with the given . + // /// + // /// The service type to register. + // /// The instance returned when this service is requested. + // public void RegisterInstance(Type serviceType, object instance) + // { + // _current.RegisterInstance(serviceType, instance); + // } + + // /// + // /// Registers the with the given . + // /// + // /// The service type to register. + // /// The instance returned when this service is requested. + // /// The name of the service. + // public void RegisterInstance(Type serviceType, object instance, string serviceName) + // { + // _current.RegisterInstance(serviceType, instance, serviceName); + // } + + // /// + // /// Registers a concrete type as a service. + // /// + // /// The service type to register. + // public void Register() + // { + // _current.Register(); + // } + + // /// + // /// Registers a concrete type as a service. + // /// + // /// The service type to register. + // /// The instance that controls the lifetime of the registered service. + // public void Register(ILifetime lifetime) + // { + // _current.Register(lifetime); + // } + + // /// + // /// Registers a concrete type as a service. + // /// + // /// The concrete type to register. + // public void Register(Type serviceType) + // { + // _current.Register(serviceType); + // } + + // /// + // /// Registers a concrete type as a service. + // /// + // /// The concrete type to register. + // /// The instance that controls the lifetime of the registered service. + // public void Register(Type serviceType, ILifetime lifetime) + // { + // _current.Register(serviceType, lifetime); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The service type to register. + // /// A factory delegate used to create the instance. + // public void Register(Expression> factory) + // { + // _current.Register(factory); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The parameter type. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // public void Register(Expression> factory) + // { + // _current.Register(factory); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The parameter type. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // /// The name of the service. + // public void Register(Expression> factory, string serviceName) + // { + // _current.Register(factory, serviceName); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // public void Register(Expression> factory) + // { + // _current.Register(factory); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // /// The name of the service. + // public void Register(Expression> factory, string serviceName) + // { + // _current.Register(factory, serviceName); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // public void Register(Expression> factory) + // { + // _current.Register(factory); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // /// The name of the service. + // public void Register(Expression> factory, string serviceName) + // { + // _current.Register(factory, serviceName); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The type of the fourth parameter. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // public void Register(Expression> factory) + // { + // _current.Register(factory); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The type of the fourth parameter. + // /// The service type to register. + // /// A factory delegate used to create the instance. + // /// The name of the service. + // public void Register(Expression> factory, string serviceName) + // { + // _current.Register(factory, serviceName); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The service type to register. + // /// The lambdaExpression that describes the dependencies of the service. + // /// The instance that controls the lifetime of the registered service. + // public void Register(Expression> factory, ILifetime lifetime) + // { + // _current.Register(factory, lifetime); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The service type to register. + // /// The lambdaExpression that describes the dependencies of the service. + // /// The name of the service. + // public void Register(Expression> factory, string serviceName) + // { + // _current.Register(factory, serviceName); + // } + + // /// + // /// Registers the with the that + // /// describes the dependencies of the service. + // /// + // /// The service type to register. + // /// The lambdaExpression that describes the dependencies of the service. + // /// The name of the service. + // /// The instance that controls the lifetime of the registered service. + // public void Register(Expression> factory, string serviceName, ILifetime lifetime) + // { + // _current.Register(factory, serviceName, lifetime); + // } + + // /// + // /// Registers a custom factory delegate used to create services that is otherwise unknown to the service container. + // /// + // /// Determines if the service can be created by the delegate. + // /// Creates a service instance according to the predicate. + // public void RegisterFallback(Func predicate, Func factory) + // { + // _current.RegisterFallback(predicate, factory); + // } + + // /// + // /// Registers a custom factory delegate used to create services that is otherwise unknown to the service container. + // /// + // /// Determines if the service can be created by the delegate. + // /// Creates a service instance according to the predicate. + // /// The instance that controls the lifetime of the registered service. + // public void RegisterFallback(Func predicate, Func factory, ILifetime lifetime) + // { + // _current.RegisterFallback(predicate, factory, lifetime); + // } + + // /// + // /// Registers a service based on a instance. + // /// + // /// The instance that contains service metadata. + // public void Register(ServiceRegistration serviceRegistration) + // { + // _current.Register(serviceRegistration); + // } + + // /// + // /// Registers composition roots from the given . + // /// + // /// The assembly to be scanned for services. + // /// + // /// If the target contains an implementation of the interface, this + // /// will be used to configure the container. + // /// + // public void RegisterAssembly(Assembly assembly) + // { + // _current.RegisterAssembly(assembly); + // } + + // /// + // /// Registers services from the given . + // /// + // /// The assembly to be scanned for services. + // /// A function delegate that determines if a service implementation should be registered. + // /// + // /// If the target contains an implementation of the interface, this + // /// will be used to configure the container. + // /// + // public void RegisterAssembly(Assembly assembly, Func shouldRegister) + // { + // _current.RegisterAssembly(assembly, shouldRegister); + // } + + // /// + // /// Registers services from the given . + // /// + // /// The assembly to be scanned for services. + // /// The instance that controls the lifetime of the registered service. + // /// + // /// If the target contains an implementation of the interface, this + // /// will be used to configure the container. + // /// + // public void RegisterAssembly(Assembly assembly, Func lifetime) + // { + // _current.RegisterAssembly(assembly, lifetime); + // } + + // /// + // /// Registers services from the given . + // /// + // /// The assembly to be scanned for services. + // /// The factory that controls the lifetime of the registered service. + // /// A function delegate that determines if a service implementation should be registered. + // /// + // /// If the target contains an implementation of the interface, this + // /// will be used to configure the container. + // /// + // public void RegisterAssembly(Assembly assembly, Func lifetimeFactory, Func shouldRegister) + // { + // _current.RegisterAssembly(assembly, lifetimeFactory, shouldRegister); + // } + + // /// + // /// Registers services from the given type. + // /// + // /// The type of to register from. + // public void RegisterFrom() where TCompositionRoot : ICompositionRoot, new() + // { + // _current.RegisterFrom(); + // } + + // /// + // /// Registers composition roots from assemblies in the base directory that matches the . + // /// + // /// The search pattern used to filter the assembly files. + // public void RegisterAssembly(string searchPattern) + // { + // _current.RegisterAssembly(searchPattern); + // } + + // /// + // /// Decorates the with the given . + // /// + // /// The target service type. + // /// The decorator type used to decorate the . + // /// A function delegate that determines if the + // /// should be applied to the target . + // public void Decorate(Type serviceType, Type decoratorType, Func predicate) + // { + // _current.Decorate(serviceType, decoratorType, predicate); + // } + + // /// + // /// Decorates the with the given . + // /// + // /// The target service type. + // /// The decorator type used to decorate the . + // public void Decorate(Type serviceType, Type decoratorType) + // { + // _current.Decorate(serviceType, decoratorType); + // } + + // /// + // /// Decorates the with the given . + // /// + // /// The target service type. + // /// The decorator type used to decorate the . + // public void Decorate() where TDecorator : TService + // { + // _current.Decorate(); + // } + + // /// + // /// Decorates the using the given decorator . + // /// + // /// The target service type. + // /// A factory delegate used to create a decorator instance. + // public void Decorate(Expression> factory) + // { + // _current.Decorate(factory); + // } + + // /// + // /// Registers a decorator based on a instance. + // /// + // /// The instance that contains the decorator metadata. + // public void Decorate(DecoratorRegistration decoratorRegistration) + // { + // _current.Decorate(decoratorRegistration); + // } + + // /// + // /// Allows a registered service to be overridden by another . + // /// + // /// A function delegate that is used to determine the service that should be + // /// overridden using the returned from the . + // /// The factory delegate used to create a that overrides + // /// the incoming . + // public void Override(Func serviceSelector, Func serviceRegistrationFactory) + // { + // _current.Override(serviceSelector, serviceRegistrationFactory); + // } + + // /// + // /// Starts a new . + // /// + // /// + // public Scope BeginScope() + // { + // return _current.BeginScope(); + // } + + // /// + // /// Ends the current . + // /// + // public void EndCurrentScope() + // { + // _current.EndCurrentScope(); + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the requested service. + // /// The requested service instance. + // public object GetInstance(Type serviceType) + // { + // var instance = _current.TryGetInstance(serviceType); + // return instance ?? _parent.GetInstance(serviceType); + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the requested service. + // /// The arguments to be passed to the target instance. + // /// The requested service instance. + // public object GetInstance(Type serviceType, object[] arguments) + // { + // try + // { + // return _current.GetInstance(serviceType, arguments); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(serviceType, arguments); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the requested service. + // /// The name of the requested service. + // /// The arguments to be passed to the target instance. + // /// The requested service instance. + // public object GetInstance(Type serviceType, string serviceName, object[] arguments) + // { + // try + // { + // return _current.GetInstance(serviceType, serviceName, arguments); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(serviceType, serviceName, arguments); + // } + // } + + // /// + // /// Gets a named instance of the given . + // /// + // /// The type of the requested service. + // /// The name of the requested service. + // /// The requested service instance. + // public object GetInstance(Type serviceType, string serviceName) + // { + // var instance = _current.TryGetInstance(serviceType, serviceName); + // return instance ?? _parent.GetInstance(serviceType, serviceName); + // } + + // /// + // /// Gets an instance of the given type. + // /// + // /// The type of the requested service. + // /// The requested service instance. + // public TService GetInstance() + // { + // var instance = _current.TryGetInstance() as object; + // return (TService) (instance ?? _parent.GetInstance()); + // } + + // /// + // /// Gets a named instance of the given . + // /// + // /// The type of the requested service. + // /// The name of the requested service. + // /// The requested service instance. + // public TService GetInstance(string serviceName) + // { + // var instance = _current.TryGetInstance(serviceName) as object; + // return (TService)(instance ?? _parent.GetInstance(serviceName)); + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the argument. + // /// The type of the requested service. + // /// The argument value. + // /// The requested service instance. + // public TService GetInstance(T value) + // { + // try + // { + // return _current.GetInstance(value); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(value); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the parameter. + // /// The type of the requested service. + // /// The argument value. + // /// The name of the requested service. + // /// The requested service instance. + // public TService GetInstance(T value, string serviceName) + // { + // try + // { + // return _current.GetInstance(value, serviceName); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(value, serviceName); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the requested service. + // /// The first argument value. + // /// The second argument value. + // /// The requested service instance. + // public TService GetInstance(T1 arg1, T2 arg2) + // { + // try + // { + // return _current.GetInstance(arg1, arg2); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(arg1, arg2); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the requested service. + // /// The first argument value. + // /// The second argument value. + // /// The name of the requested service. + // /// The requested service instance. + // public TService GetInstance(T1 arg1, T2 arg2, string serviceName) + // { + // try + // { + // return _current.GetInstance(arg1, arg2, serviceName); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(arg1, arg2, serviceName); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The type of the requested service. + // /// The first argument value. + // /// The second argument value. + // /// The third argument value. + // /// The requested service instance. + // public TService GetInstance(T1 arg1, T2 arg2, T3 arg3) + // { + // try + // { + // return _current.GetInstance(arg1, arg2, arg3); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(arg1, arg2, arg3); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The type of the requested service. + // /// The first argument value. + // /// The second argument value. + // /// The third argument value. + // /// The name of the requested service. + // /// The requested service instance. + // public TService GetInstance(T1 arg1, T2 arg2, T3 arg3, string serviceName) + // { + // try + // { + // return _current.GetInstance(arg1, arg2, arg3, serviceName); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(arg1, arg2, arg3, serviceName); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The type of the fourth parameter. + // /// The type of the requested service. + // /// The first argument value. + // /// The second argument value. + // /// The third argument value. + // /// The fourth argument value. + // /// The requested service instance. + // public TService GetInstance(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + // { + // try + // { + // return _current.GetInstance(arg1, arg2, arg3, arg4); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(arg1, arg2, arg3, arg4); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the first parameter. + // /// The type of the second parameter. + // /// The type of the third parameter. + // /// The type of the fourth parameter. + // /// The type of the requested service. + // /// The first argument value. + // /// The second argument value. + // /// The third argument value. + // /// The fourth argument value. + // /// The name of the requested service. + // /// The requested service instance. + // public TService GetInstance(T1 arg1, T2 arg2, T3 arg3, T4 arg4, string serviceName) + // { + // try + // { + // return _current.GetInstance(arg1, arg2, arg3, arg4, serviceName); + // } + // catch (InvalidOperationException) + // { + // return _parent.GetInstance(arg1, arg2, arg3, arg4, serviceName); + // } + // } + + // /// + // /// Gets an instance of the given . + // /// + // /// The type of the requested service. + // /// The requested service instance if available, otherwise null. + // public object TryGetInstance(Type serviceType) + // { + // return _current.TryGetInstance(serviceType) ?? _parent.TryGetInstance(serviceType); + // } + + // /// + // /// Gets a named instance of the given . + // /// + // /// The type of the requested service. + // /// The name of the requested service. + // /// The requested service instance if available, otherwise null. + // public object TryGetInstance(Type serviceType, string serviceName) + // { + // return _current.TryGetInstance(serviceType, serviceName) ?? _parent.TryGetInstance(serviceType, serviceName); + // } + + // /// + // /// Tries to get an instance of the given type. + // /// + // /// The type of the requested service. + // /// The requested service instance if available, otherwise default(T). + // public TService TryGetInstance() + // { + // return (TService) ((_current.TryGetInstance() as object) ?? _parent.TryGetInstance()); + // } + + // /// + // /// Tries to get an instance of the given type. + // /// + // /// The type of the requested service. + // /// The name of the requested service. + // /// The requested service instance if available, otherwise default(T). + // public TService TryGetInstance(string serviceName) + // { + // return (TService)((_current.TryGetInstance(serviceName) as object) ?? _parent.TryGetInstance(serviceName)); + // } + + // /// + // /// Gets all instances of the given . + // /// + // /// The type of services to resolve. + // /// A list that contains all implementations of the . + // public IEnumerable GetAllInstances(Type serviceType) + // { + // return _current.GetAllInstances(serviceType).Union(_parent.GetAllInstances(serviceType)); + // } + + // /// + // /// Gets all instances of type . + // /// + // /// The type of services to resolve. + // /// A list that contains all implementations of the type. + // public IEnumerable GetAllInstances() + // { + // return _current.GetAllInstances().Union(_parent.GetAllInstances()); + // } + + // /// + // /// Creates an instance of a concrete class. + // /// + // /// The type of class for which to create an instance. + // /// An instance of . + // /// The concrete type will be registered if not already registered with the container. + // public TService Create() where TService : class + // { + // return _current.Create(); + // } + + // /// + // /// Creates an instance of a concrete class. + // /// + // /// The type of class for which to create an instance. + // /// An instance of the . + // public object Create(Type serviceType) + // { + // return _current.Create(serviceType); + // } + + // /// + // /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + // /// + // public void Dispose() + // { + // //only dispose the current container + // _current.Dispose(); + // } + + // /// + // /// Gets or sets the that is responsible + // /// for providing the used to manage scopes. + // /// + // public IScopeManagerProvider ScopeManagerProvider + // { + // get { return _current.ScopeManagerProvider; } + // set { _current.ScopeManagerProvider = value; } + // } + + // /// + // /// Returns true if the container can create the requested service, otherwise false. + // /// + // /// The of the service. + // /// The name of the service. + // /// true if the container can create the requested service, otherwise false. + // public bool CanGetInstance(Type serviceType, string serviceName) + // { + // return _current.CanGetInstance(serviceType, serviceName) || _parent.CanGetInstance(serviceType, serviceName); + // } + + // /// + // /// Injects the property dependencies for a given . + // /// + // /// The target instance for which to inject its property dependencies. + // /// The with its property dependencies injected. + // public object InjectProperties(object instance) + // { + // var result = _current.InjectProperties(instance); + // return _parent.InjectProperties(result); + // } + //} + + } +} diff --git a/src/Umbraco.Core/Logging/LoggerResolver.cs b/src/Umbraco.Core/Logging/LoggerResolver.cs index 5ca0839c60..431700c67b 100644 --- a/src/Umbraco.Core/Logging/LoggerResolver.cs +++ b/src/Umbraco.Core/Logging/LoggerResolver.cs @@ -2,6 +2,12 @@ using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.Logging { + /// + /// The logger resolver + /// + /// + /// NOTE: This is a 'special' resolver in that it gets initialized before most other things, it cannot use IoC so it cannot implement ContainerObjectResolverBase + /// public sealed class LoggerResolver : SingleObjectResolverBase { public LoggerResolver(ILogger logger) diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs index b2e882aa17..3e66fe0eeb 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs @@ -1,12 +1,24 @@ -using Umbraco.Core.ObjectResolution; +using System; +using Umbraco.Core.LightInject; +using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.Models.PublishedContent { /// /// Resolves the IPublishedContentModelFactory object. /// - public class PublishedContentModelFactoryResolver : SingleObjectResolverBase + public class PublishedContentModelFactoryResolver : ContainerSingleObjectResolver { + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal PublishedContentModelFactoryResolver(IServiceContainer container, Type implementationType) + : base(container, implementationType) + { + } + /// /// Initializes a new instance of the . /// @@ -24,6 +36,15 @@ namespace Umbraco.Core.Models.PublishedContent : base(factory) { } + /// + /// Initialize the resolver to use IoC, when using this contructor the type must be set manually + /// + /// + internal PublishedContentModelFactoryResolver(IServiceContainer container) + : base(container) + { + } + /// /// Sets the factory. /// diff --git a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs index 1b55a89a9c..97c725609c 100644 --- a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs +++ b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs @@ -1,151 +1,172 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Umbraco.Core.Logging; -using umbraco.interfaces; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Threading; +//using Umbraco.Core.LightInject; +//using Umbraco.Core.Logging; +//using Umbraco.Core.Services; +//using umbraco.interfaces; -namespace Umbraco.Core.ObjectResolution -{ - /// - /// A resolver to return all IApplicationEvents objects - /// - /// - /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances. - /// - internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase, IDisposable - { +//namespace Umbraco.Core.ObjectResolution +//{ +// /// +// /// A resolver to return all IApplicationEvents objects +// /// +// /// +// /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances. +// /// +// internal sealed class ApplicationEventsResolver : ManyObjectsResolverBase, IDisposable +// { +// private IServiceContainer _container; +// private readonly LegacyStartupHandlerResolver _legacyResolver; - private readonly LegacyStartupHandlerResolver _legacyResolver; +// public ApplicationEventsResolver(IServiceContainer container, IEnumerable applicationEventHandlers) +// : base(container.GetInstance(), container.GetInstance(), applicationEventHandlers) +// { +// //create the legacy resolver and only include the legacy types +// _legacyResolver = new LegacyStartupHandlerResolver( +// container, +// applicationEventHandlers.Where(x => TypeHelper.IsTypeAssignableFrom(x) == false)); +// } - /// - /// Constructor - /// - /// - /// - /// - internal ApplicationEventsResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable applicationEventHandlers) - : base(serviceProvider, logger, applicationEventHandlers) - { - //create the legacy resolver and only include the legacy types - _legacyResolver = new LegacyStartupHandlerResolver( - applicationEventHandlers.Where(x => !TypeHelper.IsTypeAssignableFrom(x))); - } +// ///// +// ///// Constructor +// ///// +// ///// +// ///// +// ///// +// ///// +// //internal ApplicationEventsResolver(IServiceContainer parentContainer, IServiceProvider serviceProvider, ILogger logger, IEnumerable applicationEventHandlers) +// // : base(serviceProvider, logger, applicationEventHandlers) +// //{ +// // //create the legacy resolver and only include the legacy types +// // _legacyResolver = new LegacyStartupHandlerResolver( +// // applicationEventHandlers.Where(x => !TypeHelper.IsTypeAssignableFrom(x))); +// //} - /// - /// Override in order to only return types of IApplicationEventHandler and above, - /// do not include the legacy types of IApplicationStartupHandler - /// - protected override IEnumerable InstanceTypes - { - get { return base.InstanceTypes.Where(TypeHelper.IsTypeAssignableFrom); } - } +// /// +// /// Override in order to only return types of IApplicationEventHandler and above, +// /// do not include the legacy types of IApplicationStartupHandler +// /// +// protected override IEnumerable InstanceTypes +// { +// get { return base.InstanceTypes.Where(TypeHelper.IsTypeAssignableFrom); } +// } - /// - /// Gets the implementations. - /// - public IEnumerable ApplicationEventHandlers - { - get { return Values; } - } +// /// +// /// Gets the implementations. +// /// +// public IEnumerable ApplicationEventHandlers +// { +// get +// { + +// //return Values; +// } +// } - /// - /// Create instances of all of the legacy startup handlers - /// - public void InstantiateLegacyStartupHandlers() - { - //this will instantiate them all - var handlers = _legacyResolver.LegacyStartupHandlers; - } +// /// +// /// Create instances of all of the legacy startup handlers +// /// +// public void InstantiateLegacyStartupHandlers() +// { +// //this will instantiate them all +// var handlers = _legacyResolver.LegacyStartupHandlers; +// } - protected override bool SupportsClear - { - get { return false; } - } +// protected override bool SupportsClear +// { +// get { return false; } +// } - protected override bool SupportsInsert - { - get { return false; } - } +// protected override bool SupportsInsert +// { +// get { return false; } +// } - private class LegacyStartupHandlerResolver : ManyObjectsResolverBase, IDisposable - { - internal LegacyStartupHandlerResolver(IEnumerable legacyStartupHandlers) - : base(legacyStartupHandlers) - { +// private class LegacyStartupHandlerResolver : ManyObjectsResolverBase, IDisposable +// { +// internal LegacyStartupHandlerResolver(IServiceContainer container, IEnumerable applicationEventHandlers) +// : base(container.GetInstance(), container.GetInstance(), applicationEventHandlers) +// { +// } - } +// //internal LegacyStartupHandlerResolver(IEnumerable legacyStartupHandlers) +// // : base(legacyStartupHandlers) +// //{ - public IEnumerable LegacyStartupHandlers - { - get { return Values; } - } +// //} - public void Dispose() - { - ResetCollections(); - } - } +// public IEnumerable LegacyStartupHandlers +// { +// get { return Values; } +// } - private bool _disposed; - private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim(); +// public void Dispose() +// { +// ResetCollections(); +// } +// } - /// - /// Gets a value indicating whether this instance is disposed. - /// - /// - /// true if this instance is disposed; otherwise, false. - /// - public bool IsDisposed - { - get { return _disposed; } - } +// private bool _disposed; +// private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim(); - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// 2 - public void Dispose() - { - Dispose(true); +// /// +// /// Gets a value indicating whether this instance is disposed. +// /// +// /// +// /// true if this instance is disposed; otherwise, false. +// /// +// public bool IsDisposed +// { +// get { return _disposed; } +// } - // Use SupressFinalize in case a subclass of this type implements a finalizer. - GC.SuppressFinalize(this); - } +// /// +// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. +// /// +// /// 2 +// public void Dispose() +// { +// Dispose(true); - ~ApplicationEventsResolver() - { - // Run dispose but let the class know it was due to the finalizer running. - Dispose(false); - } +// // Use SupressFinalize in case a subclass of this type implements a finalizer. +// GC.SuppressFinalize(this); +// } - private void Dispose(bool disposing) - { - // Only operate if we haven't already disposed - if (IsDisposed || disposing == false) return; +// ~ApplicationEventsResolver() +// { +// // Run dispose but let the class know it was due to the finalizer running. +// Dispose(false); +// } - using (new WriteLock(_disposalLocker)) - { - // Check again now we're inside the lock - if (IsDisposed) return; +// private void Dispose(bool disposing) +// { +// // Only operate if we haven't already disposed +// if (IsDisposed || disposing == false) return; - // Call to actually release resources. This method is only - // kept separate so that the entire disposal logic can be used as a VS snippet - DisposeResources(); +// using (new WriteLock(_disposalLocker)) +// { +// // Check again now we're inside the lock +// if (IsDisposed) return; - // Indicate that the instance has been disposed. - _disposed = true; - } - } +// // Call to actually release resources. This method is only +// // kept separate so that the entire disposal logic can be used as a VS snippet +// DisposeResources(); - /// - /// Clear out all of the instances, we don't want them hanging around and cluttering up memory - /// - private void DisposeResources() - { - _legacyResolver.Dispose(); - ResetCollections(); - } +// // Indicate that the instance has been disposed. +// _disposed = true; +// } +// } + +// /// +// /// Clear out all of the instances, we don't want them hanging around and cluttering up memory +// /// +// private void DisposeResources() +// { +// _legacyResolver.Dispose(); +// ResetCollections(); +// } - } -} \ No newline at end of file +// } +//} \ No newline at end of file diff --git a/src/Umbraco.Core/ObjectResolution/ContainerManyObjectsResolver.cs b/src/Umbraco.Core/ObjectResolution/ContainerManyObjectsResolver.cs new file mode 100644 index 0000000000..fcac5c01f8 --- /dev/null +++ b/src/Umbraco.Core/ObjectResolution/ContainerManyObjectsResolver.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Umbraco.Core.LightInject; +using Umbraco.Core.Logging; +using Umbraco.Core.Security; + +namespace Umbraco.Core.ObjectResolution +{ + /// + /// A many objects resolver that uses IoC + /// + /// + /// + public abstract class ContainerManyObjectsResolver : ManyObjectsResolverBase + where TResolved : class + where TResolver : ResolverBase + { + private readonly IServiceContainer _container; + + #region Constructors used for test - ONLY so that a container is not required and will just revert to using the normal ManyObjectsResolverBase + [Obsolete("Used for tests only - should remove")] + internal ContainerManyObjectsResolver(ILogger logger, IEnumerable types, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) + : base(logger, types, scope) + { + } + [Obsolete("Used for tests only - should remove")] + internal ContainerManyObjectsResolver(IServiceProvider serviceProvider, ILogger logger, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) + : base(serviceProvider, logger, scope) + { + } + [Obsolete("Used for tests only - should remove")] + internal ContainerManyObjectsResolver(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext) + : base(serviceProvider, logger, httpContext) + { + } + [Obsolete("Used for tests only - should remove")] + internal ContainerManyObjectsResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) + : base(serviceProvider, logger, value, scope) + { + } + #endregion + + + /// + /// Constructor for use with IoC + /// + /// + /// + /// + /// + internal ContainerManyObjectsResolver(IServiceContainer container, ILogger logger, IEnumerable types, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) + : base(logger, types, scope) + { + _container = container; + + + Resolution.Frozen += Resolution_Frozen; + + } + + + + /// + /// When resolution is frozen add all the types to the container + /// + /// + /// + void Resolution_Frozen(object sender, EventArgs e) + { + foreach (var type in InstanceTypes) + { + _container.Register(type, GetLifetime(LifetimeScope)); + } + } + + /// + /// Convert the ObjectLifetimeScope to ILifetime + /// + /// + /// + private static ILifetime GetLifetime(ObjectLifetimeScope scope) + { + switch (scope) + { + case ObjectLifetimeScope.HttpRequest: + return new PerRequestLifeTime(); + case ObjectLifetimeScope.Application: + return new PerContainerLifetime(); + case ObjectLifetimeScope.Transient: + default: + return null; + } + } + + /// + /// Creates the instances from IoC + /// + /// A list of objects of type . + protected override IEnumerable CreateValues(ObjectLifetimeScope scope) + { + //NOTE: we ignore scope because objects are registered under this scope and not build based on the scope. + + return _container.GetAllInstances(); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/ObjectResolution/ContainerSingleObjectResolver.cs b/src/Umbraco.Core/ObjectResolution/ContainerSingleObjectResolver.cs new file mode 100644 index 0000000000..ca757b97a4 --- /dev/null +++ b/src/Umbraco.Core/ObjectResolution/ContainerSingleObjectResolver.cs @@ -0,0 +1,123 @@ +using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; + +namespace Umbraco.Core.ObjectResolution +{ + + /// + /// A single object resolver that can be configured to use IoC to instantiate and wire up the object + /// + /// + /// + public abstract class ContainerSingleObjectResolver : SingleObjectResolverBase + where TResolved : class + where TResolver : ResolverBase + { + private readonly IServiceContainer _container; + + + #region Constructors used for test - ONLY so that a container is not required and will just revert to using the normal SingleObjectResolverBase + + [Obsolete("Used for tests only - should remove")] + internal ContainerSingleObjectResolver() + { + } + + [Obsolete("Used for tests only - should remove")] + internal ContainerSingleObjectResolver(TResolved value) + : base(value) + { + } + + [Obsolete("Used for tests only - should remove")] + internal ContainerSingleObjectResolver(bool canBeNull) + : base(canBeNull) + { + } + + [Obsolete("Used for tests only - should remove")] + internal ContainerSingleObjectResolver(TResolved value, bool canBeNull) + : base(value, canBeNull) + { + } + #endregion + + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal ContainerSingleObjectResolver(IServiceContainer container, Type implementationType) + { + if (container == null) throw new ArgumentNullException("container"); + if (implementationType == null) throw new ArgumentNullException("implementationType"); + _container = container; + _container.Register(typeof(TResolved), implementationType, new PerContainerLifetime()); + } + + /// + /// Initialize the resolver to use IoC, when using this contructor the type must be set manually + /// + /// + internal ContainerSingleObjectResolver(IServiceContainer container) + { + if (container == null) throw new ArgumentNullException("container"); + _container = container; + } + + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal ContainerSingleObjectResolver(IServiceContainer container, Expression> implementationType) + { + _container = container; + _container.Register(implementationType); + } + + /// + /// Gets or sets the resolved object instance. + /// + /// + /// value is set to null, but cannot be null (CanBeNull is false). + /// value is read and is null, but cannot be null (CanBeNull is false), + /// or value is set (read) and resolution is (not) frozen. + protected override TResolved Value + { + get + { + if (_container == null) return base.Value; + return _container.GetInstance(); + } + set + { + if (_container != null) + { + _container.Override( + sr => sr.ServiceType == typeof (TResolved), + (factory, registration) => + { + registration.Value = value; + registration.Lifetime = new PerContainerLifetime(); + return registration; + }); + } + base.Value = value; + } + } + + /// + /// Gets a value indicating whether the resolved object instance is null. + /// + public override bool HasValue + { + get + { + if (_container == null) return base.HasValue; + return (_container.TryGetInstance() == null) == false; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs index 2851a70e15..0a3c9a98e7 100644 --- a/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs +++ b/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs @@ -39,74 +39,30 @@ namespace Umbraco.Core.ObjectResolution { Initialize(); } - - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) - : base(scope) - { - Initialize(); - } - - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected LazyManyObjectsResolverBase(HttpContextBase httpContext) - : base(httpContext) - { - Initialize(); - } - + protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, IEnumerable> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(serviceProvider, logger, scope) { AddTypes(lazyTypeList); } - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected LazyManyObjectsResolverBase(IEnumerable> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, lazyTypeList, scope) - { - } - protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, Func> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(serviceProvider, logger, scope) { _typeListProducerList.Add(typeListProducerList); } - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected LazyManyObjectsResolverBase(Func> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, typeListProducerList, scope) - { - } - protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext, IEnumerable> lazyTypeList) : this(serviceProvider, logger, httpContext) { AddTypes(lazyTypeList); } - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected LazyManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable> lazyTypeList) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext, lazyTypeList) - { - } - protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext, Func> typeListProducerList) : this(serviceProvider, logger, httpContext) { _typeListProducerList.Add(typeListProducerList); - } - - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected LazyManyObjectsResolverBase(HttpContextBase httpContext, Func> typeListProducerList) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext, typeListProducerList) - { - } + } #endregion diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index 1cfa81228a..8eec5b1163 100644 --- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs @@ -1,4 +1,5 @@ using System; +using System.CodeDom; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -9,23 +10,39 @@ using Umbraco.Core.Logging; namespace Umbraco.Core.ObjectResolution { /// - /// The base class for all many-objects resolvers. - /// - /// The type of the concrete resolver class. - /// The type of the resolved objects. - public abstract class ManyObjectsResolverBase : ResolverBase - where TResolved : class + /// The base class for all many-objects resolvers. + /// + /// The type of the concrete resolver class. + /// The type of the resolved objects. + public abstract class ManyObjectsResolverBase : ResolverBase + where TResolved : class where TResolver : ResolverBase - { - private Lazy> _applicationInstances; - private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); - private readonly string _httpContextKey; - private readonly List _instanceTypes = new List(); - private IEnumerable _sortedValues; + { + private Lazy> _applicationInstances; + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private readonly string _httpContextKey; + private readonly List _instanceTypes = new List(); + private IEnumerable _sortedValues; - private int _defaultPluginWeight = 10; + private int _defaultPluginWeight = 10; - #region Constructors + #region Constructors + + /// + /// Hack: This is purely here to allow for the container resolver to be used, we'll need to refactor all of this slowly till we're + /// happy with the outcome + /// + /// + /// + /// + internal ManyObjectsResolverBase(ILogger logger, IEnumerable types, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) + { + if (logger == null) throw new ArgumentNullException("logger"); + if (types == null) throw new ArgumentNullException("types"); + _instanceTypes = types.ToList(); + LifetimeScope = scope; + Logger = logger; + } /// /// Initializes a new instance of the class with an empty list of objects, @@ -59,14 +76,6 @@ namespace Umbraco.Core.ObjectResolution InitializeAppInstances(); } - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected ManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, scope) - { - - } - /// /// Initializes a new instance of the class with an empty list of objects, /// with creation of objects based on an HttpRequest lifetime scope. @@ -76,26 +85,16 @@ namespace Umbraco.Core.ObjectResolution /// The HttpContextBase corresponding to the HttpRequest. /// is null. protected ManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext) - { + { if (serviceProvider == null) throw new ArgumentNullException("serviceProvider"); if (httpContext == null) throw new ArgumentNullException("httpContext"); CanResolveBeforeFrozen = false; Logger = logger; - LifetimeScope = ObjectLifetimeScope.HttpRequest; - _httpContextKey = GetType().FullName; + LifetimeScope = ObjectLifetimeScope.HttpRequest; + _httpContextKey = GetType().FullName; ServiceProvider = serviceProvider; CurrentHttpContext = httpContext; - _instanceTypes = new List(); - - InitializeAppInstances(); - } - - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected ManyObjectsResolverBase(HttpContextBase httpContext) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext) - { - + _instanceTypes = new List(); } /// @@ -110,57 +109,36 @@ namespace Umbraco.Core.ObjectResolution /// is per HttpRequest but the current HttpContext is null. protected ManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, IEnumerable value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(serviceProvider, logger, scope) - { - _instanceTypes = value.ToList(); - } - - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected ManyObjectsResolverBase(IEnumerable value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, value, scope) { - + _instanceTypes = value.ToList(); } - /// - /// Initializes a new instance of the class with an initial list of objects, - /// with creation of objects based on an HttpRequest lifetime scope. - /// - /// The HttpContextBase corresponding to the HttpRequest. - /// The list of object types. - /// is null. - [Obsolete("Use ctor specifying IServiceProvider instead")] - protected ManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable value) - : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext) - { - _instanceTypes = value.ToList(); - } - #endregion + #endregion private void InitializeAppInstances() { _applicationInstances = new Lazy>(() => CreateInstances().ToArray()); } - /// - /// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen. - /// - /// This is false by default and is used for some special internal resolvers. - internal bool CanResolveBeforeFrozen { get; set; } + /// + /// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen. + /// + /// This is false by default and is used for some special internal resolvers. + internal bool CanResolveBeforeFrozen { get; set; } - /// - /// Gets the list of types to create instances from. - /// - protected virtual IEnumerable InstanceTypes - { - get { return _instanceTypes; } - } + /// + /// Gets the list of types to create instances from. + /// + protected virtual IEnumerable InstanceTypes + { + get { return _instanceTypes; } + } - /// - /// Gets or sets the used to initialize this object, if any. - /// - /// If not null, then LifetimeScope will be ObjectLifetimeScope.HttpRequest. - protected HttpContextBase CurrentHttpContext { get; private set; } + /// + /// Gets or sets the used to initialize this object, if any. + /// + /// If not null, then LifetimeScope will be ObjectLifetimeScope.HttpRequest. + protected HttpContextBase CurrentHttpContext { get; private set; } /// /// Returns the service provider used to instantiate objects @@ -170,20 +148,20 @@ namespace Umbraco.Core.ObjectResolution public ILogger Logger { get; private set; } /// - /// Gets or sets the lifetime scope of resolved objects. - /// - protected ObjectLifetimeScope LifetimeScope { get; private set; } + /// Gets or sets the lifetime scope of resolved objects. + /// + protected ObjectLifetimeScope LifetimeScope { get; private set; } - /// - /// Gets the resolved object instances, sorted by weight. - /// - /// The sorted resolved object instances. - /// - /// The order is based upon the WeightedPluginAttribute and DefaultPluginWeight. - /// Weights are sorted ascendingly (lowest weights come first). - /// - protected IEnumerable GetSortedValues() - { + /// + /// Gets the resolved object instances, sorted by weight. + /// + /// The sorted resolved object instances. + /// + /// The order is based upon the WeightedPluginAttribute and DefaultPluginWeight. + /// Weights are sorted ascendingly (lowest weights come first). + /// + protected IEnumerable GetSortedValues() + { if (_sortedValues == null) { var values = Values.ToList(); @@ -191,199 +169,206 @@ namespace Umbraco.Core.ObjectResolution _sortedValues = values; } return _sortedValues; - } + } - /// - /// Gets or sets the default type weight. - /// - /// Determines the weight of types that do not have a WeightedPluginAttribute set on - /// them, when calling GetSortedValues. - protected virtual int DefaultPluginWeight - { - get { return _defaultPluginWeight; } - set { _defaultPluginWeight = value; } - } + /// + /// Gets or sets the default type weight. + /// + /// Determines the weight of types that do not have a WeightedPluginAttribute set on + /// them, when calling GetSortedValues. + protected virtual int DefaultPluginWeight + { + get { return _defaultPluginWeight; } + set { _defaultPluginWeight = value; } + } /// /// Returns the weight of an object for user with GetSortedValues /// /// /// - protected virtual int GetObjectWeight(object o) - { - var type = o.GetType(); - var attr = type.GetCustomAttribute(true); - return attr == null ? DefaultPluginWeight : attr.Weight; - } + protected virtual int GetObjectWeight(object o) + { + var type = o.GetType(); + var attr = type.GetCustomAttribute(true); + return attr == null ? DefaultPluginWeight : attr.Weight; + } - /// - /// Gets the resolved object instances. - /// - /// CanResolveBeforeFrozen is false, and resolution is not frozen. - protected IEnumerable Values - { - get - { - using (Resolution.Reader(CanResolveBeforeFrozen)) - { - // note: we apply .ToArray() to the output of CreateInstance() because that is an IEnumerable that - // comes from the PluginManager we want to be _sure_ that it's not a Linq of some sort, but the - // instances have actually been instanciated when we return. + /// + /// Gets the resolved object instances. + /// + /// CanResolveBeforeFrozen is false, and resolution is not frozen. + protected IEnumerable Values + { + get { return CreateValues(LifetimeScope); } + } - switch (LifetimeScope) - { - case ObjectLifetimeScope.HttpRequest: + /// + /// Creates the values collection based on the scope + /// + /// + /// + protected virtual IEnumerable CreateValues(ObjectLifetimeScope scope) + { + using (Resolution.Reader(CanResolveBeforeFrozen)) + { + // note: we apply .ToArray() to the output of CreateInstance() because that is an IEnumerable that + // comes from the PluginManager we want to be _sure_ that it's not a Linq of some sort, but the + // instances have actually been instanciated when we return. - // create new instances per HttpContext - if (CurrentHttpContext.Items[_httpContextKey] == null) + switch (LifetimeScope) + { + case ObjectLifetimeScope.HttpRequest: + + // create new instances per HttpContext + if (CurrentHttpContext.Items[_httpContextKey] == null) + { + var instances = CreateInstances().ToArray(); + var disposableInstances = instances.OfType(); + //Ensure anything resolved that is IDisposable is disposed when the request termintates + foreach (var disposable in disposableInstances) { - var instances = CreateInstances().ToArray(); - var disposableInstances = instances.OfType(); - //Ensure anything resolved that is IDisposable is disposed when the request termintates - foreach (var disposable in disposableInstances) - { - CurrentHttpContext.DisposeOnPipelineCompleted(disposable); - } - CurrentHttpContext.Items[_httpContextKey] = instances; + CurrentHttpContext.DisposeOnPipelineCompleted(disposable); } - return (TResolved[])CurrentHttpContext.Items[_httpContextKey]; - - case ObjectLifetimeScope.Application: + CurrentHttpContext.Items[_httpContextKey] = instances; + } + return (TResolved[])CurrentHttpContext.Items[_httpContextKey]; - return _applicationInstances.Value; + case ObjectLifetimeScope.Application: - case ObjectLifetimeScope.Transient: - default: - // create new instances each time - return CreateInstances().ToArray(); - } + return _applicationInstances.Value; + + case ObjectLifetimeScope.Transient: + default: + // create new instances each time + return CreateInstances().ToArray(); } - } - } + } + } - /// - /// Creates the object instances for the types contained in the types collection. - /// - /// A list of objects of type . - protected virtual IEnumerable CreateInstances() - { - return ServiceProvider.CreateInstances(InstanceTypes, Logger); - } + /// + /// Instantiates the object instances for the types contained in the types collection. + /// + /// A list of objects of type . + protected virtual IEnumerable CreateInstances() + { + return ServiceProvider.CreateInstances(InstanceTypes, Logger); + } - #region Types collection manipulation + #region Types collection manipulation - /// - /// Removes a type. - /// - /// The type to remove. - /// the resolver does not support removing types, or - /// the type is not a valid type for the resolver. - public virtual void RemoveType(Type value) - { - EnsureSupportsRemove(); + /// + /// Removes a type. + /// + /// The type to remove. + /// the resolver does not support removing types, or + /// the type is not a valid type for the resolver. + public virtual void RemoveType(Type value) + { + EnsureSupportsRemove(); - using (Resolution.Configuration) - using (var l = new UpgradeableReadLock(_lock)) - { - EnsureCorrectType(value); + using (Resolution.Configuration) + using (var l = new UpgradeableReadLock(_lock)) + { + EnsureCorrectType(value); - l.UpgradeToWriteLock(); - _instanceTypes.Remove(value); - } - } + l.UpgradeToWriteLock(); + _instanceTypes.Remove(value); + } + } - /// - /// Removes a type. - /// - /// The type to remove. - /// the resolver does not support removing types, or - /// the type is not a valid type for the resolver. - public void RemoveType() + /// + /// Removes a type. + /// + /// The type to remove. + /// the resolver does not support removing types, or + /// the type is not a valid type for the resolver. + public void RemoveType() where T : TResolved - { - RemoveType(typeof(T)); - } + { + RemoveType(typeof(T)); + } - /// - /// Adds types. - /// - /// The types to add. - /// The types are appended at the end of the list. - /// the resolver does not support adding types, or - /// a type is not a valid type for the resolver, or a type is already in the collection of types. - protected void AddTypes(IEnumerable types) - { - EnsureSupportsAdd(); + /// + /// Adds types. + /// + /// The types to add. + /// The types are appended at the end of the list. + /// the resolver does not support adding types, or + /// a type is not a valid type for the resolver, or a type is already in the collection of types. + protected void AddTypes(IEnumerable types) + { + EnsureSupportsAdd(); - using (Resolution.Configuration) - using (new WriteLock(_lock)) - { - foreach(var t in types) - { - EnsureCorrectType(t); + using (Resolution.Configuration) + using (new WriteLock(_lock)) + { + foreach (var t in types) + { + EnsureCorrectType(t); if (_instanceTypes.Contains(t)) - { - throw new InvalidOperationException(string.Format( - "Type {0} is already in the collection of types.", t.FullName)); - } - _instanceTypes.Add(t); - } - } - } + { + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", t.FullName)); + } + _instanceTypes.Add(t); + } + } + } - /// - /// Adds a type. - /// - /// The type to add. - /// The type is appended at the end of the list. - /// the resolver does not support adding types, or - /// the type is not a valid type for the resolver, or the type is already in the collection of types. - public virtual void AddType(Type value) - { - EnsureSupportsAdd(); + /// + /// Adds a type. + /// + /// The type to add. + /// The type is appended at the end of the list. + /// the resolver does not support adding types, or + /// the type is not a valid type for the resolver, or the type is already in the collection of types. + public virtual void AddType(Type value) + { + EnsureSupportsAdd(); - using (Resolution.Configuration) - using (var l = new UpgradeableReadLock(_lock)) - { - EnsureCorrectType(value); + using (Resolution.Configuration) + using (var l = new UpgradeableReadLock(_lock)) + { + EnsureCorrectType(value); if (_instanceTypes.Contains(value)) - { - throw new InvalidOperationException(string.Format( - "Type {0} is already in the collection of types.", value.FullName)); - } + { + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", value.FullName)); + } - l.UpgradeToWriteLock(); - _instanceTypes.Add(value); - } - } + l.UpgradeToWriteLock(); + _instanceTypes.Add(value); + } + } - /// - /// Adds a type. - /// - /// The type to add. - /// The type is appended at the end of the list. - /// the resolver does not support adding types, or - /// the type is not a valid type for the resolver, or the type is already in the collection of types. - public void AddType() + /// + /// Adds a type. + /// + /// The type to add. + /// The type is appended at the end of the list. + /// the resolver does not support adding types, or + /// the type is not a valid type for the resolver, or the type is already in the collection of types. + public void AddType() where T : TResolved - { - AddType(typeof(T)); - } + { + AddType(typeof(T)); + } - /// - /// Clears the list of types - /// - /// the resolver does not support clearing types. - public virtual void Clear() - { - EnsureSupportsClear(); + /// + /// Clears the list of types + /// + /// the resolver does not support clearing types. + public virtual void Clear() + { + EnsureSupportsClear(); - using (Resolution.Configuration) - using (new WriteLock(_lock)) - { - _instanceTypes.Clear(); - } - } + using (Resolution.Configuration) + using (new WriteLock(_lock)) + { + _instanceTypes.Clear(); + } + } /// /// WARNING! Do not use this unless you know what you are doing, clear all types registered and instances @@ -399,32 +384,32 @@ namespace Umbraco.Core.ObjectResolution } } - /// - /// Inserts a type at the specified index. - /// - /// The zero-based index at which the type should be inserted. - /// The type to insert. - /// the resolver does not support inserting types, or - /// the type is not a valid type for the resolver, or the type is already in the collection of types. - /// is out of range. - public virtual void InsertType(int index, Type value) - { - EnsureSupportsInsert(); + /// + /// Inserts a type at the specified index. + /// + /// The zero-based index at which the type should be inserted. + /// The type to insert. + /// the resolver does not support inserting types, or + /// the type is not a valid type for the resolver, or the type is already in the collection of types. + /// is out of range. + public virtual void InsertType(int index, Type value) + { + EnsureSupportsInsert(); - using (Resolution.Configuration) - using (var l = new UpgradeableReadLock(_lock)) - { - EnsureCorrectType(value); + using (Resolution.Configuration) + using (var l = new UpgradeableReadLock(_lock)) + { + EnsureCorrectType(value); if (_instanceTypes.Contains(value)) - { - throw new InvalidOperationException(string.Format( - "Type {0} is already in the collection of types.", value.FullName)); - } + { + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", value.FullName)); + } - l.UpgradeToWriteLock(); - _instanceTypes.Insert(index, value); - } - } + l.UpgradeToWriteLock(); + _instanceTypes.Insert(index, value); + } + } /// /// Inserts a type at the beginning of the list. @@ -438,16 +423,16 @@ namespace Umbraco.Core.ObjectResolution } /// - /// Inserts a type at the specified index. - /// - /// The type to insert. - /// The zero-based index at which the type should be inserted. - /// is out of range. - public void InsertType(int index) + /// Inserts a type at the specified index. + /// + /// The type to insert. + /// The zero-based index at which the type should be inserted. + /// is out of range. + public void InsertType(int index) where T : TResolved - { - InsertType(index, typeof(T)); - } + { + InsertType(index, typeof(T)); + } /// /// Inserts a type at the beginning of the list. @@ -458,68 +443,68 @@ namespace Umbraco.Core.ObjectResolution { InsertType(0, typeof(T)); } - - /// - /// Inserts a type before a specified, already existing type. - /// - /// The existing type before which to insert. - /// The type to insert. - /// the resolver does not support inserting types, or - /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, - /// or the new type is already in the collection of types. - public virtual void InsertTypeBefore(Type existingType, Type value) - { - EnsureSupportsInsert(); - using (Resolution.Configuration) - using (var l = new UpgradeableReadLock(_lock)) - { - EnsureCorrectType(existingType); - EnsureCorrectType(value); + /// + /// Inserts a type before a specified, already existing type. + /// + /// The existing type before which to insert. + /// The type to insert. + /// the resolver does not support inserting types, or + /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, + /// or the new type is already in the collection of types. + public virtual void InsertTypeBefore(Type existingType, Type value) + { + EnsureSupportsInsert(); + + using (Resolution.Configuration) + using (var l = new UpgradeableReadLock(_lock)) + { + EnsureCorrectType(existingType); + EnsureCorrectType(value); if (_instanceTypes.Contains(existingType) == false) - { - throw new InvalidOperationException(string.Format( - "Type {0} is not in the collection of types.", existingType.FullName)); - } + { + throw new InvalidOperationException(string.Format( + "Type {0} is not in the collection of types.", existingType.FullName)); + } if (_instanceTypes.Contains(value)) - { - throw new InvalidOperationException(string.Format( - "Type {0} is already in the collection of types.", value.FullName)); - } + { + throw new InvalidOperationException(string.Format( + "Type {0} is already in the collection of types.", value.FullName)); + } int index = _instanceTypes.IndexOf(existingType); - l.UpgradeToWriteLock(); - _instanceTypes.Insert(index, value); - } - } + l.UpgradeToWriteLock(); + _instanceTypes.Insert(index, value); + } + } - /// - /// Inserts a type before a specified, already existing type. - /// - /// The existing type before which to insert. - /// The type to insert. - /// the resolver does not support inserting types, or - /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, - /// or the new type is already in the collection of types. - public void InsertTypeBefore() + /// + /// Inserts a type before a specified, already existing type. + /// + /// The existing type before which to insert. + /// The type to insert. + /// the resolver does not support inserting types, or + /// one of the types is not a valid type for the resolver, or the existing type is not in the collection, + /// or the new type is already in the collection of types. + public void InsertTypeBefore() where TExisting : TResolved where T : TResolved - { - InsertTypeBefore(typeof(TExisting), typeof(T)); - } + { + InsertTypeBefore(typeof(TExisting), typeof(T)); + } - /// - /// Returns a value indicating whether the specified type is already in the collection of types. - /// - /// The type to look for. - /// A value indicating whether the type is already in the collection of types. - public virtual bool ContainsType(Type value) - { - using (new ReadLock(_lock)) - { - return _instanceTypes.Contains(value); - } - } + /// + /// Returns a value indicating whether the specified type is already in the collection of types. + /// + /// The type to look for. + /// A value indicating whether the type is already in the collection of types. + public virtual bool ContainsType(Type value) + { + using (new ReadLock(_lock)) + { + return _instanceTypes.Contains(value); + } + } /// /// Gets the types in the collection of types. @@ -536,27 +521,27 @@ namespace Umbraco.Core.ObjectResolution return types; } - /// - /// Returns a value indicating whether the specified type is already in the collection of types. - /// - /// The type to look for. - /// A value indicating whether the type is already in the collection of types. - public bool ContainsType() + /// + /// Returns a value indicating whether the specified type is already in the collection of types. + /// + /// The type to look for. + /// A value indicating whether the type is already in the collection of types. + public bool ContainsType() where T : TResolved - { - return ContainsType(typeof(T)); - } + { + return ContainsType(typeof(T)); + } - #endregion + #endregion - /// - /// Returns a WriteLock to use when modifying collections - /// - /// - protected WriteLock GetWriteLock() - { - return new WriteLock(_lock); - } + /// + /// Returns a WriteLock to use when modifying collections + /// + /// + protected WriteLock GetWriteLock() + { + return new WriteLock(_lock); + } #region Type utilities @@ -581,70 +566,71 @@ namespace Umbraco.Core.ObjectResolution /// /// The resolver does not support removing types. protected void EnsureSupportsRemove() - { - if (SupportsRemove == false) + { + if (SupportsRemove == false) throw new InvalidOperationException("This resolver does not support removing types"); - } + } /// /// Ensures that the resolver supports clearing types. /// /// The resolver does not support clearing types. - protected void EnsureSupportsClear() { - if (SupportsClear == false) + protected void EnsureSupportsClear() + { + if (SupportsClear == false) throw new InvalidOperationException("This resolver does not support clearing types"); - } + } /// /// Ensures that the resolver supports adding types. /// /// The resolver does not support adding types. protected void EnsureSupportsAdd() - { - if (SupportsAdd == false) + { + if (SupportsAdd == false) throw new InvalidOperationException("This resolver does not support adding new types"); - } + } /// /// Ensures that the resolver supports inserting types. /// /// The resolver does not support inserting types. protected void EnsureSupportsInsert() - { - if (SupportsInsert == false) + { + if (SupportsInsert == false) throw new InvalidOperationException("This resolver does not support inserting new types"); - } + } /// /// Gets a value indicating whether the resolver supports adding types. /// - protected virtual bool SupportsAdd - { - get { return true; } - } + protected virtual bool SupportsAdd + { + get { return true; } + } /// /// Gets a value indicating whether the resolver supports inserting types. /// protected virtual bool SupportsInsert - { - get { return true; } - } + { + get { return true; } + } /// /// Gets a value indicating whether the resolver supports clearing types. /// protected virtual bool SupportsClear - { - get { return true; } - } + { + get { return true; } + } /// /// Gets a value indicating whether the resolver supports removing types. /// protected virtual bool SupportsRemove - { - get { return true; } + { + get { return true; } } #endregion diff --git a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ResolverBase.cs index 4b3b21082c..2f1653bd64 100644 --- a/src/Umbraco.Core/ObjectResolution/ResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ResolverBase.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using Umbraco.Core.LightInject; namespace Umbraco.Core.ObjectResolution { @@ -48,6 +49,12 @@ namespace Umbraco.Core.ObjectResolution } + internal ResolverBase(IServiceContainer container) + : base(() => Reset()) + { + + } + /// /// Gets or sets the resolver singleton instance. /// diff --git a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs b/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs index 27a82b17ef..7bb502e9fd 100644 --- a/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/SingleObjectResolverBase.cs @@ -3,7 +3,7 @@ using System.Threading; namespace Umbraco.Core.ObjectResolution { - /// + /// /// The base class for all single-object resolvers. /// /// The type of the concrete resolver class. @@ -86,7 +86,7 @@ namespace Umbraco.Core.ObjectResolution /// /// Gets a value indicating whether the resolved object instance is null. /// - public bool HasValue + public virtual bool HasValue { get { return _value != null; } } @@ -98,7 +98,7 @@ namespace Umbraco.Core.ObjectResolution /// value is set to null, but cannot be null (CanBeNull is false). /// value is read and is null, but cannot be null (CanBeNull is false), /// or value is set (read) and resolution is (not) frozen. - protected TResolved Value + protected virtual TResolved Value { get { diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 7fa3b63ede..d9d98a3ffe 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -46,7 +46,7 @@ namespace Umbraco.Core /// /// /// - internal PluginManager(IServiceProvider serviceProvider, IRuntimeCacheProvider runtimeCache, ProfilingLogger logger, bool detectChanges = true) + public PluginManager(IServiceProvider serviceProvider, IRuntimeCacheProvider runtimeCache, ProfilingLogger logger, bool detectChanges = true) { if (serviceProvider == null) throw new ArgumentNullException("serviceProvider"); if (runtimeCache == null) throw new ArgumentNullException("runtimeCache"); @@ -494,7 +494,7 @@ namespace Umbraco.Core /// internal IEnumerable ResolveApplicationStartupHandlers() { - return ResolveTypes(); + return ResolveTypes(); } /// diff --git a/src/Umbraco.Core/Profiling/ProfilerResolver.cs b/src/Umbraco.Core/Profiling/ProfilerResolver.cs index e6b8bc7aab..63f647af87 100644 --- a/src/Umbraco.Core/Profiling/ProfilerResolver.cs +++ b/src/Umbraco.Core/Profiling/ProfilerResolver.cs @@ -5,6 +5,9 @@ namespace Umbraco.Core.Profiling /// /// A resolver exposing the current profiler /// + /// + /// NOTE: This is a 'special' resolver in that it gets initialized before most other things, it cannot use IoC so it cannot implement ContainerObjectResolverBase + /// internal class ProfilerResolver : SingleObjectResolverBase { /// diff --git a/src/Umbraco.Core/Profiling/WebProfiler.cs b/src/Umbraco.Core/Profiling/WebProfiler.cs index 00d088bca7..0592e25bc1 100644 --- a/src/Umbraco.Core/Profiling/WebProfiler.cs +++ b/src/Umbraco.Core/Profiling/WebProfiler.cs @@ -10,20 +10,19 @@ namespace Umbraco.Core.Profiling /// /// A profiler used for web based activity based on the MiniProfiler framework /// - internal class WebProfiler : IProfiler + internal class WebProfiler : ApplicationEventHandler, IProfiler { - /// - /// Constructor + ///Binds to application events to enable the MiniProfiler /// - /// - /// Binds to application events to enable the MiniProfiler - /// - internal WebProfiler() + /// + /// + protected override void ApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { UmbracoApplicationBase.ApplicationInit += UmbracoApplicationApplicationInit; } + /// /// Handle the Init event o fthe UmbracoApplication which allows us to subscribe to the HttpApplication events /// diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs index fa2d4da594..95d3316635 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs @@ -20,12 +20,6 @@ namespace Umbraco.Core.PropertyEditors { } - [Obsolete("Use the ctor specifying a PluginManager instead")] - public PropertyEditorResolver(Func> typeListProducerList) - : base(typeListProducerList, ObjectLifetimeScope.Application) - { - } - /// /// Returns the property editors /// diff --git a/src/Umbraco.Core/Standalone/StandaloneCoreBootManager.cs b/src/Umbraco.Core/Standalone/StandaloneCoreBootManager.cs index ea4397c285..5a95a4476c 100644 --- a/src/Umbraco.Core/Standalone/StandaloneCoreBootManager.cs +++ b/src/Umbraco.Core/Standalone/StandaloneCoreBootManager.cs @@ -23,16 +23,16 @@ namespace Umbraco.Core.Standalone } - protected override void InitializeApplicationEventsResolver() - { - base.InitializeApplicationEventsResolver(); + //protected override void InitializeApplicationEventsResolver() + //{ + // base.InitializeApplicationEventsResolver(); - foreach (var type in _handlersToAdd) - ApplicationEventsResolver.Current.AddType(type); + // foreach (var type in _handlersToAdd) + // ApplicationEventsResolver.Current.AddType(type); - foreach (var type in _handlersToRemove) - ApplicationEventsResolver.Current.RemoveType(type); - } + // foreach (var type in _handlersToRemove) + // ApplicationEventsResolver.Current.RemoveType(type); + //} protected override void InitializeResolvers() { diff --git a/src/Umbraco.Core/Strategies/ManifestWatcherHandler.cs b/src/Umbraco.Core/Strategies/ManifestWatcherHandler.cs new file mode 100644 index 0000000000..ad28f2e0bf --- /dev/null +++ b/src/Umbraco.Core/Strategies/ManifestWatcherHandler.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +using Umbraco.Core.IO; +using Umbraco.Core.Manifest; + +namespace Umbraco.Core.Strategies +{ + public sealed class ManifestWatcherHandler : ApplicationEventHandler + { + private ManifestWatcher _mw; + + protected override void ApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) + { + UmbracoApplicationBase.ApplicationEnd += app_ApplicationEnd; + } + + void app_ApplicationEnd(object sender, EventArgs e) + { + if (_mw != null) + { + _mw.Dispose(); + } + } + + protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) + { + _mw = new ManifestWatcher(applicationContext.ProfilingLogger.Logger); + _mw.Start(Directory.GetDirectories(IOHelper.MapPath("~/App_Plugins/"))); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 0aa2028e39..566ff9bcd7 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -929,7 +929,7 @@ namespace Umbraco.Core // as the ShortStringHelper is too important, so as long as it's not there // already, we use a default one. That should never happen, but... Logging.LogHelper.Warn("ShortStringHelperResolver.HasCurrent == false, fallback to default."); - _helper = new DefaultShortStringHelper().WithDefaultConfig(); + _helper = new DefaultShortStringHelper(UmbracoConfig.For.UmbracoSettings()).WithDefaultConfig(); _helper.Freeze(); return _helper; } diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 3414389704..e3a3aec9ca 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Globalization; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; namespace Umbraco.Core.Strings { @@ -18,10 +19,13 @@ namespace Umbraco.Core.Strings /// public class DefaultShortStringHelper : IShortStringHelper { + private readonly IUmbracoSettingsSection _settings; + #region Ctor and vars - public DefaultShortStringHelper() + public DefaultShortStringHelper(IUmbracoSettingsSection settings) { + _settings = settings; InitializeLegacyUrlReplaceCharacters(); } @@ -57,7 +61,7 @@ namespace Umbraco.Core.Strings private void InitializeLegacyUrlReplaceCharacters() { - foreach (var node in UmbracoConfig.For.UmbracoSettings().RequestHandler.CharCollection) + foreach (var node in _settings.RequestHandler.CharCollection) { if(string.IsNullOrEmpty(node.Char) == false) _urlReplaceCharacters[node.Char] = node.Replacement; @@ -146,7 +150,7 @@ namespace Umbraco.Core.Strings PreFilter = ApplyUrlReplaceCharacters, PostFilter = x => CutMaxLength(x, 240), IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', // letter, digit or underscore - StringType = (UmbracoConfig.For.UmbracoSettings().RequestHandler.ConvertUrlsToAscii ? CleanStringType.Ascii : CleanStringType.Utf8) | CleanStringType.LowerCase, + StringType = (_settings.RequestHandler.ConvertUrlsToAscii ? CleanStringType.Ascii : CleanStringType.Utf8) | CleanStringType.LowerCase, BreakTermsOnUpper = false, Separator = '-' }).WithConfig(CleanStringType.FileName, new Config @@ -323,7 +327,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ public string GetShortStringServicesJavaScript(string controllerPath) { return string.Format(SssjsFormat, - UmbracoConfig.For.UmbracoSettings().Content.ForceSafeAliases ? "true" : "false", controllerPath); + _settings.Content.ForceSafeAliases ? "true" : "false", controllerPath); } #endregion diff --git a/src/Umbraco.Core/Strings/ShortStringHelperResolver.cs b/src/Umbraco.Core/Strings/ShortStringHelperResolver.cs index 25be58b102..62e8957591 100644 --- a/src/Umbraco.Core/Strings/ShortStringHelperResolver.cs +++ b/src/Umbraco.Core/Strings/ShortStringHelperResolver.cs @@ -1,12 +1,37 @@ +using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.Strings { - /// + /// /// Resolves the IShortStringHelper object - /// - public sealed class ShortStringHelperResolver : SingleObjectResolverBase - { + /// + public sealed class ShortStringHelperResolver : ContainerSingleObjectResolver + { + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal ShortStringHelperResolver(IServiceContainer container, Type implementationType) + : base(container, implementationType) + { + Resolution.Frozen += (sender, args) => Value.Freeze(); + } + + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal ShortStringHelperResolver(IServiceContainer container, Expression> implementationType) + : base(container, implementationType) + { + Resolution.Frozen += (sender, args) => Value.Freeze(); + } + /// /// Initializes a new instance of the class with an instance of a helper. /// @@ -17,24 +42,24 @@ namespace Umbraco.Core.Strings { Resolution.Frozen += (sender, args) => Value.Freeze(); } - + /// - /// Sets the helper. - /// + /// Sets the helper. + /// /// The helper. - /// For developers, at application startup. + /// For developers, at application startup. public void SetHelper(IShortStringHelper helper) - { + { Value = helper; - } + } - /// - /// Gets the helper. - /// + /// + /// Gets the helper. + /// public IShortStringHelper Helper - { - get { return Value; } - } + { + get { return Value; } + } - } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs b/src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs index 745694e178..522d90605f 100644 --- a/src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs +++ b/src/Umbraco.Core/Strings/UrlSegmentProviderResolver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Core.LightInject; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; @@ -8,28 +9,40 @@ namespace Umbraco.Core.Strings /// /// Resolves IUrlSegmentProvider objects. /// - public sealed class UrlSegmentProviderResolver : ManyObjectsResolverBase + public sealed class UrlSegmentProviderResolver : ContainerManyObjectsResolver { /// - /// Initializes a new instance of the class with an initial list of provider types. + /// ONLY for testing /// /// /// + /// + [Obsolete("Used only for Tests - should remove")] + internal UrlSegmentProviderResolver(IServiceProvider serviceProvider, ILogger logger, params Type[] providerTypes) + : base(serviceProvider, logger, providerTypes) + { + } + + /// + /// Initializes a new instance of the class with an initial list of provider types. + /// + /// + /// /// The list of provider types. /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal UrlSegmentProviderResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable providerTypes) - : base(serviceProvider, logger, providerTypes) + internal UrlSegmentProviderResolver(IServiceContainer container, ILogger logger, IEnumerable providerTypes) + : base(container, logger, providerTypes) { } /// /// Initializes a new instance of the class with an initial list of provider types. /// - /// + /// /// /// The list of provider types. /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal UrlSegmentProviderResolver(IServiceProvider serviceProvider, ILogger logger, params Type[] providerTypes) - : base(serviceProvider, logger, providerTypes) + internal UrlSegmentProviderResolver(IServiceContainer container, ILogger logger, params Type[] providerTypes) + : base(container, logger, providerTypes) { } /// diff --git a/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs b/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs index 69f54fcab7..7143ed4095 100644 --- a/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs +++ b/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs @@ -16,8 +16,8 @@ namespace Umbraco.Core.Sync { private readonly IEnumerable _servers; - public ConfigServerRegistrar() - : this(UmbracoConfig.For.UmbracoSettings().DistributedCall.Servers) + public ConfigServerRegistrar(IUmbracoSettingsSection settings) + : this(settings.DistributedCall.Servers) { } diff --git a/src/Umbraco.Core/Sync/ServerMessengerResolver.cs b/src/Umbraco.Core/Sync/ServerMessengerResolver.cs index 549f3520e0..5f1471b3bd 100644 --- a/src/Umbraco.Core/Sync/ServerMessengerResolver.cs +++ b/src/Umbraco.Core/Sync/ServerMessengerResolver.cs @@ -1,14 +1,32 @@ -using Umbraco.Core.ObjectResolution; +using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; +using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.Sync { /// /// A resolver to return the currently registered IServerMessenger object /// - public sealed class ServerMessengerResolver : SingleObjectResolverBase + public sealed class ServerMessengerResolver : ContainerSingleObjectResolver { - internal ServerMessengerResolver(IServerMessenger factory) - : base(factory) + /// + /// Initialize a new instance of the class with an instance of the resolved object. + /// + /// An instance of the resolved object. + /// By default CanBeNull is false, so value has to be non-null, or Value has to be + /// initialized before being accessed, otherwise an exception will be thrown when reading it. + public ServerMessengerResolver(IServerMessenger value) : base(value) + { + } + + internal ServerMessengerResolver(IServiceContainer container, Type implementationType) + : base(container, implementationType) + { + } + + internal ServerMessengerResolver(IServiceContainer container, Expression> implementationType) + : base(container, implementationType) { } diff --git a/src/Umbraco.Core/Sync/ServerRegistrarResolver.cs b/src/Umbraco.Core/Sync/ServerRegistrarResolver.cs index 196c6fb74c..a8006ccf6b 100644 --- a/src/Umbraco.Core/Sync/ServerRegistrarResolver.cs +++ b/src/Umbraco.Core/Sync/ServerRegistrarResolver.cs @@ -1,15 +1,32 @@ -using Umbraco.Core.ObjectResolution; +using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; +using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.Sync { /// /// The resolver to return the currently registered IServerRegistrar object /// - public sealed class ServerRegistrarResolver : SingleObjectResolverBase + public sealed class ServerRegistrarResolver : ContainerSingleObjectResolver { + /// + /// Initialize a new instance of the class with an instance of the resolved object. + /// + /// An instance of the resolved object. + /// By default CanBeNull is false, so value has to be non-null, or Value has to be + /// initialized before being accessed, otherwise an exception will be thrown when reading it. + public ServerRegistrarResolver(IServerRegistrar value) : base(value) + { + } - internal ServerRegistrarResolver(IServerRegistrar factory) - : base(factory) + internal ServerRegistrarResolver(IServiceContainer container, Type implementationType) + : base(container, implementationType) + { + } + + internal ServerRegistrarResolver(IServiceContainer container, Expression> implementationType) + : base(container, implementationType) { } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 459b8c426b..ab608bb089 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -294,8 +294,12 @@ + + + + @@ -316,6 +320,7 @@ + diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs index bed1149234..d997c5e83a 100644 --- a/src/Umbraco.Core/UmbracoApplicationBase.cs +++ b/src/Umbraco.Core/UmbracoApplicationBase.cs @@ -21,13 +21,19 @@ namespace Umbraco.Core public abstract class UmbracoApplicationBase : System.Web.HttpApplication { - public static event EventHandler ApplicationStarting; - public static event EventHandler ApplicationStarted; + public event EventHandler ApplicationStarting; + public event EventHandler ApplicationStarted; /// /// Called when the HttpApplication.Init() is fired, allows developers to subscribe to the HttpApplication events /// + /// + /// Needs to be static otherwise null refs occur - though I don't know why + /// public static event EventHandler ApplicationInit; + public static event EventHandler ApplicationError; + public static event EventHandler ApplicationEnd; + /// /// Boots up the Umbraco application @@ -44,7 +50,7 @@ namespace Umbraco.Core .Complete(appContext => OnApplicationStarted(sender, e)); //And now we can dispose of our startup handlers - save some memory - ApplicationEventsResolver.Current.Dispose(); + //ApplicationEventsResolver.Current.Dispose(); } /// @@ -106,7 +112,8 @@ namespace Umbraco.Core /// protected virtual void OnApplicationError(object sender, EventArgs e) { - + EventHandler handler = ApplicationError; + if (handler != null) handler(this, EventArgs.Empty); } protected void Application_Error(object sender, EventArgs e) @@ -134,7 +141,8 @@ namespace Umbraco.Core /// protected virtual void OnApplicationEnd(object sender, EventArgs e) { - + EventHandler handler = ApplicationEnd; + if (handler != null) handler(this, EventArgs.Empty); } protected void Application_End(object sender, EventArgs e) diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index f01b9e68ba..0a7e3b962b 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -2,6 +2,7 @@ + diff --git a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs b/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs index 381f3889b5..e8eab43597 100644 --- a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs +++ b/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs @@ -1,155 +1,143 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using Moq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.ObjectResolution; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Tests.TestHelpers; -using umbraco.interfaces; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using System.Web; +//using Moq; +//using NUnit.Framework; +//using Umbraco.Core; +//using Umbraco.Core.Configuration; +//using Umbraco.Core.Logging; +//using Umbraco.Core.ObjectResolution; +//using Umbraco.Core.Persistence.SqlSyntax; +//using Umbraco.Tests.TestHelpers; +//using umbraco.interfaces; -namespace Umbraco.Tests.BootManagers -{ - [TestFixture] - public class CoreBootManagerTests : BaseUmbracoApplicationTest - { +//namespace Umbraco.Tests.BootManagers +//{ +// [TestFixture] +// public class CoreBootManagerTests : BaseUmbracoApplicationTest +// { - private TestApp _testApp; +// private TestApp _testApp; - [SetUp] - public override void Initialize() - { - base.Initialize(); - _testApp = new TestApp(); - } +// [SetUp] +// public override void Initialize() +// { +// base.Initialize(); +// _testApp = new TestApp(); +// } - [TearDown] - public override void TearDown() - { - base.TearDown(); +// [TearDown] +// public override void TearDown() +// { +// base.TearDown(); - _testApp = null; - } +// _testApp = null; +// } - protected override void FreezeResolution() - { - //don't freeze resolution, we'll do that in the boot manager - } +// protected override void FreezeResolution() +// { +// //don't freeze resolution, we'll do that in the boot manager +// } - /// - /// test application using a CoreBootManager instance to boot - /// - public class TestApp : UmbracoApplicationBase - { - protected override IBootManager GetBootManager() - { - return new TestBootManager(this); - } - } +// /// +// /// test application using a CoreBootManager instance to boot +// /// +// public class TestApp : UmbracoApplicationBase +// { +// protected override IBootManager GetBootManager() +// { +// return new TestBootManager(this); +// } +// } - /// - /// Test boot manager to add a custom application event handler - /// - public class TestBootManager : CoreBootManager - { - public TestBootManager(UmbracoApplicationBase umbracoApplication) - : base(umbracoApplication) - { - } +// /// +// /// Test boot manager to add a custom application event handler +// /// +// public class TestBootManager : CoreBootManager +// { +// public TestBootManager(UmbracoApplicationBase umbracoApplication) +// : base(umbracoApplication) +// { +// } - protected override void InitializeApplicationEventsResolver() - { - //create an empty resolver so we can add our own custom ones (don't type find) - ApplicationEventsResolver.Current = new ApplicationEventsResolver( - new ActivatorServiceProvider(), Mock.Of(), - new Type[] - { - typeof(LegacyStartupHandler), - typeof(TestApplicationEventHandler) - }) - { - CanResolveBeforeFrozen = true - }; - } - - } +// //TODO: For this test to work we need to udpate the multiple objects resolver to IoC, currently +// // the app events are purely IoC in the Core Boot manager - /// - /// Test legacy startup handler - /// - public class LegacyStartupHandler : IApplicationStartupHandler - { - public static bool Initialized = false; +// protected override void InitializeApplicationEventsResolver() +// { +// //create an empty resolver so we can add our own custom ones (don't type find) +// ApplicationEventsResolver.Current = new ApplicationEventsResolver( +// new ActivatorServiceProvider(), Mock.Of(), +// new Type[] +// { +// typeof(LegacyStartupHandler), +// typeof(TestApplicationEventHandler) +// }) +// { +// CanResolveBeforeFrozen = true +// }; +// } - public LegacyStartupHandler() - { - Initialized = true; - } - } +// } - /// - /// test event handler - /// - public class TestApplicationEventHandler : IApplicationEventHandler - { - public static bool Initialized = false; - public static bool Starting = false; - public static bool Started = false; +// /// +// /// test event handler +// /// +// public class TestApplicationEventHandler : IApplicationEventHandler +// { +// public static bool Initialized = false; +// public static bool Starting = false; +// public static bool Started = false; - public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) - { - Initialized = true; - } +// public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) +// { +// Initialized = true; +// } - public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) - { - Starting = true; - } +// public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) +// { +// Starting = true; +// } - public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) - { - Started = true; - } - } +// public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) +// { +// Started = true; +// } +// } - [Test] - public void Handle_IApplicationEventHandler_Objects_Outside_Web_Context() - { - _testApp.StartApplication(_testApp, new EventArgs()); +// [Test] +// public void Handle_IApplicationEventHandler_Objects_Outside_Web_Context() +// { +// _testApp.StartApplication(_testApp, new EventArgs()); - Assert.IsTrue(TestApplicationEventHandler.Initialized); - Assert.IsTrue(TestApplicationEventHandler.Starting); - Assert.IsTrue(TestApplicationEventHandler.Started); - } +// Assert.IsTrue(TestApplicationEventHandler.Initialized); +// Assert.IsTrue(TestApplicationEventHandler.Starting); +// Assert.IsTrue(TestApplicationEventHandler.Started); +// } - [Test] - public void Ensure_Legacy_Startup_Handlers_Not_Started_Until_Complete() - { - EventHandler starting = (sender, args) => - { - Assert.IsTrue(TestApplicationEventHandler.Initialized); - Assert.IsTrue(TestApplicationEventHandler.Starting); - Assert.IsFalse(LegacyStartupHandler.Initialized); - }; - EventHandler started = (sender, args) => - { - Assert.IsTrue(TestApplicationEventHandler.Started); - Assert.IsTrue(LegacyStartupHandler.Initialized); - }; - TestApp.ApplicationStarting += starting; - TestApp.ApplicationStarted += started; +// [Test] +// public void Ensure_Legacy_Startup_Handlers_Not_Started_Until_Complete() +// { +// EventHandler starting = (sender, args) => +// { +// Assert.IsTrue(TestApplicationEventHandler.Initialized); +// Assert.IsTrue(TestApplicationEventHandler.Starting); +// }; +// EventHandler started = (sender, args) => +// { +// Assert.IsTrue(TestApplicationEventHandler.Started); +// }; +// _testApp.ApplicationStarting += starting; +// _testApp.ApplicationStarted += started; - _testApp.StartApplication(_testApp, new EventArgs()); +// _testApp.StartApplication(_testApp, new EventArgs()); - TestApp.ApplicationStarting -= starting; - TestApp.ApplicationStarting -= started; +// _testApp.ApplicationStarting -= starting; +// _testApp.ApplicationStarting -= started; - } +// } - } -} +// } +//} diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs index 60fee894d3..9674baf920 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs @@ -1,4 +1,6 @@ +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; @@ -20,7 +22,7 @@ namespace Umbraco.Tests.Routing var routingContext = GetRoutingContext(urlAsString); var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docRequest = new PublishedContentRequest(url, routingContext); - var lookup = new ContentFinderByUrlAlias(); + var lookup = new ContentFinderByUrlAlias(Logger); var result = lookup.TryFindContent(docRequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs index b83f0bfd2b..e52498033e 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -1,5 +1,7 @@ using System.Linq; +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using umbraco.cms.businesslogic.language; @@ -71,7 +73,7 @@ namespace Umbraco.Tests.Routing if (expectedNode > 0) Assert.AreEqual(expectedCulture, pcr.Culture.Name); - var finder = new ContentFinderByUrlAlias(); + var finder = new ContentFinderByUrlAlias(Logger); var result = finder.TryFindContent(pcr); if (expectedNode > 0) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 6623ac23b9..d68b8190de 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -1,4 +1,6 @@ +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using umbraco.BusinessLogic; @@ -17,7 +19,7 @@ namespace Umbraco.Tests.Routing var routingContext = GetRoutingContext(urlAsString); var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docRequest = new PublishedContentRequest(url, routingContext); - var lookup = new ContentFinderByIdPath(); + var lookup = new ContentFinderByIdPath(Logger); var result = lookup.TryFindContent(docRequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs index 8308241607..e75a8b8e80 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs @@ -1,4 +1,6 @@ +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using umbraco.BusinessLogic; @@ -30,7 +32,7 @@ namespace Umbraco.Tests.Routing var routingContext = GetRoutingContext(urlAsString, template); var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docRequest = new PublishedContentRequest(url, routingContext); - var lookup = new ContentFinderByNiceUrlAndTemplate(); + var lookup = new ContentFinderByNiceUrlAndTemplate(Logger); SettingsForTests.HideTopLevelNodeFromPath = false; diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs index 8002acd0ac..b69852dcb4 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs @@ -1,6 +1,8 @@ using System; using System.Configuration; +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; @@ -30,7 +32,7 @@ namespace Umbraco.Tests.Routing var routingContext = GetRoutingContext(urlString); var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(url, routingContext); - var lookup = new ContentFinderByNiceUrl(); + var lookup = new ContentFinderByNiceUrl(Logger); SettingsForTests.HideTopLevelNodeFromPath = true; var result = lookup.TryFindContent(docreq); @@ -56,8 +58,8 @@ namespace Umbraco.Tests.Routing { var routingContext = GetRoutingContext(urlString); var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url - var docreq = new PublishedContentRequest(url, routingContext); - var lookup = new ContentFinderByNiceUrl(); + var docreq = new PublishedContentRequest(url, routingContext); + var lookup = new ContentFinderByNiceUrl(Logger); SettingsForTests.HideTopLevelNodeFromPath = false; var result = lookup.TryFindContent(docreq); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs index 15912a8c5d..1d3727ce42 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using umbraco.cms.businesslogic.web; @@ -160,7 +162,7 @@ namespace Umbraco.Tests.Routing // must lookup domain else lookup by url fails pcr.Engine.FindDomain(); - var lookup = new ContentFinderByNiceUrl(); + var lookup = new ContentFinderByNiceUrl(Logger); var result = lookup.TryFindContent(pcr); Assert.IsTrue(result); Assert.AreEqual(expectedId, pcr.PublishedContent.Id); @@ -199,7 +201,7 @@ namespace Umbraco.Tests.Routing pcr.Engine.FindDomain(); Assert.AreEqual(expectedCulture, pcr.Culture.Name); - var lookup = new ContentFinderByNiceUrl(); + var lookup = new ContentFinderByNiceUrl(Logger); var result = lookup.TryFindContent(pcr); Assert.IsTrue(result); Assert.AreEqual(expectedId, pcr.PublishedContent.Id); diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index 27b00cf676..4b7aa91dc6 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using umbraco.cms.businesslogic.web; @@ -181,8 +183,8 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(expectedCulture, pcr.Culture.Name); - SettingsForTests.HideTopLevelNodeFromPath = false; - var finder = new ContentFinderByNiceUrl(); + SettingsForTests.HideTopLevelNodeFromPath = false; + var finder = new ContentFinderByNiceUrl(Logger); var result = finder.TryFindContent(pcr); Assert.IsTrue(result); @@ -225,7 +227,7 @@ namespace Umbraco.Tests.Routing // find document SettingsForTests.HideTopLevelNodeFromPath = false; - var finder = new ContentFinderByNiceUrl(); + var finder = new ContentFinderByNiceUrl(Logger); var result = finder.TryFindContent(pcr); // apply wildcard domain diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index f1a80de06d..6f4d0e136b 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -3,6 +3,7 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; using Umbraco.Web.PublishedCache.XmlPublishedCache; using umbraco.cms.businesslogic.web; @@ -52,7 +53,7 @@ namespace Umbraco.Tests.Routing Assert.IsTrue(pcr.HasDomain); // check that it's been routed - var lookup = new ContentFinderByNiceUrl(); + var lookup = new ContentFinderByNiceUrl(Logger); var result = lookup.TryFindContent(pcr); Assert.IsTrue(result); Assert.AreEqual(100111, pcr.PublishedContent.Id); diff --git a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs index 798cc57526..0f7b4fbd41 100644 --- a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs +++ b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs @@ -51,7 +51,7 @@ namespace Umbraco.Tests.Strings [TestCase("WhoIsNumber6InTheVillage", "Who Is Number6 In The Village")] // issue is fixed public void CompatibleDefaultReplacement(string input, string expected) { - var helper = new DefaultShortStringHelper(); + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()); var output = input.Length < 2 ? input : helper.SplitPascalCasing(input, ' ').ToFirstUpperInvariant(); Assert.AreEqual(expected, output); } diff --git a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs index 04b632ffeb..fba1775f50 100644 --- a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Strings // NOTE pre-filters runs _before_ Recode takes place // so there still may be utf8 chars even though you want ascii - _helper = new DefaultShortStringHelper() + _helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.FileName, new DefaultShortStringHelper.Config { //PreFilter = ClearFileChars, // done in IsTerm @@ -131,11 +131,11 @@ namespace Umbraco.Tests.Strings const string input = "ÆØÅ and æøå and 中文测试 and אודות האתר and größer БбДдЖж page"; - var helper = new DefaultShortStringHelper().WithDefaultConfig(); // unicode + var helper = new DefaultShortStringHelper(settings).WithDefaultConfig(); // unicode var output = helper.CleanStringForUrlSegment(input); Assert.AreEqual("æøå-and-æøå-and-中文测试-and-אודות-האתר-and-größer-ббдджж-page", output); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(settings) .WithConfig(CleanStringType.UrlSegment, new DefaultShortStringHelper.Config { IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', @@ -149,7 +149,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringUnderscoreInTerm() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { // underscore is accepted within terms @@ -159,7 +159,7 @@ namespace Umbraco.Tests.Strings }); Assert.AreEqual("foo_bar*nil", helper.CleanString("foo_bar nil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { // underscore is not accepted within terms @@ -173,7 +173,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringLeadingChars() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { // letters and digits are valid leading chars @@ -183,7 +183,7 @@ namespace Umbraco.Tests.Strings }); Assert.AreEqual("0123foo*bar*543*nil*321", helper.CleanString("0123foo_bar 543 nil 321", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { // only letters are valid leading chars @@ -194,14 +194,14 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*bar*543*nil*321", helper.CleanString("0123foo_bar 543 nil 321", CleanStringType.Alias)); Assert.AreEqual("foo*bar*543*nil*321", helper.CleanString("0123 foo_bar 543 nil 321", CleanStringType.Alias)); - helper = new DefaultShortStringHelper().WithDefaultConfig(); + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()).WithDefaultConfig(); Assert.AreEqual("child2", helper.CleanStringForSafeAlias("1child2")); } [Test] public void CleanStringTermOnUpper() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -211,7 +211,7 @@ namespace Umbraco.Tests.Strings }); Assert.AreEqual("foo*Bar", helper.CleanString("fooBar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -225,7 +225,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringAcronymOnNonUpper() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -238,7 +238,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*BAnil", helper.CleanString("foo BAnil", CleanStringType.Alias)); Assert.AreEqual("foo*Bnil", helper.CleanString("foo Bnil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -255,7 +255,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringGreedyAcronyms() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -268,7 +268,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*BA*nil", helper.CleanString("foo BAnil", CleanStringType.Alias)); Assert.AreEqual("foo*Bnil", helper.CleanString("foo Bnil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -285,7 +285,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringWhiteSpace() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -298,7 +298,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringSeparator() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -306,7 +306,7 @@ namespace Umbraco.Tests.Strings }); Assert.AreEqual("foo*bar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -314,14 +314,14 @@ namespace Umbraco.Tests.Strings }); Assert.AreEqual("foo bar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged }); Assert.AreEqual("foobar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -333,7 +333,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringSymbols() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -387,7 +387,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringEncoding() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -396,7 +396,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("中文测试", helper.CleanString("中文测试", CleanStringType.Alias)); Assert.AreEqual("léger*中文测试*ZÔRG", helper.CleanString("léger 中文测试 ZÔRG", CleanStringType.Alias)); - helper = new DefaultShortStringHelper() + helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Ascii | CleanStringType.Unchanged, @@ -415,7 +415,7 @@ namespace Umbraco.Tests.Strings contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); SettingsForTests.ConfigureSettings(settings); - var helper = new DefaultShortStringHelper().WithDefaultConfig(); + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()).WithDefaultConfig(); const string input = "0123 中文测试 中文测试 léger ZÔRG (2) a?? *x"; @@ -436,7 +436,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringCasing() { - var helper = new DefaultShortStringHelper() + var helper = new DefaultShortStringHelper(SettingsForTests.GetDefault()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelper.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs index d36e49bc25..5fed998194 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs @@ -15,7 +15,7 @@ namespace Umbraco.Tests.UmbracoExamine { UmbracoExamineSearcher.DisableInitializationCheck = true; BaseUmbracoIndexer.DisableInitializationCheck = true; - ShortStringHelperResolver.Current = new ShortStringHelperResolver(new DefaultShortStringHelper()); + ShortStringHelperResolver.Current = new ShortStringHelperResolver(new DefaultShortStringHelper(SettingsForTests.GetDefault())); Resolution.Freeze(); } diff --git a/src/Umbraco.Web/LightInject/LightInjectExtensions.cs b/src/Umbraco.Web/LightInject/LightInjectExtensions.cs new file mode 100644 index 0000000000..756e689608 --- /dev/null +++ b/src/Umbraco.Web/LightInject/LightInjectExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http.Controllers; +using System.Web.Mvc; +using Umbraco.Core; +using Umbraco.Core.LightInject; + +namespace Umbraco.Web.LightInject +{ + internal static class LightInjectExtensions + { + /// + /// Registers all IControllers using the PluginManager for scanning and caching found instances for the calling assembly + /// + /// + /// + public static void RegisterMvcControllers(this IServiceRegistry container, PluginManager pluginManager) + { + var types = pluginManager.ResolveTypes(specificAssemblies: new[] { Assembly.GetCallingAssembly() }); + foreach (var type in types) + { + container.Register(type, new PerRequestLifeTime()); + } + } + + /// + /// Registers all IHttpController using the PluginManager for scanning and caching found instances for the calling assembly + /// + /// + /// + public static void RegisterApiControllers(this IServiceRegistry container, PluginManager pluginManager) + { + var types = pluginManager.ResolveTypes(specificAssemblies: new[] { Assembly.GetCallingAssembly() }); + foreach (var type in types) + { + container.Register(type, new PerRequestLifeTime()); + } + } + + } +} diff --git a/src/Umbraco.Web/LightInject/Mvc/LightInject.Mvc.cs b/src/Umbraco.Web/LightInject/Mvc/LightInject.Mvc.cs new file mode 100644 index 0000000000..e949b40d1e --- /dev/null +++ b/src/Umbraco.Web/LightInject/Mvc/LightInject.Mvc.cs @@ -0,0 +1,203 @@ +/***************************************************************************** + The MIT License (MIT) + + Copyright (c) 2014 bernhard.richter@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +****************************************************************************** + LightInject.Mvc version 1.0.0.4 + http://seesharper.github.io/LightInject/ + http://twitter.com/bernhardrichter +******************************************************************************/ + +using Umbraco.Core.LightInject; + +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "No inheritance")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Single source file deployment.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1403:FileMayOnlyContainASingleNamespace", Justification = "Extension methods must be visible")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Custom header.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "All public members are documented.")] + +namespace Umbraco.Web.LightInject +{ + using System.Linq; + using System.Reflection; + using System.Web.Mvc; + using LightInject.Mvc; + + /// + /// Extends the interface with a method that + /// enables dependency injection in an ASP.NET MVC application. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class MvcContainerExtensions + { + /// + /// Registers all implementations found in the given . + /// + /// The target . + /// A list of assemblies from which to register implementations. + public static void RegisterControllers(this IServiceRegistry serviceRegistry, params Assembly[] assemblies) + { + foreach (var assembly in assemblies) + { + var controllerTypes = + assembly.GetTypes().Where(t => !t.IsAbstract && typeof(IController).IsAssignableFrom(t)); + foreach (var controllerType in controllerTypes) + { + serviceRegistry.Register(controllerType, new PerRequestLifeTime()); + } + } + } + + /// + /// Registers all implementations found in this assembly. + /// + /// The target . + public static void RegisterControllers(this IServiceRegistry serviceRegistry) + { + RegisterControllers(serviceRegistry, Assembly.GetCallingAssembly()); + } + + /// + /// Enables dependency injection in an ASP.NET MVC application. + /// + /// The target . + public static void EnableMvc(this IServiceContainer serviceContainer) + { + ((ServiceContainer)serviceContainer).EnablePerWebRequestScope(); + SetDependencyResolver(serviceContainer); + InitializeFilterAttributeProvider(serviceContainer); + } + + private static void SetDependencyResolver(IServiceContainer serviceContainer) + { + DependencyResolver.SetResolver(new LightInjectMvcDependencyResolver(serviceContainer)); + } + + private static void InitializeFilterAttributeProvider(IServiceContainer serviceContainer) + { + RemoveExistingFilterAttributeFilterProviders(); + var filterProvider = new LightInjectFilterProvider(serviceContainer); + FilterProviders.Providers.Add(filterProvider); + } + + private static void RemoveExistingFilterAttributeFilterProviders() + { + var existingFilterAttributeProviders = + FilterProviders.Providers.OfType().ToArray(); + foreach (FilterAttributeFilterProvider filterAttributeFilterProvider in existingFilterAttributeProviders) + { + FilterProviders.Providers.Remove(filterAttributeFilterProvider); + } + } + } +} + +namespace Umbraco.Web.LightInject.Mvc +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web.Mvc; + + /// + /// An adapter for the LightInject service container. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class LightInjectMvcDependencyResolver : IDependencyResolver + { + private readonly IServiceContainer serviceContainer; + + /// + /// Initializes a new instance of the class. + /// + /// The instance to + /// be used for resolving service instances. + internal LightInjectMvcDependencyResolver(IServiceContainer serviceContainer) + { + this.serviceContainer = serviceContainer; + } + + /// + /// Resolves singly registered services that support arbitrary object creation. + /// + /// + /// The requested service or object. + /// + /// The type of the requested service or object. + public object GetService(Type serviceType) + { + return serviceContainer.TryGetInstance(serviceType); + } + + /// + /// Resolves multiply registered services. + /// + /// + /// The requested services. + /// + /// The type of the requested services. + public IEnumerable GetServices(Type serviceType) + { + return serviceContainer.GetAllInstances(serviceType); + } + } + + /// + /// A that uses an + /// to inject property dependencies into instances. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class LightInjectFilterProvider : FilterAttributeFilterProvider + { + private readonly IServiceContainer serviceContainer; + + /// + /// Initializes a new instance of the class. + /// + /// The instance + /// used to inject property dependencies. + public LightInjectFilterProvider(IServiceContainer serviceContainer) + { + this.serviceContainer = serviceContainer; + serviceContainer.RegisterInstance(this); + } + + /// + /// Aggregates the filters from all of the filter providers into one collection. + /// + /// + /// The collection filters from all of the filter providers. + /// + /// The controller context. + /// The action descriptor. + public override IEnumerable GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) + { + var filters = base.GetFilters(controllerContext, actionDescriptor).ToArray(); + foreach (var filter in filters) + { + serviceContainer.InjectProperties(filter.Instance); + } + + return filters; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/LightInject/Web/LightInject.Web.cs b/src/Umbraco.Web/LightInject/Web/LightInject.Web.cs new file mode 100644 index 0000000000..c3c06b6003 --- /dev/null +++ b/src/Umbraco.Web/LightInject/Web/LightInject.Web.cs @@ -0,0 +1,164 @@ +/***************************************************************************** + The MIT License (MIT) + + Copyright (c) 2014 bernhard.richter@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +****************************************************************************** + LightInject.Web version 1.0.0.7 + http://seesharper.github.io/LightInject/ + http://twitter.com/bernhardrichter +******************************************************************************/ + +using Umbraco.Core.LightInject; + +[assembly: System.Web.PreApplicationStartMethod(typeof(Umbraco.Web.LightInject.Web.LightInjectHttpModuleInitializer), "Initialize")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "No inheritance")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Single source file deployment.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1403:FileMayOnlyContainASingleNamespace", Justification = "Extension methods must be visible")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Custom header.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "All public members are documented.")] + +namespace Umbraco.Web.LightInject +{ + using Web; + + /// + /// Extends the interface with a method + /// to enable services that are scoped per web request. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class WebContainerExtensions + { + /// + /// Ensures that services registered with the is properly + /// disposed when the web request ends. + /// + /// The target . + public static void EnablePerWebRequestScope(this ServiceContainer serviceContainer) + { + serviceContainer.ScopeManagerProvider = new PerWebRequestScopeManagerProvider(); + } + } +} + +namespace Umbraco.Web.LightInject.Web +{ + using System; + using System.Web; + using Microsoft.Web.Infrastructure.DynamicModuleHelper; + + /// + /// Registers the with the current . + /// + public static class LightInjectHttpModuleInitializer + { + private static bool isInitialized; + + /// + /// Executed before the is started and registers + /// the with the current . + /// + public static void Initialize() + { + if (!isInitialized) + { + isInitialized = true; + DynamicModuleUtility.RegisterModule(typeof(LightInjectHttpModule)); + } + } + } + + /// + /// A that ensures that services registered + /// with the lifetime is scoped per web request. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public class LightInjectHttpModule : IHttpModule + { + /// + /// Initializes a module and prepares it to handle requests. + /// + /// An that provides access to the methods, properties, and events common to all application objects within an ASP.NET application + public void Init(HttpApplication context) + { + context.BeginRequest += BeginRequest; + context.EndRequest += EndRequest; + } + + /// + /// Disposes of the resources (other than memory) used by the module that implements . + /// + public void Dispose() + { + } + + private static void EndRequest(object sender, EventArgs eventArgs) + { + var application = sender as HttpApplication; + if (application == null) + { + return; + } + + var scopeManager = (ScopeManager)application.Context.Items["ScopeManager"]; + if (scopeManager != null) + { + scopeManager.CurrentScope.Dispose(); + } + } + + private static void BeginRequest(object sender, EventArgs eventArgs) + { + var application = sender as HttpApplication; + if (application == null) + { + return; + } + + var scopeManager = new ScopeManager(); + scopeManager.BeginScope(); + application.Context.Items["ScopeManager"] = scopeManager; + } + } + + /// + /// An that provides the + /// used by the current . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class PerWebRequestScopeManagerProvider : IScopeManagerProvider + { + /// + /// Returns the that is responsible for managing scopes. + /// + /// The that is responsible for managing scopes. + public ScopeManager GetScopeManager() + { + var scopeManager = (ScopeManager)HttpContext.Current.Items["ScopeManager"]; + if (scopeManager == null) + { + throw new InvalidOperationException("Unable to locate a scope manager for the current HttpRequest. Ensure that the LightInjectHttpModule has been added."); + } + + return scopeManager; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/LightInject/WebApi/LightInject.WebApi.cs b/src/Umbraco.Web/LightInject/WebApi/LightInject.WebApi.cs new file mode 100644 index 0000000000..fba4464d17 --- /dev/null +++ b/src/Umbraco.Web/LightInject/WebApi/LightInject.WebApi.cs @@ -0,0 +1,235 @@ +/***************************************************************************** + The MIT License (MIT) + + Copyright (c) 2014 bernhard.richter@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +****************************************************************************** + LightInject.WebApi version 1.0.0.3 + http://www.lightinject.net/ + http://twitter.com/bernhardrichter +******************************************************************************/ + +using Umbraco.Core.LightInject; + +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "No inheritance")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Single source file deployment.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1403:FileMayOnlyContainASingleNamespace", Justification = "Extension methods must be visible")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:FileMustHaveHeader", Justification = "Custom header.")] +[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "All public members are documented.")] + +namespace Umbraco.Web.LightInject +{ + using System.Linq; + using System.Reflection; + using System.Web.Http; + using System.Web.Http.Controllers; + using System.Web.Http.Filters; + using LightInject.WebApi; + + /// + /// Extends the interface with methods that + /// enables dependency injection in a Web API application. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class WebApiContainerExtensions + { + /// + /// Enables dependency injection in a Web API application. + /// + /// The target . + /// The that represents the configuration of this Web API application. + public static void EnableWebApi(this IServiceContainer serviceContainer, HttpConfiguration httpConfiguration) + { + httpConfiguration.DependencyResolver = new LightInjectWebApiDependencyResolver(serviceContainer); + var provider = httpConfiguration.Services.GetFilterProviders(); + httpConfiguration.Services.RemoveAll(typeof(IFilterProvider), o => true); + httpConfiguration.Services.Add(typeof(IFilterProvider), new LightInjectWebApiFilterProvider(serviceContainer, provider)); + } + + /// + /// Registers all implementations found in the given . + /// + /// The target . + /// A list of assemblies from which to register implementations. + public static void RegisterApiControllers(this IServiceRegistry serviceRegistry, params Assembly[] assemblies) + { + foreach (var assembly in assemblies) + { + var controllerTypes = assembly.GetTypes().Where(t => !t.IsAbstract && typeof(IHttpController).IsAssignableFrom(t)); + foreach (var controllerType in controllerTypes) + { + serviceRegistry.Register(controllerType, new PerRequestLifeTime()); + } + } + } + + /// + /// Registers all implementations found in this assembly. + /// + /// The target . + public static void RegisterApiControllers(this IServiceRegistry serviceRegistry) + { + RegisterApiControllers(serviceRegistry, Assembly.GetCallingAssembly()); + } + } +} + +namespace Umbraco.Web.LightInject.WebApi +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web.Http; + using System.Web.Http.Controllers; + using System.Web.Http.Dependencies; + using System.Web.Http.Filters; + + /// + /// An adapter for the LightInject service container + /// that enables instances and their dependencies to be + /// resolved through the service container. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class LightInjectWebApiDependencyResolver : IDependencyResolver + { + private readonly IServiceContainer serviceContainer; + + /// + /// Initializes a new instance of the class. + /// + /// The instance to + /// be used for resolving service instances. + internal LightInjectWebApiDependencyResolver(IServiceContainer serviceContainer) + { + this.serviceContainer = serviceContainer; + } + + /// + /// Disposes the underlying . + /// + public void Dispose() + { + serviceContainer.Dispose(); + } + + /// + /// Gets an instance of the given . + /// + /// The type of the requested service. + /// The requested service instance if available, otherwise null. + public object GetService(Type serviceType) + { + return serviceContainer.TryGetInstance(serviceType); + } + + /// + /// Gets all instance of the given . + /// + /// The type of services to resolve. + /// A list that contains all implementations of the . + public IEnumerable GetServices(Type serviceType) + { + return serviceContainer.GetAllInstances(serviceType); + } + + /// + /// Starts a new that represents + /// the scope for services registered with . + /// + /// + /// A new . + /// + public IDependencyScope BeginScope() + { + return new LightInjectWebApiDependencyScope(serviceContainer, serviceContainer.BeginScope()); + } + } + + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class LightInjectWebApiDependencyScope : IDependencyScope + { + private readonly IServiceContainer serviceContainer; + private readonly Scope scope; + + public LightInjectWebApiDependencyScope(IServiceContainer serviceContainer, Scope scope) + { + this.serviceContainer = serviceContainer; + this.scope = scope; + } + + public object GetService(Type serviceType) + { + return serviceContainer.GetInstance(serviceType); + } + + public IEnumerable GetServices(Type serviceType) + { + return serviceContainer.GetAllInstances(serviceType); + } + + public void Dispose() + { + scope.Dispose(); + } + } + + /// + /// A that uses an + /// to inject property dependencies into instances. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal class LightInjectWebApiFilterProvider : IFilterProvider + { + private readonly IServiceContainer serviceContainer; + private readonly IEnumerable filterProviders; + + /// + /// Initializes a new instance of the class. + /// + /// The instance + /// used to inject property dependencies. + /// The list of existing filter providers. + public LightInjectWebApiFilterProvider(IServiceContainer serviceContainer, IEnumerable filterProviders) + { + this.serviceContainer = serviceContainer; + this.filterProviders = filterProviders; + } + + /// + /// Returns an enumeration of filters. + /// + /// + /// An enumeration of filters. + /// + /// The HTTP configuration.The action descriptor. + public IEnumerable GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) + { + var filters = filterProviders.SelectMany(p => p.GetFilters(configuration, actionDescriptor)).ToArray(); + + foreach (var filterInfo in filters) + { + serviceContainer.InjectProperties(filterInfo.Instance); + } + + return filters; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs b/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs index a2777c0137..2e59eece7f 100644 --- a/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs +++ b/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs @@ -12,8 +12,15 @@ namespace Umbraco.Web.Media.ThumbnailProviders { [WeightedPlugin(1000)] public class ImageThumbnailProvider : AbstractThumbnailProvider - { - protected override IEnumerable SupportedExtensions + { + private readonly MediaFileSystem _mediaFileSystem; + + public ImageThumbnailProvider(MediaFileSystem mediaFileSystem) + { + _mediaFileSystem = mediaFileSystem; + } + + protected override IEnumerable SupportedExtensions { get { return new List { ".jpeg", ".jpg", ".gif", ".bmp", ".png", ".tiff", ".tif" }; } } @@ -37,7 +44,7 @@ namespace Umbraco.Web.Media.ThumbnailProviders try { - var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + var fs = _mediaFileSystem; var relativeThumbPath = fs.GetRelativePath(tmpThumbUrl); if (!fs.FileExists(relativeThumbPath)) return false; diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProvidersResolver.cs b/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProvidersResolver.cs index a27bbe7f8e..b6007dcc14 100644 --- a/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProvidersResolver.cs +++ b/src/Umbraco.Web/Media/ThumbnailProviders/ThumbnailProvidersResolver.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Web; using Umbraco.Core; +using Umbraco.Core.LightInject; using Umbraco.Core.Logging; using Umbraco.Core.Media; using Umbraco.Core.ObjectResolution; @@ -12,16 +13,16 @@ using umbraco.BusinessLogic.Utils; namespace Umbraco.Web.Media.ThumbnailProviders { - internal sealed class ThumbnailProvidersResolver : ManyObjectsResolverBase + internal sealed class ThumbnailProvidersResolver : ContainerManyObjectsResolver { /// /// Constructor /// - /// + /// /// /// - internal ThumbnailProvidersResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable providers) - : base(serviceProvider, logger, providers) + internal ThumbnailProvidersResolver(IServiceContainer container, ILogger logger, IEnumerable providers) + : base(container, logger, providers) { } diff --git a/src/Umbraco.Web/Mvc/DefaultRenderMvcControllerResolver.cs b/src/Umbraco.Web/Mvc/DefaultRenderMvcControllerResolver.cs index 715fc674a7..7e9313289c 100644 --- a/src/Umbraco.Web/Mvc/DefaultRenderMvcControllerResolver.cs +++ b/src/Umbraco.Web/Mvc/DefaultRenderMvcControllerResolver.cs @@ -33,21 +33,6 @@ namespace Umbraco.Web.Mvc return Value; } - /// - /// Returns an instance of the default controller instance. - /// - public IRenderMvcController GetControllerInstance() - { - //try the dependency resolver, then the activator - var instance = DependencyResolver.Current.GetService(Value) ?? Activator.CreateInstance(Value); - var result = instance as IRenderMvcController; - if (result == null) - { - throw new InvalidOperationException("Could not create an instance of " + Value + " for the default " + typeof(IRenderMvcController).Name); - } - return result; - } - /// /// Sets the default RenderMvcController type /// diff --git a/src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs b/src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs index d060ac554b..22e9ea2e65 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCachesResolver.cs @@ -1,13 +1,27 @@ +using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; using Umbraco.Core.ObjectResolution; namespace Umbraco.Web.PublishedCache { + //TODO: REmove this requirement, just use normal IoC and publicize IPublishedCaches + /// /// Resolves the IPublishedCaches object. /// - internal sealed class PublishedCachesResolver : SingleObjectResolverBase + internal sealed class PublishedCachesResolver : ContainerSingleObjectResolver { - /// + /// + /// Initializes the resolver to use IoC + /// + /// + /// + public PublishedCachesResolver(IServiceContainer container, Type implementationType) : base(container, implementationType) + { + } + + /// /// Initializes a new instance of the class with caches. /// /// The caches. @@ -16,7 +30,16 @@ namespace Umbraco.Web.PublishedCache : base(caches) { } - /// + /// + /// Initializes the resolver to use IoC + /// + /// + /// + public PublishedCachesResolver(IServiceContainer container, Expression> implementationType) : base(container, implementationType) + { + } + + /// /// Sets the caches. /// /// The caches. diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index f3b133a645..683230f06f 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -22,6 +22,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { internal class PublishedContentCache : IPublishedContentCache { + public PublishedContentCache() + { + } + #region Routes cache private readonly RoutesCache _routesCache = new RoutesCache(!UnitTesting); diff --git a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs index 9bae24c5dd..5188993cb5 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByIdPath.cs @@ -13,7 +13,14 @@ namespace Umbraco.Web.Routing /// public class ContentFinderByIdPath : IContentFinder { - /// + private readonly ILogger _logger; + + public ContentFinderByIdPath(ILogger logger) + { + _logger = logger; + } + + /// /// Tries to find and assign an Umbraco document to a PublishedContentRequest. /// /// The PublishedContentRequest. @@ -33,13 +40,13 @@ namespace Umbraco.Web.Routing if (nodeId > 0) { - LogHelper.Debug("Id={0}", () => nodeId); + _logger.Debug("Id={0}", () => nodeId); node = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(nodeId); if (node != null) { docRequest.PublishedContent = node; - LogHelper.Debug("Found node with id={0}", () => docRequest.PublishedContent.Id); + _logger.Debug("Found node with id={0}", () => docRequest.PublishedContent.Id); } else { @@ -49,7 +56,7 @@ namespace Umbraco.Web.Routing } if (nodeId == -1) - LogHelper.Debug("Not a node id"); + _logger.Debug("Not a node id"); return node != null; } diff --git a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs index 1181ac3195..d6648094d6 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs @@ -8,6 +8,13 @@ namespace Umbraco.Web.Routing /// public class ContentFinderByLegacy404 : IContentFinder { + private readonly ILogger _logger; + + public ContentFinderByLegacy404(ILogger logger) + { + _logger = logger; + } + /// /// Tries to find and assign an Umbraco document to a PublishedContentRequest. /// @@ -15,7 +22,7 @@ namespace Umbraco.Web.Routing /// A value indicating whether an Umbraco document was found and assigned. public bool TryFindContent(PublishedContentRequest pcr) { - LogHelper.Debug("Looking for a page to handle 404."); + _logger.Debug("Looking for a page to handle 404."); // TODO - replace the whole logic and stop calling into library! var error404 = global::umbraco.library.GetCurrentNotFoundPageId(); @@ -25,17 +32,17 @@ namespace Umbraco.Web.Routing if (id > 0) { - LogHelper.Debug("Got id={0}.", () => id); + _logger.Debug("Got id={0}.", () => id); content = pcr.RoutingContext.UmbracoContext.ContentCache.GetById(id); - LogHelper.Debug(content == null + _logger.Debug(content == null ? "Could not find content with that id." : "Found corresponding content."); } else { - LogHelper.Debug("Got nothing."); + _logger.Debug("Got nothing."); } pcr.PublishedContent = content; diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs index 529d027ea1..b3377a0078 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs @@ -12,6 +12,13 @@ namespace Umbraco.Web.Routing /// public class ContentFinderByNiceUrl : IContentFinder { + protected ILogger Logger { get; private set; } + + public ContentFinderByNiceUrl(ILogger logger) + { + Logger = logger; + } + /// /// Tries to find and assign an Umbraco document to a PublishedContentRequest. /// @@ -21,7 +28,7 @@ namespace Umbraco.Web.Routing { string route; if (docRequest.HasDomain) - route = docRequest.Domain.RootNodeId.ToString() + DomainHelper.PathRelativeToDomain(docRequest.DomainUri, docRequest.Uri.GetAbsolutePathDecoded()); + route = docRequest.Domain.RootNodeId + DomainHelper.PathRelativeToDomain(docRequest.DomainUri, docRequest.Uri.GetAbsolutePathDecoded()); else route = docRequest.Uri.GetAbsolutePathDecoded(); @@ -37,17 +44,17 @@ namespace Umbraco.Web.Routing /// The document node, or null. protected IPublishedContent FindContent(PublishedContentRequest docreq, string route) { - LogHelper.Debug("Test route \"{0}\"", () => route); + Logger.Debug("Test route \"{0}\"", () => route); var node = docreq.RoutingContext.UmbracoContext.ContentCache.GetByRoute(route); if (node != null) { docreq.PublishedContent = node; - LogHelper.Debug("Got content, id={0}", () => node.Id); + Logger.Debug("Got content, id={0}", () => node.Id); } else { - LogHelper.Debug("No match."); + Logger.Debug("No match."); } return node; diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs index 21fd81a8e4..e547d3a3a2 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs @@ -4,54 +4,59 @@ using Umbraco.Core; namespace Umbraco.Web.Routing { - /// - /// Provides an implementation of that handles page nice urls and a template. - /// - /// - /// Handles /foo/bar/template where /foo/bar is the nice url of a document, and template a template alias. - /// If successful, then the template of the document request is also assigned. - /// + /// + /// Provides an implementation of that handles page nice urls and a template. + /// + /// + /// Handles /foo/bar/template where /foo/bar is the nice url of a document, and template a template alias. + /// If successful, then the template of the document request is also assigned. + /// public class ContentFinderByNiceUrlAndTemplate : ContentFinderByNiceUrl { - /// - /// Tries to find and assign an Umbraco document to a PublishedContentRequest. - /// - /// The PublishedContentRequest. - /// A value indicating whether an Umbraco document was found and assigned. - /// If successful, also assigns the template. - public override bool TryFindContent(PublishedContentRequest docRequest) + public ContentFinderByNiceUrlAndTemplate(ILogger logger) + : base(logger) + { + } + + /// + /// Tries to find and assign an Umbraco document to a PublishedContentRequest. + /// + /// The PublishedContentRequest. + /// A value indicating whether an Umbraco document was found and assigned. + /// If successful, also assigns the template. + public override bool TryFindContent(PublishedContentRequest docRequest) { IPublishedContent node = null; - string path = docRequest.Uri.GetAbsolutePathDecoded(); + string path = docRequest.Uri.GetAbsolutePathDecoded(); - if (docRequest.HasDomain) - path = DomainHelper.PathRelativeToDomain(docRequest.DomainUri, path); + if (docRequest.HasDomain) + path = DomainHelper.PathRelativeToDomain(docRequest.DomainUri, path); - if (path != "/") // no template if "/" + if (path != "/") // no template if "/" { - var pos = path.LastIndexOf('/'); - var templateAlias = path.Substring(pos + 1); - path = pos == 0 ? "/" : path.Substring(0, pos); + var pos = path.LastIndexOf('/'); + var templateAlias = path.Substring(pos + 1); + path = pos == 0 ? "/" : path.Substring(0, pos); var template = ApplicationContext.Current.Services.FileService.GetTemplate(templateAlias); if (template != null) { - LogHelper.Debug("Valid template: \"{0}\"", () => templateAlias); + Logger.Debug("Valid template: \"{0}\"", () => templateAlias); - var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; - node = FindContent(docRequest, route); + var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; + node = FindContent(docRequest, route); if (node != null) - docRequest.TemplateModel = template; + docRequest.TemplateModel = template; } else { - LogHelper.Debug("Not a valid template: \"{0}\"", () => templateAlias); + Logger.Debug("Not a valid template: \"{0}\"", () => templateAlias); } } else { - LogHelper.Debug("No template in path \"/\""); + Logger.Debug("No template in path \"/\""); } return node != null; diff --git a/src/Umbraco.Web/Routing/ContentFinderByProfile.cs b/src/Umbraco.Web/Routing/ContentFinderByProfile.cs index 4a9034460a..d2fb3c2ce8 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByProfile.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByProfile.cs @@ -5,56 +5,61 @@ using Umbraco.Core; namespace Umbraco.Web.Routing { - /// - /// Provides an implementation of that handles profiles. - /// - /// - /// Handles /profile/login where /profile is the profile page nice url and login the login of a member. - /// This should rather be done with a rewriting rule. There would be multiple profile pages in multi-sites/multi-langs setups. - /// We keep it for backward compatility reasons. - /// + /// + /// Provides an implementation of that handles profiles. + /// + /// + /// Handles /profile/login where /profile is the profile page nice url and login the login of a member. + /// This should rather be done with a rewriting rule. There would be multiple profile pages in multi-sites/multi-langs setups. + /// We keep it for backward compatility reasons. + /// public class ContentFinderByProfile : ContentFinderByNiceUrl { - /// - /// Tries to find and assign an Umbraco document to a PublishedContentRequest. - /// - /// The PublishedContentRequest. - /// A value indicating whether an Umbraco document was found and assigned. - public override bool TryFindContent(PublishedContentRequest docRequest) + public ContentFinderByProfile(ILogger logger) + : base(logger) + { + } + + /// + /// Tries to find and assign an Umbraco document to a PublishedContentRequest. + /// + /// The PublishedContentRequest. + /// A value indicating whether an Umbraco document was found and assigned. + public override bool TryFindContent(PublishedContentRequest docRequest) { IPublishedContent node = null; - var path = docRequest.Uri.GetAbsolutePathDecoded(); + var path = docRequest.Uri.GetAbsolutePathDecoded(); bool isProfile = false; - var pos = path.LastIndexOf('/'); + var pos = path.LastIndexOf('/'); if (pos > 0) { - var memberLogin = path.Substring(pos + 1); - path = path.Substring(0, pos); + var memberLogin = path.Substring(pos + 1); + path = path.Substring(0, pos); if (path == GlobalSettings.ProfileUrl) { isProfile = true; - LogHelper.Debug("Path \"{0}\" is the profile path", () => path); + Logger.Debug("Path \"{0}\" is the profile path", () => path); - var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; - node = FindContent(docRequest, route); + var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; + node = FindContent(docRequest, route); if (node != null) { - //TODO: Should be handled by Context Items class manager (http://issues.umbraco.org/issue/U4-61) - docRequest.RoutingContext.UmbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin; - } + //TODO: Should be handled by Context Items class manager (http://issues.umbraco.org/issue/U4-61) + docRequest.RoutingContext.UmbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin; + } else { - LogHelper.Debug("No document matching profile path?"); + Logger.Debug("No document matching profile path?"); } } } if (!isProfile) { - LogHelper.Debug("Not the profile path"); + Logger.Debug("Not the profile path"); } return node != null; diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs index e0121a4b3c..dcd96df526 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs @@ -19,6 +19,13 @@ namespace Umbraco.Web.Routing /// public class ContentFinderByUrlAlias : IContentFinder { + protected ILogger Logger { get; private set; } + + public ContentFinderByUrlAlias(ILogger logger) + { + Logger = logger; + } + /// /// Tries to find and assign an Umbraco document to a PublishedContentRequest. /// @@ -37,7 +44,7 @@ namespace Umbraco.Web.Routing if (node != null) { docRequest.PublishedContent = node; - LogHelper.Debug("Path \"{0}\" is an alias for id={1}", () => docRequest.Uri.AbsolutePath, () => docRequest.PublishedContent.Id); + Logger.Debug("Path \"{0}\" is an alias for id={1}", () => docRequest.Uri.AbsolutePath, () => docRequest.PublishedContent.Id); } } diff --git a/src/Umbraco.Web/Routing/ContentFinderResolver.cs b/src/Umbraco.Web/Routing/ContentFinderResolver.cs index b069f86315..e629566dbd 100644 --- a/src/Umbraco.Web/Routing/ContentFinderResolver.cs +++ b/src/Umbraco.Web/Routing/ContentFinderResolver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Core.LightInject; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; @@ -8,17 +9,17 @@ namespace Umbraco.Web.Routing /// /// Resolves IPublishedContentFinder objects. /// - public sealed class ContentFinderResolver : ManyObjectsResolverBase + public sealed class ContentFinderResolver : ContainerManyObjectsResolver { /// /// Initializes a new instance of the class with an initial list of finder types. /// - /// + /// /// /// The list of finder types /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal ContentFinderResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable finders) - : base(serviceProvider, logger, finders) + internal ContentFinderResolver(IServiceContainer container, ILogger logger, IEnumerable finders) + : base(container, logger, finders) { } /// @@ -26,10 +27,10 @@ namespace Umbraco.Web.Routing /// /// /// The list of finder types - /// + /// /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal ContentFinderResolver(IServiceProvider serviceProvider, ILogger logger, params Type[] finders) - : base(serviceProvider, logger, finders) + internal ContentFinderResolver(IServiceContainer container, ILogger logger, params Type[] finders) + : base(container, logger, finders) { } /// diff --git a/src/Umbraco.Web/Routing/ContentLastChanceFinderResolver.cs b/src/Umbraco.Web/Routing/ContentLastChanceFinderResolver.cs index 7e1faf6daf..9d3e06ac6f 100644 --- a/src/Umbraco.Web/Routing/ContentLastChanceFinderResolver.cs +++ b/src/Umbraco.Web/Routing/ContentLastChanceFinderResolver.cs @@ -1,20 +1,33 @@ +using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; using Umbraco.Core.ObjectResolution; namespace Umbraco.Web.Routing { - /// + /// /// Resolves the last chance IPublishedContentFinder object. - /// - public sealed class ContentLastChanceFinderResolver : SingleObjectResolverBase - { - /// - /// Initializes a new instance of the class with no finder. - /// - /// The resolver is created by the WebBootManager and thus the constructor remains internal. - internal ContentLastChanceFinderResolver() - : base(true) - { } + /// + public sealed class ContentLastChanceFinderResolver : ContainerSingleObjectResolver + { + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal ContentLastChanceFinderResolver(IServiceContainer container, Type implementationType) + : base(container, implementationType) + { + } + + /// + /// Initializes a new instance of the class with no finder. + /// + /// The resolver is created by the WebBootManager and thus the constructor remains internal. + internal ContentLastChanceFinderResolver() + : base(true) + { } /// /// Initializes a new instance of the class with an instance of a finder. @@ -24,24 +37,34 @@ namespace Umbraco.Web.Routing internal ContentLastChanceFinderResolver(IContentFinder finder) : base(finder, true) { } - + /// - /// Sets the last chance finder. - /// - /// The finder. - /// For developers, at application startup. - public void SetFinder(IContentFinder finder) - { - Value = finder; - } + /// Initializes the resolver to use IoC + /// + /// + /// + internal ContentLastChanceFinderResolver(IServiceContainer container, Expression> implementationType) + : base(container, implementationType) + { + } - /// - /// Gets the last chance finder. - /// - public IContentFinder Finder - { - get { return Value; } - } + /// + /// Sets the last chance finder. + /// + /// The finder. + /// For developers, at application startup. + public void SetFinder(IContentFinder finder) + { + Value = finder; + } - } + /// + /// Gets the last chance finder. + /// + public IContentFinder Finder + { + get { return Value; } + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/SiteDomainHelperResolver.cs b/src/Umbraco.Web/Routing/SiteDomainHelperResolver.cs index daf1d932d8..34fe2c7718 100644 --- a/src/Umbraco.Web/Routing/SiteDomainHelperResolver.cs +++ b/src/Umbraco.Web/Routing/SiteDomainHelperResolver.cs @@ -1,38 +1,59 @@ using System; +using System.Linq.Expressions; +using Umbraco.Core.LightInject; using Umbraco.Core.ObjectResolution; namespace Umbraco.Web.Routing { - /// - /// Resolves the implementation. - /// - public sealed class SiteDomainHelperResolver : SingleObjectResolverBase - { - - /// + /// + /// Resolves the implementation. + /// + public sealed class SiteDomainHelperResolver : ContainerSingleObjectResolver + { + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal SiteDomainHelperResolver(IServiceContainer container, Type implementationType) + : base(container, implementationType) + { + } + + /// /// Initializes a new instance of the class with an implementation. - /// + /// /// The implementation. internal SiteDomainHelperResolver(ISiteDomainHelper helper) - : base(helper) - { } + : base(helper) + { } - /// + /// + /// Initializes the resolver to use IoC + /// + /// + /// + internal SiteDomainHelperResolver(IServiceContainer container, Expression> implementationType) + : base(container, implementationType) + { + } + + /// /// Can be used by developers at runtime to set their IDomainHelper at app startup - /// + /// /// - public void SetHelper(ISiteDomainHelper helper) - { - Value = helper; - } + public void SetHelper(ISiteDomainHelper helper) + { + Value = helper; + } - /// + /// /// Gets or sets the implementation. - /// + /// public ISiteDomainHelper Helper - { - get { return Value; } - } - } + { + get { return Value; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Scheduling/Scheduler.cs b/src/Umbraco.Web/Scheduling/Scheduler.cs index cf2b366e40..5ab0760100 100644 --- a/src/Umbraco.Web/Scheduling/Scheduler.cs +++ b/src/Umbraco.Web/Scheduling/Scheduler.cs @@ -27,7 +27,12 @@ namespace Umbraco.Web.Scheduling private static volatile bool _started = false; private static readonly object Locker = new object(); - protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) + /// + /// Overridable method to execute when the ApplicationContext is created and other static objects that require initialization have been setup + /// + /// + /// + protected override void ApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { if (umbracoApplication.Context == null) return; diff --git a/src/Umbraco.Web/Standalone/StandaloneBootManager.cs b/src/Umbraco.Web/Standalone/StandaloneBootManager.cs index a5393c9870..52d2acaf20 100644 --- a/src/Umbraco.Web/Standalone/StandaloneBootManager.cs +++ b/src/Umbraco.Web/Standalone/StandaloneBootManager.cs @@ -39,14 +39,14 @@ namespace Umbraco.Web.Standalone //var editorControlsAssemblyName = typeof(uploadField).Assembly.FullName; } - protected override void InitializeApplicationEventsResolver() - { - base.InitializeApplicationEventsResolver(); - foreach (var type in _handlersToAdd) - ApplicationEventsResolver.Current.AddType(type); - foreach (var type in _handlersToRemove) - ApplicationEventsResolver.Current.RemoveType(type); - } + //protected override void InitializeApplicationEventsResolver() + //{ + // base.InitializeApplicationEventsResolver(); + // foreach (var type in _handlersToAdd) + // ApplicationEventsResolver.Current.AddType(type); + // foreach (var type in _handlersToRemove) + // ApplicationEventsResolver.Current.RemoveType(type); + //} protected override void InitializeResolvers() { @@ -63,7 +63,7 @@ namespace Umbraco.Web.Standalone ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver(); ContentFinderResolver.Current = new ContentFinderResolver( - ServiceProvider, LoggerResolver.Current.Logger, + Container, LoggerResolver.Current.Logger, typeof (ContentFinderByPageIdQuery), typeof (ContentFinderByNiceUrl), typeof (ContentFinderByIdPath) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 9e57a14d11..7b326a40e8 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -264,6 +264,10 @@ + + + + diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 37de741007..6fa4b91691 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -23,28 +23,6 @@ namespace Umbraco.Web /// public class UmbracoApplication : UmbracoApplicationBase { - private ManifestWatcher _mw; - - protected override void OnApplicationStarted(object sender, EventArgs e) - { - base.OnApplicationStarted(sender, e); - - if (ApplicationContext.Current.IsConfigured && GlobalSettings.DebugMode) - { - _mw = new ManifestWatcher(LoggerResolver.Current.Logger); - _mw.Start(Directory.GetDirectories(IOHelper.MapPath("~/App_Plugins/"))); - } - } - - protected override void OnApplicationEnd(object sender, EventArgs e) - { - base.OnApplicationEnd(sender, e); - - if (_mw != null) - { - _mw.Dispose(); - } - } protected override IBootManager GetBootManager() { diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 1d11991664..55cfcfc3df 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -14,6 +14,7 @@ using Examine; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; +using Umbraco.Core.LightInject; using Umbraco.Core.Logging; using Umbraco.Core.Macros; using Umbraco.Core.ObjectResolution; @@ -23,6 +24,7 @@ using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Sync; using Umbraco.Web.Dictionary; using Umbraco.Web.Install; +using Umbraco.Web.LightInject; using Umbraco.Web.Macros; using Umbraco.Web.Media; using Umbraco.Web.Media.ThumbnailProviders; @@ -31,6 +33,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.PropertyEditors; using Umbraco.Web.PropertyEditors.ValueConverters; using Umbraco.Web.PublishedCache; +using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; using Umbraco.Web.Scheduling; @@ -38,6 +41,8 @@ using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; using umbraco.BusinessLogic; using ProfilingViewEngine = Umbraco.Core.Profiling.ProfilingViewEngine; +using TypeHelper = Umbraco.Core.TypeHelper; +using Umbraco.Core.LightInject; namespace Umbraco.Web @@ -49,7 +54,7 @@ namespace Umbraco.Web { private readonly bool _isForTesting; //NOTE: see the Initialize method for what this is used for - private List _indexesToRebuild = new List(); + private readonly List _indexesToRebuild = new List(); public WebBootManager(UmbracoApplicationBase umbracoApplication) : this(umbracoApplication, false) @@ -74,7 +79,7 @@ namespace Umbraco.Web /// public override IBootManager Initialize() { - //This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976 + //This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976 // when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's // event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build // it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the @@ -83,6 +88,9 @@ namespace Umbraco.Web base.Initialize(); + //setup mvc and webapi services + SetupMvcAndWebApi(); + // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK] ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency"; ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings; @@ -94,32 +102,17 @@ namespace Umbraco.Web ClientDependency.Core.CompositeFiles.CompositeDependencyHandler.MaxHandlerUrlLength = Math.Min(section.MaxQueryStringLength, section.MaxRequestLength); } - //set master controller factory - ControllerBuilder.Current.SetControllerFactory( - new MasterControllerFactory(FilteredControllerFactoriesResolver.Current)); - - //set the render view engine - ViewEngines.Engines.Add(new RenderViewEngine()); - //set the plugin view engine - ViewEngines.Engines.Add(new PluginViewEngine()); - - //set model binder - ModelBinders.Binders.Add(new KeyValuePair(typeof(RenderModel), new RenderModelBinder())); - - ////add the profiling action filter - //GlobalFilters.Filters.Add(new ProfilingActionFilter()); - //Register a custom renderer - used to process property editor dependencies var renderer = new DependencyPathRenderer(); renderer.Initialize("Umbraco.DependencyPathRenderer", new NameValueCollection { { "compositeFileHandlerPath", "~/DependencyHandler.axd" } }); ClientDependencySettings.Instance.MvcRendererCollection.Add(renderer); - InstallHelper insHelper = new InstallHelper(UmbracoContext.Current); + var insHelper = new InstallHelper(UmbracoContext.Current); insHelper.DeleteLegacyInstaller(); return this; } - + /// /// Override this method in order to ensure that the UmbracoContext is also created, this can only be /// created after resolution is frozen! @@ -140,20 +133,9 @@ namespace Umbraco.Web /// /// Ensure the current profiler is the web profiler /// - protected override void InitializeProfilerResolver() + protected override IProfiler CreateProfiler() { - base.InitializeProfilerResolver(); - - //Set the profiler to be the web profiler - ProfilerResolver.Current.SetProfiler(new WebProfiler()); - } - - /// - /// Adds custom types to the ApplicationEventsResolver - /// - protected override void InitializeApplicationEventsResolver() - { - base.InitializeApplicationEventsResolver(); + return new WebProfiler(); } /// @@ -171,9 +153,6 @@ namespace Umbraco.Web base.Complete(afterComplete); - //Now, startup all of our legacy startup handler - ApplicationEventsResolver.Current.InstantiateLegacyStartupHandlers(); - //Ok, now that everything is complete we'll check if we've stored any references to index that need rebuilding and run them // (see the initialize method for notes) - we'll ensure we remove the event handler too in case examine manager doesn't actually // initialize during startup, in which case we want it to rebuild the indexes itself. @@ -205,10 +184,10 @@ namespace Umbraco.Web /// /// Creates the application cache based on the HttpRuntime cache /// - protected override void CreateApplicationCache() + protected override CacheHelper CreateApplicationCache() { //create a web-based cache helper - ApplicationCache = new CacheHelper(); + return new CacheHelper(); } /// @@ -235,7 +214,7 @@ namespace Umbraco.Web //plugin controllers must come first because the next route will catch many things RoutePluginControllers(); } - + private void RoutePluginControllers() { var umbracoPath = GlobalSettings.UmbracoMvcArea; @@ -308,6 +287,26 @@ namespace Umbraco.Web route.RouteHandler = new SurfaceRouteHandler(); } + /// + /// Called to customize the IoC container + /// + /// + internal override void ConfigureServices(ServiceContainer container) + { + base.ConfigureServices(container); + + //IoC setup for LightInject for mvc/webapi + Container.EnableMvc(); + Container.RegisterMvcControllers(PluginManager); + container.EnablePerWebRequestScope(); + container.EnableWebApi(GlobalConfiguration.Configuration); + container.RegisterApiControllers(PluginManager); + + //register other services + container.Register(); + container.Register(); + } + /// /// Initializes all web based and core resolves /// @@ -315,7 +314,7 @@ namespace Umbraco.Web { base.InitializeResolvers(); - XsltExtensionsResolver.Current = new XsltExtensionsResolver(ServiceProvider, LoggerResolver.Current.Logger, () => PluginManager.Current.ResolveXsltExtensions()); + XsltExtensionsResolver.Current = new XsltExtensionsResolver(ServiceProvider, ProfilingLogger.Logger, () => PluginManager.ResolveXsltExtensions()); //set the default RenderMvcController DefaultRenderMvcControllerResolver.Current = new DefaultRenderMvcControllerResolver(typeof(RenderMvcController)); @@ -334,21 +333,21 @@ namespace Umbraco.Web } catch (Exception e) { - LoggerResolver.Current.Logger.Error("An error occurred trying to set the IServerMessenger during application startup", e); + ProfilingLogger.Logger.Error("An error occurred trying to set the IServerMessenger during application startup", e); return null; } } - LoggerResolver.Current.Logger.Warn("Could not initialize the DefaultServerMessenger, the application is not configured or the database is not configured"); + ProfilingLogger.Logger.Warn("Could not initialize the DefaultServerMessenger, the application is not configured or the database is not configured"); return null; })); SurfaceControllerResolver.Current = new SurfaceControllerResolver( - ServiceProvider, LoggerResolver.Current.Logger, - PluginManager.Current.ResolveSurfaceControllers()); + ServiceProvider, ProfilingLogger.Logger, + PluginManager.ResolveSurfaceControllers()); UmbracoApiControllerResolver.Current = new UmbracoApiControllerResolver( - ServiceProvider, LoggerResolver.Current.Logger, - PluginManager.Current.ResolveUmbracoApiControllers()); + ServiceProvider, ProfilingLogger.Logger, + PluginManager.ResolveUmbracoApiControllers()); // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be // discovered when CoreBootManager configures the converters. We HAVE to remove one of them @@ -360,15 +359,10 @@ namespace Umbraco.Web PropertyValueConvertersResolver.Current.RemoveType(); PropertyValueConvertersResolver.Current.RemoveType(); - PublishedCachesResolver.Current = new PublishedCachesResolver(new PublishedCaches( - new PublishedCache.XmlPublishedCache.PublishedContentCache(), - new PublishedCache.XmlPublishedCache.PublishedMediaCache(ApplicationContext))); - - GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), - new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration)); + PublishedCachesResolver.Current = new PublishedCachesResolver(Container, typeof(PublishedCaches)); FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver( - ServiceProvider, LoggerResolver.Current.Logger, + ServiceProvider, ProfilingLogger.Logger, // add all known factories, devs can then modify this list on application // startup either by binding to events or in their own global.asax new[] @@ -377,44 +371,66 @@ namespace Umbraco.Web }); UrlProviderResolver.Current = new UrlProviderResolver( - ServiceProvider, LoggerResolver.Current.Logger, - //typeof(AliasUrlProvider), // not enabled by default + ServiceProvider, ProfilingLogger.Logger, + //typeof(AliasUrlProvider), // not enabled by default typeof(DefaultUrlProvider), typeof(CustomRouteUrlProvider) ); - ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver(new ContentFinderByLegacy404()); + ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver(Container, typeof(ContentFinderByLegacy404)); ContentFinderResolver.Current = new ContentFinderResolver( - ServiceProvider, LoggerResolver.Current.Logger, + Container, ProfilingLogger.Logger, // all built-in finders in the correct order, devs can then modify this list // on application startup via an application event handler. typeof(ContentFinderByPageIdQuery), typeof(ContentFinderByNiceUrl), typeof(ContentFinderByIdPath), - typeof (ContentFinderByNiceUrlAndTemplate), - typeof (ContentFinderByProfile), - typeof (ContentFinderByUrlAlias) + typeof(ContentFinderByNiceUrlAndTemplate), + typeof(ContentFinderByProfile), + typeof(ContentFinderByUrlAlias) ); - SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper()); + SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(Container, typeof(SiteDomainHelper)); - // ain't that a bit dirty? + // ain't that a bit dirty? YES PublishedCache.XmlPublishedCache.PublishedContentCache.UnitTesting = _isForTesting; ThumbnailProvidersResolver.Current = new ThumbnailProvidersResolver( - ServiceProvider, LoggerResolver.Current.Logger, - PluginManager.Current.ResolveThumbnailProviders()); + Container, ProfilingLogger.Logger, + PluginManager.ResolveThumbnailProviders()); ImageUrlProviderResolver.Current = new ImageUrlProviderResolver( - ServiceProvider, LoggerResolver.Current.Logger, - PluginManager.Current.ResolveImageUrlProviders()); + ServiceProvider, ProfilingLogger.Logger, + PluginManager.ResolveImageUrlProviders()); - CultureDictionaryFactoryResolver.Current = new CultureDictionaryFactoryResolver( - new DefaultCultureDictionaryFactory()); + CultureDictionaryFactoryResolver.Current = new CultureDictionaryFactoryResolver(Container, typeof(DefaultCultureDictionaryFactory)); } + /// + /// Sets up MVC/WebApi services + /// + private void SetupMvcAndWebApi() + { + //set master controller factory + ControllerBuilder.Current.SetControllerFactory( + new MasterControllerFactory(FilteredControllerFactoriesResolver.Current)); + + //set the render view engine + ViewEngines.Engines.Add(new RenderViewEngine()); + //set the plugin view engine + ViewEngines.Engines.Add(new PluginViewEngine()); + + //set model binder + ModelBinders.Binders.Add(new KeyValuePair(typeof(RenderModel), new RenderModelBinder())); + + ////add the profiling action filter + //GlobalFilters.Filters.Add(new ProfilingActionFilter()); + + GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), + new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration)); + } private void OnInstanceOnBuildingEmptyIndexOnStartup(object sender, BuildingEmptyIndexOnStartupEventArgs args) { diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 9adab52a05..2e3bad534f 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -5,6 +5,9 @@ + + +