From 944d47f669b59e65ce58a4a2f8baa38e34dfc0a1 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 9 Jan 2019 17:39:32 +0100 Subject: [PATCH 1/4] Refactor filesystems registration --- src/Umbraco.Core/Components/Composition.cs | 123 +++++++++--------- .../Components/CompositionExtensions.cs | 13 +- .../Composing/CollectionBuilderBase.cs | 2 +- .../Composers/FileSystemsComposer.cs | 31 +++-- .../Composing/CompositionExtensions.cs | 9 +- .../Composing/FactoryExtensions.cs | 3 + src/Umbraco.Core/Composing/IFactory.cs | 12 +- src/Umbraco.Core/Composing/IRegister.cs | 21 ++- .../Composing/LazyCollectionBuilderBase.cs | 2 +- .../LightInject/LightInjectContainer.cs | 57 ++++---- .../Composing/OrderedCollectionBuilderBase.cs | 2 +- .../Composing/RegisterExtensions.cs | 10 ++ .../Composing/TargetedServiceProvider.cs | 18 +++ .../WeightedCollectionBuilderBase.cs | 2 +- src/Umbraco.Core/IO/SupportingFileSystems.cs | 11 ++ src/Umbraco.Core/Umbraco.Core.csproj | 2 + src/Umbraco.Web/Composing/ModuleInjector.cs | 5 +- 17 files changed, 211 insertions(+), 112 deletions(-) create mode 100644 src/Umbraco.Core/Composing/TargetedServiceProvider.cs create mode 100644 src/Umbraco.Core/IO/SupportingFileSystems.cs diff --git a/src/Umbraco.Core/Components/Composition.cs b/src/Umbraco.Core/Components/Composition.cs index dd0b83dcb3..6cd8c8e5f0 100644 --- a/src/Umbraco.Core/Components/Composition.cs +++ b/src/Umbraco.Core/Components/Composition.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Components public class Composition : IRegister { private readonly Dictionary _builders = new Dictionary(); - private readonly Dictionary _uniques = new Dictionary(); + private readonly Dictionary> _uniques = new Dictionary>(); private readonly IRegister _register; /// @@ -81,14 +81,30 @@ namespace Umbraco.Core.Components public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient) => _register.Register(serviceType, implementingType, lifetime); + /// + public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class + => _register.RegisterFor(implementingType, lifetime); + /// public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class => _register.Register(factory, lifetime); + /// + public void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class + => _register.RegisterFor(factory, lifetime); + /// public void RegisterInstance(Type serviceType, object instance) => _register.RegisterInstance(serviceType, instance); + /// + public void RegisterInstanceFor(TService instance) + where TService : class + => _register.RegisterInstanceFor(instance); + /// public void RegisterAuto(Type serviceBaseType) => _register.RegisterAuto(serviceBaseType); @@ -104,10 +120,12 @@ namespace Umbraco.Core.Components onCreating(); foreach (var unique in _uniques.Values) - unique.RegisterWith(_register); + unique(_register); + _uniques.Clear(); // no point keep them around foreach (var builder in _builders.Values) builder.RegisterWith(_register); + _builders.Clear(); // no point keep them around Configs.RegisterWith(_register); @@ -123,74 +141,63 @@ namespace Umbraco.Core.Components #region Unique - /// - /// Registers a unique service. - /// - /// Unique services have one single implementation, and a Singleton lifetime. - public void RegisterUnique(Type serviceType, Type implementingType) - => _uniques[serviceType] = new Unique(serviceType, implementingType); + private string GetUniqueName() + => GetUniqueName(typeof(TService)); + + private string GetUniqueName(Type serviceType) + => serviceType.FullName; + + private string GetUniqueName() + => GetUniqueName(typeof(TService), typeof(TTarget)); + + private string GetUniqueName(Type serviceType, Type targetType) + => serviceType.FullName + "::" + targetType.FullName; /// /// Registers a unique service. /// /// Unique services have one single implementation, and a Singleton lifetime. - public void RegisterUnique(Type serviceType, object instance) - => _uniques[serviceType] = new Unique(serviceType, instance); + public void RegisterUnique(Type serviceType, Type implementingType) + => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, implementingType, Lifetime.Singleton); + + /// + /// Registers a unique service, for a target. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor(Type implementingType) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(implementingType, Lifetime.Singleton); + + /// + /// Registers a unique service. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueInstance(Type serviceType, object instance) + => _uniques[GetUniqueName(serviceType)] = register => register.RegisterInstance(serviceType, instance); + + /// + /// Registers a unique service, for a target. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueInstanceFor(TService instance) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterInstanceFor(instance); /// /// Registers a unique service. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Func factory) - => _uniques[typeof(TService)] = new Unique(factory); + where TService : class + => _uniques[GetUniqueName()] = register => register.Register(factory, Lifetime.Singleton); - private class Unique - { - private readonly Type _serviceType; - private readonly Type _implementingType; - private readonly object _instance; - - protected Unique(Type serviceType) - { - _serviceType = serviceType; - } - - public Unique(Type serviceType, Type implementingType) - : this(serviceType) - { - _implementingType = implementingType; - } - - public Unique(Type serviceType, object instance) - : this(serviceType) - { - _instance = instance; - } - - public virtual void RegisterWith(IRegister register) - { - if (_implementingType != null) - register.Register(_serviceType, _implementingType, Lifetime.Singleton); - else if (_instance != null) - register.RegisterInstance(_serviceType, _instance); - } - } - - private class Unique : Unique - { - private readonly Func _factory; - - public Unique(Func factory) - : base(typeof(TService)) - { - _factory = factory; - } - - public override void RegisterWith(IRegister register) - { - register.Register(_factory, Lifetime.Singleton); - } - } + /// + /// Registers a unique service, for a target. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor(Func factory) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(factory, Lifetime.Singleton); #endregion diff --git a/src/Umbraco.Core/Components/CompositionExtensions.cs b/src/Umbraco.Core/Components/CompositionExtensions.cs index 93d190d17e..a4bc82cb4b 100644 --- a/src/Umbraco.Core/Components/CompositionExtensions.cs +++ b/src/Umbraco.Core/Components/CompositionExtensions.cs @@ -26,15 +26,16 @@ namespace Umbraco.Core.Components /// The type of the filesystem. /// The implementing type. /// The composition. - /// A factory method creating the supporting filesystem. /// The register. - public static void RegisterFileSystem(this Composition composition, Func supportingFileSystemFactory) + public static void RegisterFileSystem(this Composition composition) where TImplementing : FileSystemWrapper, TFileSystem + where TFileSystem : class { composition.RegisterUnique(factory => { var fileSystems = factory.GetInstance(); - return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + var supporting = factory.GetInstance(); + return fileSystems.GetFileSystem(supporting.For()); }); } @@ -43,15 +44,15 @@ namespace Umbraco.Core.Components /// /// The type of the filesystem. /// The composition. - /// A factory method creating the supporting filesystem. /// The register. - public static void RegisterFileSystem(this Composition composition, Func supportingFileSystemFactory) + public static void RegisterFileSystem(this Composition composition) where TFileSystem : FileSystemWrapper { composition.RegisterUnique(factory => { var fileSystems = factory.GetInstance(); - return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + var supporting = factory.GetInstance(); + return fileSystems.GetFileSystem(supporting.For()); }); } diff --git a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs index 7633f6b001..41038ea4e9 100644 --- a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class CollectionBuilderBase : ICollectionBuilder where TBuilder: CollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { private readonly List _types = new List(); private readonly object _locker = new object(); diff --git a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs index f1fb095406..ab4bd015de 100644 --- a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs @@ -12,10 +12,9 @@ namespace Umbraco.Core.Composing.Composers * * Create a component and use it to modify the composition by adding something like: * - * composition.Container.RegisterFileSystem( - * factory => new PhysicalFileSystem("~/somewhere")); + * composition.RegisterUniqueFor(...); * - * return whatever supporting filesystem you like. + * and register whatever supporting filesystem you like. * * * HOW TO IMPLEMENT MY OWN FILESYSTEM @@ -30,12 +29,15 @@ namespace Umbraco.Core.Composing.Composers * { } * } * - * The ctor can have more parameters that will be resolved by the container. + * The ctor can have more parameters, that will be resolved by the container. * * Register your filesystem, in a component: * - * composition.Container.RegisterFileSystem( - * factory => new PhysicalFileSystem("~/my")); + * composition.RegisterFileSystem(); + * + * Register the underlying filesystem: + * + * composition.RegisterUniqueFor(...); * * And that's it, you can inject MyFileSystem wherever it's needed. * @@ -48,8 +50,8 @@ namespace Umbraco.Core.Composing.Composers * Make the class implement the interface, then * register your filesystem, in a component: * - * composition.Container.RegisterFileSystem( - * factory => new PhysicalFileSystem("~/my")); + * composition.RegisterFileSystem(); + * composition.RegisterUniqueFor(...); * * And that's it, you can inject IMyFileSystem wherever it's needed. * @@ -79,9 +81,16 @@ namespace Umbraco.Core.Composing.Composers // register the scheme for media paths composition.RegisterUnique(); - // register the IMediaFileSystem implementation with a supporting filesystem - composition.RegisterFileSystem( - factory => new PhysicalFileSystem("~/media")); + // register the IMediaFileSystem implementation + composition.RegisterFileSystem(); + + // register the supporting filesystems provider + composition.Register(factory => new SupportingFileSystems(factory), Lifetime.Singleton); + + // register the IFileSystem supporting the IMediaFileSystem + // this is the only thing that need to be overriden to change the supporting filesystem + // and, SupportingFileSystem.For() returns the underlying filesystem + composition.RegisterUniqueFor(_ => new PhysicalFileSystem("~/media")); return composition; } diff --git a/src/Umbraco.Core/Composing/CompositionExtensions.cs b/src/Umbraco.Core/Composing/CompositionExtensions.cs index cfc465b59d..ac9a5d6409 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions.cs +++ b/src/Umbraco.Core/Composing/CompositionExtensions.cs @@ -51,11 +51,18 @@ namespace Umbraco.Core.Composing public static void RegisterUnique(this Composition composition) => composition.RegisterUnique(typeof(TService), typeof(TImplementing)); + /// + /// Registers a unique service with an implementation type, for a target. + /// + public static void RegisterUniqueFor(this Composition composition) + where TService : class + => composition.RegisterUniqueFor(typeof(TImplementing)); + /// /// Registers a unique service with an implementing instance. /// public static void RegisterUnique(this Composition composition, TService instance) - => composition.RegisterUnique(typeof(TService), instance); + => composition.RegisterUniqueInstance(typeof(TService), instance); #endregion } diff --git a/src/Umbraco.Core/Composing/FactoryExtensions.cs b/src/Umbraco.Core/Composing/FactoryExtensions.cs index 2640b7f7e6..8027f2c7a1 100644 --- a/src/Umbraco.Core/Composing/FactoryExtensions.cs +++ b/src/Umbraco.Core/Composing/FactoryExtensions.cs @@ -17,6 +17,7 @@ namespace Umbraco.Core.Composing /// An instance of the specified type. /// Throws an exception if the factory failed to get an instance of the specified type. public static T GetInstance(this IFactory factory) + where T : class => (T)factory.GetInstance(typeof(T)); /// @@ -28,6 +29,7 @@ namespace Umbraco.Core.Composing /// of the specified type. Throws an exception if the factory does know how /// to get an instance of the specified type, but failed to do so. public static T TryGetInstance(this IFactory factory) + where T : class => (T)factory.TryGetInstance(typeof(T)); /// @@ -42,6 +44,7 @@ namespace Umbraco.Core.Composing /// The arguments are used as dependencies by the factory. /// public static T CreateInstance(this IFactory factory, params object[] args) + where T : class => (T)factory.CreateInstance(typeof(T), args); /// diff --git a/src/Umbraco.Core/Composing/IFactory.cs b/src/Umbraco.Core/Composing/IFactory.cs index 9a59b1c052..20fbe33737 100644 --- a/src/Umbraco.Core/Composing/IFactory.cs +++ b/src/Umbraco.Core/Composing/IFactory.cs @@ -28,6 +28,15 @@ namespace Umbraco.Core.Composing /// Throws an exception if the container failed to get an instance of the specified type. object GetInstance(Type type); + /// + /// Gets a targeted instance of a service. + /// + /// The type of the service. + /// The type of the target. + /// The instance of the specified type for the specified target. + /// Throws an exception if the container failed to get an instance of the specified type. + TService GetInstanceFor(); + /// /// Tries to get an instance of a service. /// @@ -48,7 +57,8 @@ namespace Umbraco.Core.Composing /// Gets all instances of a service. /// /// The type of the service. - IEnumerable GetAllInstances(); + IEnumerable GetAllInstances() + where TService : class; /// /// Releases an instance. diff --git a/src/Umbraco.Core/Composing/IRegister.cs b/src/Umbraco.Core/Composing/IRegister.cs index 8ad3db5409..d145df8790 100644 --- a/src/Umbraco.Core/Composing/IRegister.cs +++ b/src/Umbraco.Core/Composing/IRegister.cs @@ -33,16 +33,35 @@ namespace Umbraco.Core.Composing /// void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient); + /// + /// Registers a service with an implementation type, for a target. + /// + void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class; + /// /// Registers a service with an implementation factory. /// - void Register(Func factory, Lifetime lifetime = Lifetime.Transient); + void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class; + + /// + /// Registers a service with an implementation factory, for a target. + /// + void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class; /// /// Registers a service with an implementing instance. /// void RegisterInstance(Type serviceType, object instance); + /// + /// Registers a service with an implementing instance, for a target. + /// + void RegisterInstanceFor(TService instance) + where TService : class; + /// /// Registers a base type for auto-registration. /// diff --git a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs index a1a06621e9..46b06daf7d 100644 --- a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class LazyCollectionBuilderBase : CollectionBuilderBase where TBuilder : LazyCollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { private readonly List>> _producers = new List>>(); private readonly List _excluded = new List(); diff --git a/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs index b39622f66a..4ccc0ba838 100644 --- a/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs @@ -102,18 +102,25 @@ namespace Umbraco.Core.Composing.LightInject /// public IFactory CreateFactory() => this; + private static string GetTargetedServiceName() => "TARGET:" + typeof(TTarget).FullName; + #region Factory /// public object GetInstance(Type type) => Container.GetInstance(type); + /// + public TService GetInstanceFor() + => Container.GetInstance(GetTargetedServiceName()); + /// public object TryGetInstance(Type type) => Container.TryGetInstance(type); /// public IEnumerable GetAllInstances() + where T : class => Container.GetAllInstances(); /// @@ -139,19 +146,7 @@ namespace Umbraco.Core.Composing.LightInject /// public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient) { - switch (lifetime) - { - case Lifetime.Transient: - Container.Register(serviceType); - break; - case Lifetime.Request: - case Lifetime.Scope: - case Lifetime.Singleton: - Container.Register(serviceType, GetLifetime(lifetime)); - break; - default: - throw new NotSupportedException($"Lifetime {lifetime} is not supported."); - } + Container.Register(serviceType, GetLifetime(lifetime)); } /// @@ -173,21 +168,24 @@ namespace Umbraco.Core.Composing.LightInject } /// - public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class { - switch (lifetime) - { - case Lifetime.Transient: - Container.Register(f => factory(this)); - break; - case Lifetime.Request: - case Lifetime.Scope: - case Lifetime.Singleton: - Container.Register(f => factory(this), GetLifetime(lifetime)); - break; - default: - throw new NotSupportedException($"Lifetime {lifetime} is not supported."); - } + Container.Register(typeof(TService), implementingType, GetTargetedServiceName(), GetLifetime(lifetime)); + } + + /// + public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class + { + Container.Register(f => factory(this), GetLifetime(lifetime)); + } + + /// + public void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class + { + Container.Register(f => factory(this), GetTargetedServiceName(), GetLifetime(lifetime)); } private ILifetime GetLifetime(Lifetime lifetime) @@ -211,6 +209,11 @@ namespace Umbraco.Core.Composing.LightInject public void RegisterInstance(Type serviceType, object instance) => Container.RegisterInstance(serviceType, instance); + /// + public void RegisterInstanceFor(TService instance) + where TService : class + => Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName()); + /// public void RegisterAuto(Type serviceBaseType) { diff --git a/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs index bde1bf96c5..241b84d8d2 100644 --- a/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class OrderedCollectionBuilderBase : CollectionBuilderBase where TBuilder : OrderedCollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { protected abstract TBuilder This { get; } diff --git a/src/Umbraco.Core/Composing/RegisterExtensions.cs b/src/Umbraco.Core/Composing/RegisterExtensions.cs index 4db1a2e9e4..9afa41e4b3 100644 --- a/src/Umbraco.Core/Composing/RegisterExtensions.cs +++ b/src/Umbraco.Core/Composing/RegisterExtensions.cs @@ -11,22 +11,32 @@ public static void Register(this IRegister register, Lifetime lifetime = Lifetime.Transient) => register.Register(typeof(TService), typeof(TImplementing), lifetime); + /// + /// Registers a service with an implementation type, for a target. + /// + public static void RegisterFor(this IRegister register, Lifetime lifetime = Lifetime.Transient) + where TService : class + => register.RegisterFor(typeof(TImplementing), lifetime); + /// /// Registers a service as its own implementation. /// public static void Register(this IRegister register, Lifetime lifetime = Lifetime.Transient) + where TService : class => register.Register(typeof(TService), lifetime); /// /// Registers a service with an implementing instance. /// public static void RegisterInstance(this IRegister register, TService instance) + where TService : class => register.RegisterInstance(typeof(TService), instance); /// /// Registers a base type for auto-registration. /// public static void RegisterAuto(this IRegister register) + where TServiceBase : class => register.RegisterAuto(typeof(TServiceBase)); } } diff --git a/src/Umbraco.Core/Composing/TargetedServiceProvider.cs b/src/Umbraco.Core/Composing/TargetedServiceProvider.cs new file mode 100644 index 0000000000..3f88e1bc28 --- /dev/null +++ b/src/Umbraco.Core/Composing/TargetedServiceProvider.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Core.Composing +{ + /// + /// Provides a base class for targeted service factories. + /// + /// + public abstract class TargetedServiceProvider + { + private readonly IFactory _factory; + + protected TargetedServiceProvider(IFactory factory) + { + _factory = factory; + } + + public TService For() => _factory.GetInstanceFor(); + } +} diff --git a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs index da47c53bf8..f8ecc11d98 100644 --- a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class WeightedCollectionBuilderBase : CollectionBuilderBase where TBuilder : WeightedCollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { protected abstract TBuilder This { get; } diff --git a/src/Umbraco.Core/IO/SupportingFileSystems.cs b/src/Umbraco.Core/IO/SupportingFileSystems.cs new file mode 100644 index 0000000000..c13ae203ee --- /dev/null +++ b/src/Umbraco.Core/IO/SupportingFileSystems.cs @@ -0,0 +1,11 @@ +using Umbraco.Core.Composing; + +namespace Umbraco.Core.IO +{ + public class SupportingFileSystems : TargetedServiceProvider + { + public SupportingFileSystems(IFactory factory) + : base(factory) + { } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a01bbf1746..a592fd0f0e 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -196,6 +196,7 @@ + @@ -343,6 +344,7 @@ + diff --git a/src/Umbraco.Web/Composing/ModuleInjector.cs b/src/Umbraco.Web/Composing/ModuleInjector.cs index 01930d55fd..57ef766dea 100644 --- a/src/Umbraco.Web/Composing/ModuleInjector.cs +++ b/src/Umbraco.Web/Composing/ModuleInjector.cs @@ -1,5 +1,4 @@ -using System; -using System.Web; +using System.Web; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Exceptions; @@ -11,7 +10,7 @@ namespace Umbraco.Web.Composing /// /// The type of the injected module. public abstract class ModuleInjector : IHttpModule - where TModule : IHttpModule + where TModule : class, IHttpModule { protected TModule Module { get; private set; } From 561b359a721ef002e98c2beb48d374ad683736e1 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 14 Jan 2019 08:53:04 +0100 Subject: [PATCH 2/4] Name change --- .../{TargetedServiceProvider.cs => TargetedServiceFactory.cs} | 4 ++-- src/Umbraco.Core/IO/SupportingFileSystems.cs | 4 ++-- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Umbraco.Core/Composing/{TargetedServiceProvider.cs => TargetedServiceFactory.cs} (76%) diff --git a/src/Umbraco.Core/Composing/TargetedServiceProvider.cs b/src/Umbraco.Core/Composing/TargetedServiceFactory.cs similarity index 76% rename from src/Umbraco.Core/Composing/TargetedServiceProvider.cs rename to src/Umbraco.Core/Composing/TargetedServiceFactory.cs index 3f88e1bc28..53022c0043 100644 --- a/src/Umbraco.Core/Composing/TargetedServiceProvider.cs +++ b/src/Umbraco.Core/Composing/TargetedServiceFactory.cs @@ -4,11 +4,11 @@ /// Provides a base class for targeted service factories. /// /// - public abstract class TargetedServiceProvider + public abstract class TargetedServiceFactory { private readonly IFactory _factory; - protected TargetedServiceProvider(IFactory factory) + protected TargetedServiceFactory(IFactory factory) { _factory = factory; } diff --git a/src/Umbraco.Core/IO/SupportingFileSystems.cs b/src/Umbraco.Core/IO/SupportingFileSystems.cs index c13ae203ee..43ac2ba85a 100644 --- a/src/Umbraco.Core/IO/SupportingFileSystems.cs +++ b/src/Umbraco.Core/IO/SupportingFileSystems.cs @@ -2,10 +2,10 @@ namespace Umbraco.Core.IO { - public class SupportingFileSystems : TargetedServiceProvider + public class SupportingFileSystems : TargetedServiceFactory { public SupportingFileSystems(IFactory factory) : base(factory) { } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a592fd0f0e..609befd233 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -196,7 +196,7 @@ - + From a68b19f1ee2bbd58975a14a91533b5c7f5c9daf9 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 14 Jan 2019 14:28:19 +0100 Subject: [PATCH 3/4] More composition / filesystems fixes --- src/Umbraco.Core/Components/Composition.cs | 94 +++++++++++-------- .../Composing/CompositionExtensions.cs | 2 +- src/Umbraco.Core/Composing/IFactory.cs | 7 -- src/Umbraco.Core/Composing/IRegister.cs | 45 ++++----- .../LightInject/LightInjectContainer.cs | 65 +++++-------- .../MixedLightInjectScopeManagerProvider.cs | 3 + .../Composing/RegisterExtensions.cs | 6 +- src/Umbraco.Core/Configuration/Configs.cs | 2 +- .../Composing/ContainerConformingTests.cs | 8 +- 9 files changed, 115 insertions(+), 117 deletions(-) diff --git a/src/Umbraco.Core/Components/Composition.cs b/src/Umbraco.Core/Components/Composition.cs index 6cd8c8e5f0..6df86d793f 100644 --- a/src/Umbraco.Core/Components/Composition.cs +++ b/src/Umbraco.Core/Components/Composition.cs @@ -81,29 +81,34 @@ namespace Umbraco.Core.Components public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient) => _register.Register(serviceType, implementingType, lifetime); - /// - public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) - where TService : class - => _register.RegisterFor(implementingType, lifetime); - /// public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class => _register.Register(factory, lifetime); + /// + public void Register(Type serviceType, object instance) + => _register.Register(serviceType, instance); + + /// + public void RegisterFor(Lifetime lifetime = Lifetime.Transient) + where TService : class + => _register.RegisterFor(lifetime); + + /// + public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class + => _register.RegisterFor(implementingType, lifetime); + /// public void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class => _register.RegisterFor(factory, lifetime); /// - public void RegisterInstance(Type serviceType, object instance) - => _register.RegisterInstance(serviceType, instance); - - /// - public void RegisterInstanceFor(TService instance) + public void RegisterFor(TService instance) where TService : class - => _register.RegisterInstanceFor(instance); + => _register.RegisterFor(instance); /// public void RegisterAuto(Type serviceBaseType) @@ -154,37 +159,21 @@ namespace Umbraco.Core.Components => serviceType.FullName + "::" + targetType.FullName; /// - /// Registers a unique service. + /// Registers a unique service as its own implementation. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUnique(Type serviceType) + => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, Lifetime.Singleton); + + /// + /// Registers a unique service with an implementation type. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Type serviceType, Type implementingType) => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, implementingType, Lifetime.Singleton); /// - /// Registers a unique service, for a target. - /// - /// Unique services have one single implementation, and a Singleton lifetime. - public void RegisterUniqueFor(Type implementingType) - where TService : class - => _uniques[GetUniqueName()] = register => register.RegisterFor(implementingType, Lifetime.Singleton); - - /// - /// Registers a unique service. - /// - /// Unique services have one single implementation, and a Singleton lifetime. - public void RegisterUniqueInstance(Type serviceType, object instance) - => _uniques[GetUniqueName(serviceType)] = register => register.RegisterInstance(serviceType, instance); - - /// - /// Registers a unique service, for a target. - /// - /// Unique services have one single implementation, and a Singleton lifetime. - public void RegisterUniqueInstanceFor(TService instance) - where TService : class - => _uniques[GetUniqueName()] = register => register.RegisterInstanceFor(instance); - - /// - /// Registers a unique service. + /// Registers a unique service with an implementation factory. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Func factory) @@ -192,13 +181,44 @@ namespace Umbraco.Core.Components => _uniques[GetUniqueName()] = register => register.Register(factory, Lifetime.Singleton); /// - /// Registers a unique service, for a target. + /// Registers a unique service with an implementing instance. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUnique(Type serviceType, object instance) + => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance); + + /// + /// Registers a unique service for a target, as its own implementation. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor() + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(Lifetime.Singleton); + + /// + /// Registers a unique service for a target, with an implementing type. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor(Type implementingType) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(implementingType, Lifetime.Singleton); + + /// + /// Registers a unique service for a target, with an implementation factory. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUniqueFor(Func factory) where TService : class => _uniques[GetUniqueName()] = register => register.RegisterFor(factory, Lifetime.Singleton); + /// + /// Registers a unique service for a target, with an implementing instance. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor(TService instance) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(instance); + #endregion #region Collection Builders diff --git a/src/Umbraco.Core/Composing/CompositionExtensions.cs b/src/Umbraco.Core/Composing/CompositionExtensions.cs index ac9a5d6409..2307d757c9 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions.cs +++ b/src/Umbraco.Core/Composing/CompositionExtensions.cs @@ -62,7 +62,7 @@ namespace Umbraco.Core.Composing /// Registers a unique service with an implementing instance. /// public static void RegisterUnique(this Composition composition, TService instance) - => composition.RegisterUniqueInstance(typeof(TService), instance); + => composition.RegisterUnique(typeof(TService), instance); #endregion } diff --git a/src/Umbraco.Core/Composing/IFactory.cs b/src/Umbraco.Core/Composing/IFactory.cs index 20fbe33737..768b9207a3 100644 --- a/src/Umbraco.Core/Composing/IFactory.cs +++ b/src/Umbraco.Core/Composing/IFactory.cs @@ -3,13 +3,6 @@ using System.Collections.Generic; namespace Umbraco.Core.Composing { - // Implementing: - // - // The factory - // - always picks the constructor with the most parameters - // - supports Lazy parameters (and prefers them over non-Lazy) in constructors - // - what happens with 'releasing' is unclear - /// /// Defines a service factory for Umbraco. /// diff --git a/src/Umbraco.Core/Composing/IRegister.cs b/src/Umbraco.Core/Composing/IRegister.cs index d145df8790..3e6aed67ee 100644 --- a/src/Umbraco.Core/Composing/IRegister.cs +++ b/src/Umbraco.Core/Composing/IRegister.cs @@ -2,17 +2,6 @@ namespace Umbraco.Core.Composing { - // Implementing: - // - // The register - // - supports registering a service, even after some instances of other services have been created - // - supports re-registering a service, as long as no instance of that service has been created - // - throws when re-registering a service, and an instance of that service has been created - // - // - registers only one implementation of a nameless service, re-registering replaces the previous - // registration - names are required to register multiple implementations - and getting an - // IEnumerable of the service, nameless, returns them all - /// /// Defines a service register for Umbraco. /// @@ -33,12 +22,6 @@ namespace Umbraco.Core.Composing /// void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient); - /// - /// Registers a service with an implementation type, for a target. - /// - void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) - where TService : class; - /// /// Registers a service with an implementation factory. /// @@ -46,20 +29,32 @@ namespace Umbraco.Core.Composing where TService : class; /// - /// Registers a service with an implementation factory, for a target. + /// Registers a service with an implementing instance. + /// + void Register(Type serviceType, object instance); + + /// + /// Registers a service for a target, as its own implementation. + /// + void RegisterFor(Lifetime lifetime = Lifetime.Transient) + where TService : class; + + /// + /// Registers a service for a target, with an implementation type. + /// + void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class; + + /// + /// Registers a service for a target, with an implementation factory. /// void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class; /// - /// Registers a service with an implementing instance. + /// Registers a service for a target, with an implementing instance. /// - void RegisterInstance(Type serviceType, object instance); - - /// - /// Registers a service with an implementing instance, for a target. - /// - void RegisterInstanceFor(TService instance) + void RegisterFor(TService instance) where TService : class; /// diff --git a/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs index 4ccc0ba838..d8a554ee8c 100644 --- a/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs @@ -145,9 +145,7 @@ namespace Umbraco.Core.Composing.LightInject /// public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient) - { - Container.Register(serviceType, GetLifetime(lifetime)); - } + => Container.Register(serviceType, GetLifetime(lifetime)); /// public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient) @@ -167,13 +165,6 @@ namespace Umbraco.Core.Composing.LightInject } } - /// - public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) - where TService : class - { - Container.Register(typeof(TService), implementingType, GetTargetedServiceName(), GetLifetime(lifetime)); - } - /// public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class @@ -181,13 +172,36 @@ namespace Umbraco.Core.Composing.LightInject Container.Register(f => factory(this), GetLifetime(lifetime)); } + /// + public void Register(Type serviceType, object instance) + => Container.RegisterInstance(serviceType, instance); + + /// + public void RegisterFor(Lifetime lifetime = Lifetime.Transient) + where TService : class + => RegisterFor(typeof(TService), lifetime); + + /// + public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class + { + // note that there can only be one implementation or instance registered "for" a service + Container.Register(typeof(TService), implementingType, GetTargetedServiceName(), GetLifetime(lifetime)); + } + /// public void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class { + // note that there can only be one implementation or instance registered "for" a service Container.Register(f => factory(this), GetTargetedServiceName(), GetLifetime(lifetime)); } + /// + public void RegisterFor(TService instance) + where TService : class + => Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName()); + private ILifetime GetLifetime(Lifetime lifetime) { switch (lifetime) @@ -205,15 +219,6 @@ namespace Umbraco.Core.Composing.LightInject } } - /// - public void RegisterInstance(Type serviceType, object instance) - => Container.RegisterInstance(serviceType, instance); - - /// - public void RegisterInstanceFor(TService instance) - where TService : class - => Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName()); - /// public void RegisterAuto(Type serviceBaseType) { @@ -226,17 +231,6 @@ namespace Umbraco.Core.Composing.LightInject }, null); } - // was the Light-Inject specific way of dealing with args, but we've replaced it with our own - // beware! does NOT work on singletons, see https://github.com/seesharper/LightInject/issues/294 - // - ///// - //public void RegisterConstructorDependency(Func factory) - // => Container.RegisterConstructorDependency((f, x) => factory(this, x)); - // - ///// - //public void RegisterConstructorDependency(Func factory) - // => Container.RegisterConstructorDependency((f, x, a) => factory(this, x, a)); - #endregion #region Control @@ -259,21 +253,14 @@ namespace Umbraco.Core.Composing.LightInject private class AssemblyScanner : IAssemblyScanner { - //private readonly IAssemblyScanner _scanner; - - //public AssemblyScanner(IAssemblyScanner scanner) - //{ - // _scanner = scanner; - //} - public void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func lifetime, Func shouldRegister, Func serviceNameProvider) { - // nothing - we *could* scan non-Umbraco assemblies, though + // nothing - we don't want LightInject to scan } public void Scan(Assembly assembly, IServiceRegistry serviceRegistry) { - // nothing - we *could* scan non-Umbraco assemblies, though + // nothing - we don't want LightInject to scan } } diff --git a/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs b/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs index 470079c6c0..897c58dd43 100644 --- a/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs +++ b/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs @@ -13,6 +13,9 @@ namespace Umbraco.Core.Composing.LightInject // of PerWebRequestScopeManagerProvider - but all delegates see is the mixed one - and therefore // they can transition without issues. // + // The PerWebRequestScopeManager maintains the scope in HttpContext and LightInject registers a + // module (PreApplicationStartMethod) which disposes it on EndRequest + // // the mixed provider is installed in container.ConfigureUmbracoCore() and then, // when doing eg container.EnableMvc() or anything that does container.EnablePerWebRequestScope() // we need to take great care to preserve the mixed scope manager provider! diff --git a/src/Umbraco.Core/Composing/RegisterExtensions.cs b/src/Umbraco.Core/Composing/RegisterExtensions.cs index 9afa41e4b3..d1eacc0c0f 100644 --- a/src/Umbraco.Core/Composing/RegisterExtensions.cs +++ b/src/Umbraco.Core/Composing/RegisterExtensions.cs @@ -14,7 +14,7 @@ /// /// Registers a service with an implementation type, for a target. /// - public static void RegisterFor(this IRegister register, Lifetime lifetime = Lifetime.Transient) + public static void RegisterFor(this IRegister register, Lifetime lifetime = Lifetime.Transient) where TService : class => register.RegisterFor(typeof(TImplementing), lifetime); @@ -28,9 +28,9 @@ /// /// Registers a service with an implementing instance. /// - public static void RegisterInstance(this IRegister register, TService instance) + public static void Register(this IRegister register, TService instance) where TService : class - => register.RegisterInstance(typeof(TService), instance); + => register.Register(typeof(TService), instance); /// /// Registers a base type for auto-registration. diff --git a/src/Umbraco.Core/Configuration/Configs.cs b/src/Umbraco.Core/Configuration/Configs.cs index 3dbbe5d4ff..51e1a327a0 100644 --- a/src/Umbraco.Core/Configuration/Configs.cs +++ b/src/Umbraco.Core/Configuration/Configs.cs @@ -100,7 +100,7 @@ namespace Umbraco.Core.Configuration if (_registerings == null) throw new InvalidOperationException("Configurations have already been registered."); - register.RegisterInstance(this); + register.Register(this); foreach (var registering in _registerings.Values) registering(register); diff --git a/src/Umbraco.Tests/Composing/ContainerConformingTests.cs b/src/Umbraco.Tests/Composing/ContainerConformingTests.cs index 9585c98cb2..21ea961636 100644 --- a/src/Umbraco.Tests/Composing/ContainerConformingTests.cs +++ b/src/Umbraco.Tests/Composing/ContainerConformingTests.cs @@ -194,8 +194,8 @@ namespace Umbraco.Tests.Composing var register = GetRegister(); // define two instances - register.RegisterInstance(typeof(Thing1), new Thing1()); - register.RegisterInstance(typeof(Thing1), new Thing2()); + register.Register(typeof(Thing1), new Thing1()); + register.Register(typeof(Thing1), new Thing2()); var factory = register.CreateFactory(); @@ -212,8 +212,8 @@ namespace Umbraco.Tests.Composing var register = GetRegister(); // define two instances - register.RegisterInstance(typeof(IThing), new Thing1()); - register.RegisterInstance(typeof(IThing), new Thing2()); + register.Register(typeof(IThing), new Thing1()); + register.Register(typeof(IThing), new Thing2()); var factory = register.CreateFactory(); From 9ba3f4769056ed9889a2662ea1083b4ac59d0d7a Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 14 Jan 2019 15:55:18 +0100 Subject: [PATCH 4/4] Cleanup --- .../Components/CompositionExtensions.cs | 16 ++++++++++++++++ .../Composing/Composers/FileSystemsComposer.cs | 4 ++-- src/Umbraco.Core/Composing/IRegister.cs | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Components/CompositionExtensions.cs b/src/Umbraco.Core/Components/CompositionExtensions.cs index a4bc82cb4b..bb23e89b81 100644 --- a/src/Umbraco.Core/Components/CompositionExtensions.cs +++ b/src/Umbraco.Core/Components/CompositionExtensions.cs @@ -281,6 +281,22 @@ namespace Umbraco.Core.Components composition.RegisterUnique(_ => helper); } + /// + /// Sets the underlying media filesystem. + /// + /// A composition. + /// A filesystem factory. + public static void SetMediaFileSystem(this Composition composition, Func filesystemFactory) + => composition.RegisterUniqueFor(filesystemFactory); + + /// + /// Sets the underlying media filesystem. + /// + /// A composition. + /// A filesystem factory. + public static void SetMediaFileSystem(this Composition composition, Func filesystemFactory) + => composition.RegisterUniqueFor(_ => filesystemFactory()); + #endregion } } diff --git a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs index ab4bd015de..4c598f27e4 100644 --- a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs @@ -88,9 +88,9 @@ namespace Umbraco.Core.Composing.Composers composition.Register(factory => new SupportingFileSystems(factory), Lifetime.Singleton); // register the IFileSystem supporting the IMediaFileSystem - // this is the only thing that need to be overriden to change the supporting filesystem + // THIS IS THE ONLY THING THAT NEEDS TO CHANGE, IN ORDER TO REPLACE THE UNDERLYING FILESYSTEM // and, SupportingFileSystem.For() returns the underlying filesystem - composition.RegisterUniqueFor(_ => new PhysicalFileSystem("~/media")); + composition.SetMediaFileSystem(() => new PhysicalFileSystem("~/media")); return composition; } diff --git a/src/Umbraco.Core/Composing/IRegister.cs b/src/Umbraco.Core/Composing/IRegister.cs index 3e6aed67ee..cbf12f54a3 100644 --- a/src/Umbraco.Core/Composing/IRegister.cs +++ b/src/Umbraco.Core/Composing/IRegister.cs @@ -36,24 +36,40 @@ namespace Umbraco.Core.Composing /// /// Registers a service for a target, as its own implementation. /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// void RegisterFor(Lifetime lifetime = Lifetime.Transient) where TService : class; /// /// Registers a service for a target, with an implementation type. /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) where TService : class; /// /// Registers a service for a target, with an implementation factory. /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class; /// /// Registers a service for a target, with an implementing instance. /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// void RegisterFor(TService instance) where TService : class;