using System;
using System.Collections.Generic;
using System.Linq;
using LightInject;
namespace Umbraco.Core.DependencyInjection
{
///
/// Provides a base class for injected collection builders.
///
/// The type of the collection.
/// The type of the items.
public abstract class InjectCollectionBuilderBase : IInjectCollectionBuilder
where TCollection : IInjectCollection
{
private readonly IServiceContainer _container;
private readonly List _types = new List();
private readonly object _locker = new object();
private bool _typesRegistered;
///
/// Initializes a new instance of the
/// class with a service container.
///
/// A service container.
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()
where T : TItem
{
Configure(() =>
{
var type = typeof(T);
if (!_types.Contains(type))
_types.Add(type);
});
}
protected virtual IEnumerable PrepareTypes(IEnumerable 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;
}
}
///
/// Gets a collection.
///
/// A collection.
///
/// Creates a new collection each time it is invoked, but only registers types once.
/// Explicitely implemented in order to hide it to users.
///
TCollection IInjectCollectionBuilder.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(x.ServiceName))
.ToArray();
return CreateCollection(items);
}
///
/// Creates the injected collection.
///
/// The items.
/// An injected collection containing the items.
protected abstract TCollection CreateCollection(IEnumerable items);
}
}