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); } }