Resvolution - Prepare

This commit is contained in:
Stephan
2016-08-16 10:00:14 +02:00
parent dbdff2e2c4
commit 0817ffdd28
11 changed files with 307 additions and 6 deletions

View File

@@ -1,10 +1,12 @@
using LightInject;
using System.Collections.Generic;
using LightInject;
using Umbraco.Core.Strings;
namespace Umbraco.Core
{
// must remain internal - this class is here to support the transition from singletons
// and resolvers to injection - by providing a static access to singleton services - it
// is initialized once with a service container, in WebBootManager.
// is initialized once with a service container, in CoreBootManager.
internal class Current
{
public static IServiceContainer Container { get; set; } // ok to set - don't be stupid

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Represents an injected collection, ie an immutable enumeration of items.
/// </summary>
/// <typeparam name="TItem">The type of the items.</typeparam>
public interface IInjectCollection<out TItem>
{
/// <summary>
/// Gets the items in the collection.
/// </summary>
IEnumerable<TItem> Items { get; }
}
}

View File

@@ -0,0 +1,18 @@
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Represents an injected collection builder.
/// </summary>
/// <typeparam name="TCollection">The type of the collection.</typeparam>
/// <typeparam name="TItem">The type of the items.</typeparam>
public interface IInjectCollectionBuilder<out TCollection, TItem>
where TCollection : IInjectCollection<TItem>
{
/// <summary>
/// Gets a collection.
/// </summary>
/// <returns>A collection.</returns>
/// <remarks>The lifetime of the collection depends on how the builder was registered.</remarks>
TCollection GetCollection();
}
}

View File

@@ -0,0 +1,23 @@
using System.Collections.Generic;
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Provides a base class for injected collections.
/// </summary>
/// <typeparam name="TItem">The type of the items.</typeparam>
public abstract class InjectCollectionBase<TItem> : IInjectCollection<TItem>
{
/// <summary>
/// Initializes a new instance of the <see cref="InjectCollectionBase{TItem}"/> with items.
/// </summary>
/// <param name="items">The items.</param>
protected InjectCollectionBase(IEnumerable<TItem> items)
{
Items = items;
}
/// <inheritdoc />
public IEnumerable<TItem> Items { get; }
}
}

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LightInject;
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Provides a base class for injected collection builders.
/// </summary>
/// <typeparam name="TCollection">The type of the collection.</typeparam>
/// <typeparam name="TItem">The type of the items.</typeparam>
public abstract class InjectCollectionBuilderBase<TCollection, TItem> : IInjectCollectionBuilder<TCollection, TItem>
where TCollection : IInjectCollection<TItem>
{
private readonly IServiceContainer _container;
private readonly List<Type> _types = new List<Type>();
private readonly object _locker = new object();
private bool _typesRegistered;
/// <summary>
/// Initializes a new instance of the <see cref="InjectCollectionBuilderBase{TCollection, TItem}"/>
/// class with a service container.
/// </summary>
/// <param name="container">A service container.</param>
protected InjectCollectionBuilderBase(IServiceContainer container)
{
_container = container;
}
// everything could be implemented, add, insert, remove, replace, order, whatever...
// and we could also have 'lazy' collections by supplying a types factory
protected void Configure(Action action)
{
lock (_locker)
{
if (_typesRegistered) throw new InvalidOperationException();
action();
}
}
public void Add<T>()
where T : TItem
{
Configure(() =>
{
var type = typeof(T);
if (!_types.Contains(type))
_types.Add(type);
});
}
protected virtual IEnumerable<Type> PrepareTypes(IEnumerable<Type> types)
{
return types;
}
private void RegisterTypes()
{
lock (_locker)
{
if (_typesRegistered) return;
var prefix = GetType().FullName + "_";
var i = 0;
foreach (var type in PrepareTypes(_types))
{
var name = $"{prefix}{i++:00000}";
_container.Register(typeof(TItem), type, name);
}
_typesRegistered = true;
}
}
/// <summary>
/// Gets a collection.
/// </summary>
/// <returns>A collection.</returns>
/// <remarks>
/// <para>Creates a new collection each time it is invoked, but only registers types once.</para>
/// <para>Explicitely implemented in order to hide it to users.</para>
/// </remarks>
TCollection IInjectCollectionBuilder<TCollection, TItem>.GetCollection()
{
RegisterTypes(); // will do it only once
var prefix = GetType().FullName + "_";
// fixme - shouldn't we save this somewhere?
var services = _container.AvailableServices
.Where(x => x.ServiceName.StartsWith(prefix))
.OrderBy(x => x.ServiceName);
var items = services
.Select(x => _container.GetInstance<TItem>(x.ServiceName))
.ToArray();
return CreateCollection(items);
}
/// <summary>
/// Creates the injected collection.
/// </summary>
/// <param name="items">The items.</param>
/// <returns>An injected collection containing the items.</returns>
protected abstract TCollection CreateCollection(IEnumerable<TItem> items);
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LightInject;
namespace Umbraco.Core.DependencyInjection
{
public abstract class InjectLazyCollectionBuilderBase<TCollection, TItem> : InjectCollectionBuilderBase<TCollection, TItem>
where TCollection : IInjectCollection<TItem>
{
private readonly List<Func<IEnumerable<Type>>> _producers = new List<Func<IEnumerable<Type>>>();
private readonly List<Type> _excluded = new List<Type>();
protected InjectLazyCollectionBuilderBase(IServiceContainer container)
: base(container)
{ }
protected override IEnumerable<Type> PrepareTypes(IEnumerable<Type> types)
{
return types.Union(_producers.SelectMany(x => x())).Distinct().Except(_excluded);
}
public void AddProducer(Func<IEnumerable<Type>> producer)
{
Configure(() =>
{
_producers.Add(producer);
});
}
public void Exclude<T>()
{
Configure(() =>
{
var type = typeof(T);
if (!_excluded.Contains(type))
_excluded.Add(type);
});
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LightInject;
namespace Umbraco.Core.DependencyInjection
{
public abstract class InjectWeightedCollectionBuilderBase<TCollection, TItem> : InjectCollectionBuilderBase<TCollection, TItem>
where TCollection : IInjectCollection<TItem>
{
protected InjectWeightedCollectionBuilderBase(IServiceContainer container)
: base(container)
{ }
protected override IEnumerable<Type> PrepareTypes(IEnumerable<Type> types)
{
var list = types.ToList();
list.Sort((t1, t2) => GetWeight(t1).CompareTo(GetWeight(t2)));
return list;
}
protected virtual int DefaultWeight { get; set; } = 10;
protected virtual int GetWeight(Type type)
{
var attr = type.GetCustomAttributes(typeof(WeightAttribute), false).OfType<WeightAttribute>().SingleOrDefault();
return attr?.Weight ?? DefaultWeight;
}
}
}

View File

@@ -162,6 +162,36 @@ namespace Umbraco.Core.DependencyInjection
}
}
/// <summary>
/// Registers an injected collection.
/// </summary>
/// <typeparam name="TBuilder">The type of the collection builder.</typeparam>
/// <typeparam name="TCollection">The type of the collection.</typeparam>
/// <typeparam name="TItem">The type of the items.</typeparam>
/// <param name="container">A container.</param>
public static void RegisterCollection<TBuilder, TCollection, TItem>(this IServiceRegistry container)
where TCollection : IInjectCollection<TItem>
where TBuilder : IInjectCollectionBuilder<TCollection, TItem>
{
container.Register<TBuilder>(new PerContainerLifetime());
container.Register(factory => factory.GetInstance<TBuilder>().GetCollection());
}
/// <summary>
/// Registers an injected collection.
/// </summary>
/// <typeparam name="TBuilder">The type of the collection builder.</typeparam>
/// <typeparam name="TCollection">The type of the collection.</typeparam>
/// <typeparam name="TItem">The type of the items.</typeparam>
/// <typeparam name="TLifetime">A lifetime type.</typeparam>
/// <param name="container">A container.</param>
public static void RegisterCollection<TBuilder, TCollection, TItem, TLifetime>(this IServiceRegistry container)
where TCollection : IInjectCollection<TItem>
where TBuilder : IInjectCollectionBuilder<TCollection, TItem>
where TLifetime : ILifetime, new()
{
container.Register<TBuilder>(new PerContainerLifetime());
container.Register(factory => factory.GetInstance<TBuilder>().GetCollection(), new TLifetime());
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Specifies the weight of pretty much anything.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class WeightAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="WeightAttribute"/> class with a weight.
/// </summary>
/// <param name="weight"></param>
public WeightAttribute(int weight)
{
Weight = weight;
}
/// <summary>
/// Gets the weight value.
/// </summary>
public int Weight { get; }
}
}

View File

@@ -215,8 +215,15 @@
<Compile Include="DelegateExtensions.cs" />
<Compile Include="DependencyInjection\ConfigurationCompositionRoot.cs" />
<Compile Include="DependencyInjection\CoreModelMappersCompositionRoot.cs" />
<Compile Include="DependencyInjection\IInjectCollection.cs" />
<Compile Include="DependencyInjection\IInjectCollectionBuilder.cs" />
<Compile Include="DependencyInjection\InjectCollectionBase.cs" />
<Compile Include="DependencyInjection\InjectCollectionBuilderBase.cs" />
<Compile Include="DependencyInjection\InjectLazyCollectionBuilderBase.cs" />
<Compile Include="DependencyInjection\InjectWeightedCollectionBuilderBase.cs" />
<Compile Include="DependencyInjection\RepositoryCompositionRoot.cs" />
<Compile Include="DependencyInjection\ServicesCompositionRoot.cs" />
<Compile Include="DependencyInjection\WeightAttribute.cs" />
<Compile Include="DictionaryExtensions.cs" />
<Compile Include="Dictionary\CultureDictionaryFactoryResolver.cs" />
<Compile Include="Dictionary\ICultureDictionaryFactory.cs" />
@@ -1328,7 +1335,6 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\" />
<Folder Include="PublishedCache\" />
</ItemGroup>
<ItemGroup />

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using LightInject;
using Umbraco.Core.Events;
using Umbraco.Core.Strings;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web
@@ -32,7 +34,6 @@ namespace Umbraco.Web
set { _umbracoContextAccessor = value; } // for tests
}
public static IFacadeAccessor FacadeAccessor
{
get
@@ -44,7 +45,6 @@ namespace Umbraco.Web
set { _facadeAccessor = value; } // for tests
}
public static UmbracoContext UmbracoContext => UmbracoContextAccessor.UmbracoContext;
// have to support set for now, because of 'ensure umbraco context' which can create