Cleanup IContainer
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -9,10 +10,13 @@ namespace Umbraco.Core.Composing
|
||||
/// </summary>
|
||||
public static class ContainerExtensions
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, Dictionary<string, Func<object, object>>> ArgumentPropertyGetters
|
||||
= new ConcurrentDictionary<Type, Dictionary<string, Func<object, object>>>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance.
|
||||
/// Gets an instance of a service.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the instance.</typeparam>
|
||||
/// <typeparam name="T">The type of the service.</typeparam>
|
||||
/// <param name="container">The container.</param>
|
||||
/// <returns>An instance of the specified type.</returns>
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
@@ -20,20 +24,20 @@ namespace Umbraco.Core.Composing
|
||||
=> (T) container.GetInstance(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a named instance.
|
||||
/// Gets an instance of a named service.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the instance.</typeparam>
|
||||
/// <typeparam name="T">The type of the service.</typeparam>
|
||||
/// <param name="container">The container.</param>
|
||||
/// <param name="name">The name of the instance.</typeparam>
|
||||
/// <param name="name">The name of the service.</param>
|
||||
/// <returns>An instance of the specified type and name.</returns>
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
public static T GetInstance<T>(this IContainer container, string name)
|
||||
=> (T) container.GetInstance(typeof(T), name);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get an instance.
|
||||
/// Tries to get an instance of a service.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the instance.</typeparam>
|
||||
/// <typeparam name="T">The type of the service.</typeparam>
|
||||
/// <returns>An instance of the specified type, or null.</returns>
|
||||
/// <remarks>Returns null if the container does not know how to get an instance
|
||||
/// of the specified type. Throws an exception if the container does know how
|
||||
@@ -42,7 +46,7 @@ namespace Umbraco.Core.Composing
|
||||
=> (T) container.TryGetInstance(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Gets registration for a service.
|
||||
/// Gets registrations for a service.
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">The type of the service.</typeparam>
|
||||
/// <returns>The registrations for the service.</returns>
|
||||
@@ -63,6 +67,32 @@ namespace Umbraco.Core.Composing
|
||||
public static T CreateInstance<T>(this IContainer container, IDictionary<string, object> args)
|
||||
=> (T) container.CreateInstance(typeof(T), args);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance with arguments.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the instance.</typeparam>
|
||||
/// <param name="container">The container.</param>
|
||||
/// <param name="args">Arguments.</param>
|
||||
/// <returns>An instance of the specified type.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Throws an exception if the container failed to get an instance of the specified type.</para>
|
||||
/// <para>The arguments are used as dependencies by the container.</para>
|
||||
/// </remarks>
|
||||
public static T CreateInstance<T>(this IContainer container, object args)
|
||||
{
|
||||
var typeOfArgs = args.GetType();
|
||||
var getters = ArgumentPropertyGetters.GetOrAdd(typeOfArgs, type =>
|
||||
args.GetType()
|
||||
.GetProperties()
|
||||
.ToDictionary(x => x.Name, x => ReflectionUtilities.EmitMethodUnsafe<Func<object, object>>(x.GetMethod)));
|
||||
|
||||
var argsDictionary = new Dictionary<string, object>();
|
||||
foreach (var (name, getter) in getters)
|
||||
argsDictionary[name] = getter(args);
|
||||
|
||||
return (T) container.CreateInstance(typeof(T), argsDictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with an implementation type.
|
||||
/// </summary>
|
||||
@@ -70,15 +100,15 @@ namespace Umbraco.Core.Composing
|
||||
=> container.Register(typeof(TService), typeof(TImplementing), lifetime);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with a named implementation type.
|
||||
/// Registers a named service with an implementation type.
|
||||
/// </summary>
|
||||
public static void Register<TService, TImplementing>(this IContainer container, string name, Lifetime lifetime = Lifetime.Transient)
|
||||
=> container.Register(typeof(TService), typeof(TImplementing), name, lifetime);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with a named implementation type and factory.
|
||||
/// Registers a named service with an implementation factory.
|
||||
/// </summary>
|
||||
public static void Register<TService, TImplementing>(this IContainer container, string name, Func<IContainer, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
public static void Register<TService>(this IContainer container, string name, Func<IContainer, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
=> container.Register(factory, name, lifetime);
|
||||
|
||||
/// <summary>
|
||||
@@ -107,7 +137,6 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
/// <summary>
|
||||
/// Registers a named singleton service with an implementation factory.
|
||||
/// Note to implementors: The last registered component must be the default.
|
||||
/// </summary>
|
||||
public static void RegisterSingleton<TService>(this IContainer container, string name, Func<IContainer, TService> factory)
|
||||
=> container.Register(factory, name, Lifetime.Singleton);
|
||||
@@ -125,7 +154,7 @@ namespace Umbraco.Core.Composing
|
||||
=> container.RegisterAuto(typeof(TServiceBase));
|
||||
|
||||
/// <summary>
|
||||
/// Registers and instanciates a collection builder.
|
||||
/// Registers and instantiates a collection builder.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the collection builder.</typeparam>
|
||||
/// <returns>A collection builder of the specified type.</returns>
|
||||
@@ -137,7 +166,8 @@ namespace Umbraco.Core.Composing
|
||||
throw new InvalidOperationException("Collection builders should be registered only once.");
|
||||
|
||||
// register the builder
|
||||
container.RegisterSingleton<TBuilder>();
|
||||
// use a factory so we don't have to self-register the container
|
||||
container.RegisterSingleton(factory => factory.CreateInstance<TBuilder>(new Dictionary<string, object> {{ "container", container }} ));
|
||||
|
||||
// initialize and return the builder
|
||||
return container.GetInstance<TBuilder>();
|
||||
|
||||
@@ -50,6 +50,10 @@ namespace Umbraco.Core.Composing
|
||||
if (container == null)
|
||||
throw new Exception($"Container factory '{configuredTypeName}' did not return an IContainer implementation.");
|
||||
|
||||
// self-register the container - this is where it should happen
|
||||
// but - we do NOT want to do it!
|
||||
//container.RegisterInstance(container);
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
// Implementing IContainer:
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// The registry
|
||||
// - 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
|
||||
|
||||
/// <summary>
|
||||
/// Defines a container for Umbraco.
|
||||
/// </summary>
|
||||
@@ -15,32 +31,27 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
#region Factory
|
||||
|
||||
// notes
|
||||
// when implementing IContainer, the following rules apply
|
||||
// - always pick the constructor with the most parameters
|
||||
// - always prefer Lazy parameters over non-Lazy in constructors
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance.
|
||||
/// Gets an instance of a service.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the instance.</param>
|
||||
/// <param name="type">The type of the service.</param>
|
||||
/// <returns>An instance of the specified type.</returns>
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
object GetInstance(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a named instance.
|
||||
/// Gets an instance of a named service.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the instance.</param>
|
||||
/// <param name="name">The name of the instance.</param>
|
||||
/// <param name="type">The type of the service.</param>
|
||||
/// <param name="name">The name of the service.</param>
|
||||
/// <returns>An instance of the specified type.</returns>
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
object GetInstance(Type type, string name);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get an instance.
|
||||
/// Tries to get an instance of a service.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the instance.</param>
|
||||
/// <param name="type">The type of the service.</param>
|
||||
/// <returns>An instance of the specified type, or null.</returns>
|
||||
/// <remarks>Returns null if the container does not know how to get an instance
|
||||
/// of the specified type. Throws an exception if the container does know how
|
||||
@@ -60,14 +71,14 @@ namespace Umbraco.Core.Composing
|
||||
IEnumerable<TService> GetAllInstances<TService>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets registration for a service.
|
||||
/// Gets registrations for a service.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">The type of the service.</param>
|
||||
/// <returns>The registrations for the service.</returns>
|
||||
IEnumerable<Registration> GetRegistered(Type serviceType);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance with arguments.
|
||||
/// Creates an instance of a service, with arguments.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the instance.</param>
|
||||
/// <param name="args">Named arguments.</param>
|
||||
@@ -79,16 +90,21 @@ namespace Umbraco.Core.Composing
|
||||
/// </remarks>
|
||||
object CreateInstance(Type type, IDictionary<string, object> args);
|
||||
|
||||
/// <summary>
|
||||
/// Releases an instance.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <remarks>
|
||||
/// See https://stackoverflow.com/questions/14072208 and http://kozmic.net/2010/08/27/must-i-release-everything-when-using-windsor/,
|
||||
/// you only need to release instances you specifically resolved, and even then, if done right, that might never be needed. For
|
||||
/// instance, LightInject does not require this and does not support it - should work with scopes.
|
||||
/// </remarks>
|
||||
void Release(object instance);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Registry
|
||||
|
||||
// notes
|
||||
// when implementing IContainer, the following rules apply
|
||||
// - it is possible to register a service, even after some instances of other services have been created
|
||||
// - it is possible to re-register a service, as long as an instance of that service has not been created
|
||||
// - once an instance of a service has been created, it is not possible to change its registration
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service as its own implementation.
|
||||
/// </summary>
|
||||
@@ -100,7 +116,7 @@ namespace Umbraco.Core.Composing
|
||||
void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with a named implementation type.
|
||||
/// Registers a named service with a implementation type.
|
||||
/// </summary>
|
||||
void Register(Type serviceType, Type implementingType, string name, Lifetime lifetime = Lifetime.Transient);
|
||||
|
||||
@@ -120,7 +136,7 @@ namespace Umbraco.Core.Composing
|
||||
void RegisterInstance(Type serviceType, object instance);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with a named implementing instance.
|
||||
/// Registers a named service with an implementing instance.
|
||||
/// </summary>
|
||||
void RegisterInstance(Type serviceType, object instance, string name);
|
||||
|
||||
@@ -139,6 +155,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <summary>
|
||||
/// Registers a service with an ordered set of implementation types.
|
||||
/// </summary>
|
||||
// fixme: once we merge the installer refactoring, kill that one
|
||||
void RegisterOrdered(Type serviceType, Type[] implementingTypes, Lifetime lifetime = Lifetime.Transient);
|
||||
|
||||
#endregion
|
||||
@@ -174,7 +191,5 @@ namespace Umbraco.Core.Composing
|
||||
IContainer EnablePerWebRequestScope();
|
||||
|
||||
#endregion
|
||||
|
||||
void Release(object instance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
protected LightInjectContainer(ServiceContainer container)
|
||||
{
|
||||
Container = container;
|
||||
|
||||
container.RegisterSingleton<IContainer>(_ => this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -154,6 +152,12 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
return ctor.Invoke(ctorArgs);
|
||||
}
|
||||
|
||||
/// <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
|
||||
@@ -318,11 +322,6 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
|
||||
#endregion
|
||||
|
||||
public void Release(object instance)
|
||||
{
|
||||
// fixme - no idea how to do this with LI
|
||||
}
|
||||
|
||||
#region Control
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
@@ -553,6 +554,22 @@ namespace Umbraco.Tests.Clr
|
||||
|
||||
// fixme - missing tests specifying 'returned' on method, property
|
||||
|
||||
[Test]
|
||||
public void DeconstructAnonymousType()
|
||||
{
|
||||
var o = new { a = 1, b = "hello" };
|
||||
|
||||
var getters = new Dictionary<string, Func<object, object>>();
|
||||
foreach (var prop in o.GetType().GetProperties())
|
||||
getters[prop.Name] = ReflectionUtilities.EmitMethodUnsafe<Func<object, object>>(prop.GetMethod);
|
||||
|
||||
Assert.AreEqual(2, getters.Count);
|
||||
Assert.IsTrue(getters.ContainsKey("a"));
|
||||
Assert.IsTrue(getters.ContainsKey("b"));
|
||||
Assert.AreEqual(1, getters["a"](o));
|
||||
Assert.AreEqual("hello", getters["b"](o));
|
||||
}
|
||||
|
||||
#region IL Code
|
||||
|
||||
// these functions can be examined in eg DotPeek to understand IL works
|
||||
|
||||
Reference in New Issue
Block a user