AB4227 - Moved a mix of files
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using LightInject;
|
||||
|
||||
namespace Umbraco.Core.Composing.LightInject
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements DI with LightInject.
|
||||
/// </summary>
|
||||
public class LightInjectContainer : IRegister, IFactory, IDisposable
|
||||
{
|
||||
private int _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectContainer"/> with a LightInject container.
|
||||
/// </summary>
|
||||
protected LightInjectContainer(ServiceContainer container)
|
||||
{
|
||||
Container = container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="LightInjectContainer"/> class.
|
||||
/// </summary>
|
||||
public static LightInjectContainer Create()
|
||||
=> new LightInjectContainer(CreateServiceContainer());
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the LightInject service container.
|
||||
/// </summary>
|
||||
protected static ServiceContainer CreateServiceContainer()
|
||||
{
|
||||
var container = new ServiceContainer(new ContainerOptions { EnablePropertyInjection = false });
|
||||
|
||||
// note: the block below is disabled, as it is too LightInject-specific
|
||||
//
|
||||
// supports annotated constructor injections
|
||||
// eg to specify the service name on some services
|
||||
//container.EnableAnnotatedConstructorInjection();
|
||||
|
||||
// note: the block below is disabled, we do not allow property injection at all anymore
|
||||
// (see options in CreateServiceContainer)
|
||||
//
|
||||
// from the docs: "LightInject considers all read/write properties a dependency, but implements
|
||||
// a loose strategy around property dependencies, meaning that it will NOT throw an exception
|
||||
// in the case of an unresolved property dependency."
|
||||
//
|
||||
// in Umbraco we do NOT want to do property injection by default, so we have to disable it.
|
||||
// from the docs, the following line will cause the container to "now only try to inject
|
||||
// dependencies for properties that is annotated with the InjectAttribute."
|
||||
//
|
||||
// could not find it documented, but tests & code review shows that LightInject considers a
|
||||
// property to be "injectable" when its setter exists and is not static, nor private, nor
|
||||
// it is an index property. which means that eg protected or internal setters are OK.
|
||||
//Container.EnableAnnotatedPropertyInjection();
|
||||
|
||||
// ensure that we do *not* scan assemblies
|
||||
// we explicitly RegisterFrom our own composition roots and don't want them scanned
|
||||
container.AssemblyScanner = new AssemblyScanner(/*container.AssemblyScanner*/);
|
||||
|
||||
// see notes in MixedLightInjectScopeManagerProvider
|
||||
container.ScopeManagerProvider = new MixedLightInjectScopeManagerProvider();
|
||||
|
||||
// note: the block below is disabled, because it does not work, because collection builders
|
||||
// are singletons, and constructor dependencies don't work on singletons, see
|
||||
// https://github.com/seesharper/LightInject/issues/294
|
||||
//
|
||||
// if looking for a IContainer, and one was passed in args, use it
|
||||
// this is for collection builders which require the IContainer
|
||||
//container.RegisterConstructorDependency((c, i, a) => a.OfType<IContainer>().FirstOrDefault());
|
||||
//
|
||||
// and, the block below is also disabled, because it is ugly
|
||||
//
|
||||
//// which means that the only way to inject the container into builders is to register it
|
||||
//container.RegisterInstance<IContainer>(this);
|
||||
//
|
||||
// instead, we use an explicit GetInstance with arguments implementation
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LightInject container.
|
||||
/// </summary>
|
||||
protected ServiceContainer Container { get; }
|
||||
|
||||
/// <inheritdoc cref="IRegister"/>
|
||||
/// <inheritdoc cref="IFactory"/>
|
||||
public object Concrete => Container;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, 1) == 1)
|
||||
return;
|
||||
|
||||
Container.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFactory CreateFactory() => this;
|
||||
|
||||
private static string GetTargetedServiceName<TTarget>() => "TARGET:" + typeof(TTarget).FullName;
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetInstance(Type type)
|
||||
=> Container.GetInstance(type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public TService GetInstanceFor<TService, TTarget>()
|
||||
=> Container.GetInstance<TService>(GetTargetedServiceName<TTarget>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public object TryGetInstance(Type type)
|
||||
=> Container.TryGetInstance(type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<T> GetAllInstances<T>()
|
||||
where T : class
|
||||
=> Container.GetAllInstances<T>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> GetAllInstances(Type type)
|
||||
=> Container.GetAllInstances(type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Release(object instance)
|
||||
{
|
||||
// nothing to release with LightInject
|
||||
}
|
||||
|
||||
// notes:
|
||||
// we may want to look into MS code, eg:
|
||||
// TypeActivatorCache in MVC at https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Internal/TypeActivatorCache.cs
|
||||
// which relies onto
|
||||
// ActivatorUtilities at https://github.com/aspnet/DependencyInjection/blob/master/shared/Microsoft.Extensions.ActivatorUtilities.Sources/ActivatorUtilities.cs
|
||||
|
||||
#endregion
|
||||
|
||||
#region Registry
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient)
|
||||
=> Container.Register(serviceType, GetLifetime(lifetime));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case Lifetime.Transient:
|
||||
Container.Register(serviceType, implementingType, implementingType.Name);
|
||||
break;
|
||||
case Lifetime.Request:
|
||||
case Lifetime.Scope:
|
||||
case Lifetime.Singleton:
|
||||
Container.Register(serviceType, implementingType, GetLifetime(lifetime));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"Lifetime {lifetime} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
{
|
||||
Container.Register(f => factory(this), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(Type serviceType, object instance)
|
||||
=> Container.RegisterInstance(serviceType, instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> RegisterFor<TService, TTarget>(typeof(TService), lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(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<TTarget>(), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> 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<TTarget>(), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(TService instance)
|
||||
where TService : class
|
||||
=> Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName<TTarget>());
|
||||
|
||||
private ILifetime GetLifetime(Lifetime lifetime)
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case Lifetime.Transient:
|
||||
return null;
|
||||
case Lifetime.Request:
|
||||
return new PerRequestLifeTime();
|
||||
case Lifetime.Scope:
|
||||
return new PerScopeLifetime();
|
||||
case Lifetime.Singleton:
|
||||
return new PerContainerLifetime();
|
||||
default:
|
||||
throw new NotSupportedException($"Lifetime {lifetime} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterAuto(Type serviceBaseType)
|
||||
{
|
||||
Container.RegisterFallback((serviceType, serviceName) =>
|
||||
{
|
||||
// https://github.com/seesharper/LightInject/issues/173
|
||||
if (serviceBaseType.IsAssignableFromGtd(serviceType))
|
||||
Container.Register(serviceType);
|
||||
return false;
|
||||
}, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Control
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable BeginScope()
|
||||
=> Container.BeginScope();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ConfigureForWeb()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void EnablePerWebRequestScope()
|
||||
{
|
||||
if (!(Container.ScopeManagerProvider is MixedLightInjectScopeManagerProvider smp))
|
||||
throw new Exception("Container.ScopeManagerProvider is not MixedLightInjectScopeManagerProvider.");
|
||||
smp.EnablePerWebRequestScope();
|
||||
}
|
||||
|
||||
private class AssemblyScanner : IAssemblyScanner
|
||||
{
|
||||
public void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func<ILifetime> lifetime, Func<Type, Type, bool> shouldRegister, Func<Type, Type, string> serviceNameProvider)
|
||||
{
|
||||
// nothing - we don't want LightInject to scan
|
||||
}
|
||||
|
||||
public void Scan(Assembly assembly, IServiceRegistry serviceRegistry)
|
||||
{
|
||||
// nothing - we don't want LightInject to scan
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Composing.LightInject
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents errors that occur due to LightInject.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class LightInjectException : Exception
|
||||
{
|
||||
private const string LightInjectUnableToResolveType = "Unable to resolve type:";
|
||||
private const string LightInjectUnresolvedDependency = "Unresolved dependency ";
|
||||
private const string LightInjectRequestedDependency = "[Requested dependency:";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
|
||||
/// </summary>
|
||||
public LightInjectException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public LightInjectException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public LightInjectException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected LightInjectException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Tries to throw the exception with additional details.
|
||||
/// </summary>
|
||||
/// <param name="e">The exception.</param>
|
||||
/// <exception cref="Umbraco.Core.Composing.LightInject.LightInjectException"></exception>
|
||||
public static void TryThrow(Exception e)
|
||||
{
|
||||
var ex = e as InvalidOperationException;
|
||||
if (ex == null || ex.Message.StartsWith(LightInjectUnableToResolveType) == false)
|
||||
return;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Unresolved type: " + ex.Message.Substring(LightInjectUnableToResolveType.Length));
|
||||
WriteDetails(ex, sb);
|
||||
throw new LightInjectException(sb.ToString(), e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to throw the exception with additional details.
|
||||
/// </summary>
|
||||
/// <param name="e">The exception.</param>
|
||||
/// <param name="implementingType">The implementing type.</param>
|
||||
/// <exception cref="Umbraco.Core.Composing.LightInject.LightInjectException"></exception>
|
||||
public static void TryThrow(Exception e, Type implementingType)
|
||||
{
|
||||
var ex = e as InvalidOperationException;
|
||||
if (ex == null || ex.Message.StartsWith(LightInjectUnableToResolveType) == false)
|
||||
return;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Unresolved type: " + ex.Message.Substring(LightInjectUnableToResolveType.Length));
|
||||
sb.AppendLine("Implementing type: " + implementingType);
|
||||
WriteDetails(ex, sb);
|
||||
throw new LightInjectException(sb.ToString(), e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the details.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception.</param>
|
||||
/// <param name="sb">The <see cref="StringBuilder" /> to write the details to.</param>
|
||||
private static void WriteDetails(InvalidOperationException ex, StringBuilder sb)
|
||||
{
|
||||
ex = ex.InnerException as InvalidOperationException;
|
||||
while (ex != null)
|
||||
{
|
||||
var message = ex.Message;
|
||||
|
||||
if (message.StartsWith(LightInjectUnableToResolveType))
|
||||
{
|
||||
sb.AppendLine("-> Unresolved type: " + message.Substring(LightInjectUnableToResolveType.Length));
|
||||
}
|
||||
else if (message.StartsWith(LightInjectUnresolvedDependency))
|
||||
{
|
||||
var pos = message.InvariantIndexOf(LightInjectRequestedDependency);
|
||||
sb.AppendLine("-> Unresolved dependency: " + message.Substring(pos + LightInjectRequestedDependency.Length + 1).TrimEnd(']'));
|
||||
}
|
||||
|
||||
ex = ex.InnerException as InvalidOperationException;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using LightInject;
|
||||
using LightInject.Web;
|
||||
|
||||
namespace Umbraco.Core.Composing.LightInject
|
||||
{
|
||||
// by default, the container's scope manager provider is PerThreadScopeManagerProvider,
|
||||
// and then container.EnablePerWebRequestScope() replaces it with PerWebRequestScopeManagerProvider,
|
||||
// however if any delegate has been compiled already at that point, it captures the scope
|
||||
// manager provider and changing it afterwards has no effect for that delegate.
|
||||
//
|
||||
// therefore, Umbraco uses the mixed scope manager provider, which initially wraps an instance
|
||||
// of PerThreadScopeManagerProvider and then can replace that wrapped instance with an instance
|
||||
// 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!
|
||||
|
||||
public class MixedLightInjectScopeManagerProvider : IScopeManagerProvider
|
||||
{
|
||||
private IScopeManagerProvider _provider;
|
||||
|
||||
public MixedLightInjectScopeManagerProvider()
|
||||
{
|
||||
_provider = new PerThreadScopeManagerProvider();
|
||||
}
|
||||
|
||||
public void EnablePerWebRequestScope()
|
||||
{
|
||||
if (_provider is PerWebRequestScopeManagerProvider) return;
|
||||
_provider = new PerWebRequestScopeManagerProvider();
|
||||
}
|
||||
|
||||
public IScopeManager GetScopeManager(IServiceFactory factory)
|
||||
{
|
||||
return _provider.GetScopeManager(factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user