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; }