Files
Umbraco-CMS/src/Umbraco.Core/Composing/LightInject/LightInjectExtensions.cs
2018-08-30 09:30:49 +02:00

200 lines
9.0 KiB
C#

using System;
using System.Linq;
using LightInject;
namespace Umbraco.Core.Composing.LightInject
{
/// <summary>
/// Provides extensions to LightInject.
/// </summary>
public static class LightInjectExtensions
{
/// <summary>
/// Registers the TService with the factory that describes the dependencies of the service, as a singleton.
/// </summary>
public static void RegisterSingleton<TService>(this IServiceRegistry container, Func<IServiceFactory, TService> factory, string serviceName)
{
var registration = container.GetAvailableService<TService>(serviceName);
if (registration == null)
{
container.Register(factory, serviceName, new PerContainerLifetime());
}
else
{
if (registration.Lifetime is PerContainerLifetime == false)
throw new InvalidOperationException("Existing registration lifetime is not PerContainer.");
UpdateRegistration(registration, null, factory);
}
}
/// <summary>
/// Registers a service with an implementation as a singleton.
/// </summary>
public static void RegisterSingleton<TService, TImplementation>(this IServiceRegistry container)
where TImplementation : TService
{
container.RegisterSingleton(typeof(TService), typeof(TImplementation));
}
/// <summary>
/// Registers a service with an implementation as a singleton.
/// </summary>
public static void RegisterSingleton(this IServiceRegistry container, Type serviceType, Type implementingType)
{
var registration = container.GetAvailableService(serviceType);
if (registration == null)
{
container.Register(serviceType, implementingType, new PerContainerLifetime());
}
else
{
if (registration.Lifetime is PerContainerLifetime == false)
throw new InvalidOperationException("Existing registration lifetime is not PerContainer.");
UpdateRegistration(registration, implementingType, null);
}
}
/// <summary>
/// Registers a service with a named implementation as a singleton.
/// </summary>
public static void RegisterSingleton(this IServiceRegistry container, Type serviceType, Type implementingType, string name)
{
var registration = container.AvailableServices.FirstOrDefault(x => x.ServiceType == serviceType && x.ServiceName == name);
if (registration == null)
{
container.Register(serviceType, implementingType, name, new PerContainerLifetime());
}
else
{
if (registration.Lifetime is PerContainerLifetime == false)
throw new InvalidOperationException("Existing registration lifetime is not PerContainer.");
UpdateRegistration(registration, implementingType, null);
}
}
/// <summary>
/// Registers a concrete type as a singleton service.
/// </summary>
public static void RegisterSingleton<TImplementation>(this IServiceRegistry container)
{
container.RegisterSingleton(typeof(TImplementation));
}
/// <summary>
/// Registers a concrete type as a singleton service.
/// </summary>
public static void RegisterSingleton(this IServiceRegistry container, Type serviceType)
{
var registration = container.GetAvailableService(serviceType);
if (registration == null)
{
container.Register(serviceType, new PerContainerLifetime());
}
else
{
if (registration.Lifetime is PerContainerLifetime == false)
throw new InvalidOperationException("Existing registration lifetime is not PerContainer.");
UpdateRegistration(registration, serviceType, null);
}
}
/// <summary>
/// Registers the TService with the factory that describes the dependencies of the service, as a singleton.
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="container"></param>
/// <param name="factory"></param>
public static void RegisterSingleton<TService>(this IServiceRegistry container, Func<IServiceFactory, TService> factory)
{
var registration = container.GetAvailableService<TService>();
if (registration == null)
container.Register(factory, new PerContainerLifetime());
else
UpdateRegistration(registration, null, factory);
}
// note - what's below ALSO applies to non-singleton ie transient services
//
// see https://github.com/seesharper/LightInject/issues/133
//
// note: we *could* use tracking lifetimes for singletons to ensure they have not been resolved
// already but that would not work for transient as the transient lifetime is null (and that is
// optimized in LightInject)
//
// also, RegisterSingleton above is dangerous because ppl could still use Register with a
// PerContainerLifetime and it will not work + the default Register would not work either for other
// lifetimes
//
// all in all, not sure we want to let ppl have direct access to the container
// we might instead want to expose some methods in UmbracoComponentBase or whatever?
/// <summary>
/// Updates a registration.
/// </summary>
private static void UpdateRegistration(ServiceRegistration registration, Type implementingType, Delegate factoryExpression)
{
// if the container has compiled already then the registrations have been captured,
// and re-registering - although updating available services - does not modify the
// output of GetInstance
//
// so we have to rely on different methods
//
// assuming the service has NOT been resolved, both methods below work, but the first
// one looks simpler. it would be good to check whether the service HAS been resolved
// but I am not sure how to do it right now, depending on the lifetime
//
// if the service HAS been resolved then updating is probably a bad idea
// not sure which is best? that one works, though, and looks simpler
registration.ImplementingType = implementingType;
registration.FactoryExpression = factoryExpression;
//container.Override(
// r => r.ServiceType == typeof (TService) && (registration.ServiceName == null || r.ServiceName == registration.ServiceName),
// (f, r) =>
// {
// r.ImplementingType = implementingType;
// r.FactoryExpression = factoryExpression;
// return r;
// });
}
/// <summary>
/// Gets the unique available service registration for a service type.
/// </summary>
/// <typeparam name="TService">The service type.</typeparam>
/// <param name="container">The container.</param>
/// <returns>The unique service registration for the service type.</returns>
/// <remarks>Can return <c>null</c>, but throws if more than one registration exist for the service type.</remarks>
private static ServiceRegistration GetAvailableService<TService>(this IServiceRegistry container)
{
var typeofTService = typeof(TService);
return container.AvailableServices.SingleOrDefault(x => x.ServiceType == typeofTService);
}
/// <summary>
/// Gets the unique available service registration for a service type.
/// </summary>
private static ServiceRegistration GetAvailableService(this IServiceRegistry container, Type serviceType)
{
return container.AvailableServices.SingleOrDefault(x => x.ServiceType == serviceType);
}
/// <summary>
/// Gets the unique available service registration for a service type and a name.
/// </summary>
/// <typeparam name="TService">The service type.</typeparam>
/// <param name="container">The container.</param>
/// <param name="name">The name.</param>
/// <returns>The unique service registration for the service type and the name.</returns>
/// <remarks>Can return <c>null</c>, but throws if more than one registration exist for the service type and the name.</remarks>
private static ServiceRegistration GetAvailableService<TService>(this IServiceRegistry container, string name)
{
var typeofTService = typeof(TService);
return container.AvailableServices.SingleOrDefault(x => x.ServiceType == typeofTService && x.ServiceName == name);
}
}
}