diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index b7630013ed..ce54c8ef03 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -53,7 +53,7 @@ namespace Umbraco.Core get { return _umbracoApplication; } } - internal ServiceContainer Container + protected ServiceContainer Container { get { return _umbracoApplication.Container; } } @@ -115,7 +115,7 @@ namespace Umbraco.Core // completed at the end of the boot process to allow garbage collection _appStartupEvtContainer = Container.Clone(); _appStartupEvtContainer.BeginScope(); - _appStartupEvtContainer.RegisterBuilderCollection(PluginManager.ResolveApplicationStartupHandlers()); + _appStartupEvtContainer.RegisterCollection(PluginManager.ResolveApplicationStartupHandlers()); //build up standard IoC services ConfigureApplicationServices(Container); @@ -154,22 +154,17 @@ namespace Umbraco.Core /// internal virtual void ConfigureCoreServices(ServiceContainer container) { - // configure the temp. Current - Current.CurrentContainer = container; - - container.Register(factory => container); - //Logging - container.RegisterSingleton(factory => _umbracoApplication.Logger); - container.RegisterSingleton(factory => ProfilingLogger.Profiler); - container.RegisterSingleton(factory => ProfilingLogger); + container.RegisterInstance(_umbracoApplication.Logger); + container.RegisterInstance(ProfilingLogger.Profiler); + container.RegisterInstance(ProfilingLogger); //Config container.RegisterFrom(); //Cache - container.RegisterSingleton(factory => ApplicationCache); - container.RegisterSingleton(factory => ApplicationCache.RuntimeCache); + container.RegisterInstance(ApplicationCache); + container.RegisterInstance(ApplicationCache.RuntimeCache); //Datalayer/Repositories/SQL/Database/etc... container.RegisterFrom(); @@ -182,7 +177,7 @@ namespace Umbraco.Core //TODO: Don't think we'll need this when the resolvers are all container resolvers container.RegisterSingleton(); - container.RegisterSingleton(factory => PluginManager); + container.RegisterInstance(PluginManager); container.RegisterSingleton(); container.Register(factory => FileSystemProviderManager.Current.GetFileSystemProvider()); diff --git a/src/Umbraco.Core/DependencyInjection/BuilderCollectionBase.cs b/src/Umbraco.Core/DependencyInjection/BuilderCollectionBase.cs index e90b162d9f..673fbf4ebc 100644 --- a/src/Umbraco.Core/DependencyInjection/BuilderCollectionBase.cs +++ b/src/Umbraco.Core/DependencyInjection/BuilderCollectionBase.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.DependencyInjection /// public IEnumerator GetEnumerator() { - return (IEnumerator) _items.GetEnumerator(); + return ((IEnumerable) _items).GetEnumerator(); } /// diff --git a/src/Umbraco.Core/DependencyInjection/CollectionBuilderBase.cs b/src/Umbraco.Core/DependencyInjection/CollectionBuilderBase.cs index 3621ef32f3..f04b70a649 100644 --- a/src/Umbraco.Core/DependencyInjection/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/DependencyInjection/CollectionBuilderBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using LightInject; namespace Umbraco.Core.DependencyInjection @@ -8,24 +9,88 @@ namespace Umbraco.Core.DependencyInjection /// /// Provides a base class for collection builders. /// + /// The type of the builder. /// The type of the collection. /// The type of the items. - public abstract class CollectionBuilderBase : ICollectionBuilder + public abstract class CollectionBuilderBase : ICollectionBuilder + where TBuilder: CollectionBuilderBase where TCollection : IBuilderCollection { - private readonly IServiceContainer _container; private readonly List _types = new List(); private readonly object _locker = new object(); - private bool _typesRegistered; + private Func, TCollection> _collectionCtor; + private ServiceRegistration[] _registrations; /// - /// Initializes a new instance of the + /// Initializes a new instance of the /// class with a service container. /// /// A service container. protected CollectionBuilderBase(IServiceContainer container) { - _container = container; + Container = container; + // ReSharper disable once DoNotCallOverridableMethodsInConstructor + Initialize(); + } + + /// + /// Gets the service container. + /// + protected IServiceContainer Container { get; } + + /// + /// Initializes a new instance of the builder. + /// + /// This is called by the constructor and, by default, registers the + /// collection automatically. + protected virtual void Initialize() + { + // compile the auto-collection constructor + var argType = typeof(IEnumerable); + var ctorArgTypes = new[] { argType }; + var constructor = typeof(TCollection).GetConstructor(ctorArgTypes); + if (constructor == null) throw new InvalidOperationException(); + var exprArg = Expression.Parameter(argType, "items"); + var exprNew = Expression.New(constructor, exprArg); + var expr = Expression.Lambda, TCollection>>(exprNew, exprArg); + _collectionCtor = expr.Compile(); + + // register the collection + Container.Register(_ => CreateCollection(), CollectionLifetime); + } + + /// + /// Gets the collection lifetime. + /// + /// Return null for transient collections. + protected virtual ILifetime CollectionLifetime => new PerContainerLifetime(); + + /// + /// Registers the collection builder into a service container. + /// + /// The service container. + /// The collection builder is registered with a "per container" lifetime, + /// and the collection is registered wiht a lifetime that is "per container" by + /// default but can be overriden by each builder implementation. + public static TBuilder Register(IServiceContainer container) + { + // register the builder - per container + var builderLifetime = new PerContainerLifetime(); + container.Register(builderLifetime); + + // return the builder + // also initializes the builder + return container.GetInstance(); + } + + /// + /// Gets the collection builder from a service container. + /// + /// The service container. + /// The collection builder. + public static TBuilder Get(IServiceContainer container) + { + return container.GetInstance(); } /// @@ -37,7 +102,8 @@ namespace Umbraco.Core.DependencyInjection { lock (_locker) { - if (_typesRegistered) throw new InvalidOperationException(); + if (_registrations != null) + throw new InvalidOperationException("Cannot configure a collection builder after its types have been resolved."); action(_types); } } @@ -56,51 +122,47 @@ namespace Umbraco.Core.DependencyInjection { lock (_locker) { - if (_typesRegistered) return; + if (_registrations != null) return; var prefix = GetType().FullName + "_"; var i = 0; foreach (var type in GetTypes(_types)) { var name = $"{prefix}{i++:00000}"; - _container.Register(typeof(TItem), type, name); + Container.Register(typeof(TItem), type, name); } - _typesRegistered = true; + _registrations = Container.AvailableServices + .Where(x => x.ServiceName.StartsWith(prefix)) + .OrderBy(x => x.ServiceName) + .ToArray(); } } /// - /// Gets a collection. + /// Creates the collection items. /// - /// 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 ICollectionBuilder.GetCollection() + /// The arguments. + /// The collection items. + protected virtual IEnumerable CreateItems(params object[] args) { 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); + var type = typeof (TItem); + return _registrations + .Select(x => (TItem) Container.GetInstance(type, x.ServiceName, args)) + .ToArray(); // safe } /// - /// Creates the injected collection. + /// Creates a collection. /// - /// The items. - /// An injected collection containing the items. - protected abstract TCollection CreateCollection(IEnumerable items); + /// A collection. + /// Creates a new collection each time it is invoked. + public virtual TCollection CreateCollection() + { + if (_collectionCtor == null) throw new InvalidOperationException("Collection auto-creation is not possible."); + return _collectionCtor(CreateItems()); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/DependencyInjection/Current.cs b/src/Umbraco.Core/DependencyInjection/Current.cs index 95f67dd45b..7d1c0ccc68 100644 --- a/src/Umbraco.Core/DependencyInjection/Current.cs +++ b/src/Umbraco.Core/DependencyInjection/Current.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using LightInject; using Umbraco.Core.Strings; @@ -8,20 +7,41 @@ namespace Umbraco.Core.DependencyInjection // 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 CoreBootManager. + // ideally, it should not exist. practically, time will tell. public static class Current { - internal static IServiceContainer CurrentContainer { get; set; } // ok to set - don't be stupid + private static ServiceContainer _container; - public static IServiceContainer Container + public static ServiceContainer Container { get { - if (CurrentContainer == null) throw new Exception("oops:container"); - return CurrentContainer; + if (_container == null) throw new Exception("No container has been set."); + return _container; + } + internal set // ok to set - don't be stupid + { + if (_container != null) throw new Exception("A container has already been set."); + _container = value; } } - public static IEnumerable UrlSegmentProviders + internal static bool HasContainer => _container != null; + + // for UNIT TESTS exclusively! + internal static void Reset() + { + _container = null; + Resetted?.Invoke(null, EventArgs.Empty); + } + + internal static event EventHandler Resetted; + + #region Getters + + public static UrlSegmentProviderCollection UrlSegmentProviders => Container.GetInstance(); + + #endregion } } diff --git a/src/Umbraco.Core/DependencyInjection/ICollectionBuilder.cs b/src/Umbraco.Core/DependencyInjection/ICollectionBuilder.cs index 8e642c58ef..da456b3094 100644 --- a/src/Umbraco.Core/DependencyInjection/ICollectionBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/ICollectionBuilder.cs @@ -9,10 +9,10 @@ namespace Umbraco.Core.DependencyInjection where TCollection : IBuilderCollection { /// - /// Gets a collection. + /// Creates a collection. /// /// A collection. - /// The lifetime of the collection depends on how the builder was registered. - TCollection GetCollection(); + /// Creates a new collection each time it is invoked. + TCollection CreateCollection(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/DependencyInjection/LazyCollectionBuilderBase.cs b/src/Umbraco.Core/DependencyInjection/LazyCollectionBuilderBase.cs index c14d844570..eacf72854f 100644 --- a/src/Umbraco.Core/DependencyInjection/LazyCollectionBuilderBase.cs +++ b/src/Umbraco.Core/DependencyInjection/LazyCollectionBuilderBase.cs @@ -11,11 +11,12 @@ namespace Umbraco.Core.DependencyInjection /// The type of the builder. /// The type of the collection. /// The type of the items. - public abstract class LazyCollectionBuilderBase : CollectionBuilderBase + public abstract class LazyCollectionBuilderBase : CollectionBuilderBase where TBuilder : LazyCollectionBuilderBase where TCollection : IBuilderCollection { - private readonly List>> _producers = new List>>(); + private readonly List>> _producers1 = new List>>(); + private readonly List>> _producers2 = new List>>(); private readonly List _excluded = new List(); protected LazyCollectionBuilderBase(IServiceContainer container) @@ -65,7 +66,21 @@ namespace Umbraco.Core.DependencyInjection { Configure(types => { - _producers.Add(producer); + _producers1.Add(producer); + }); + return This; + } + + /// + /// Adds a types producer to the collection. + /// + /// The types producer. + /// The builder. + public TBuilder AddProducer(Func> producer) + { + Configure(types => + { + _producers2.Add(producer); }); return This; } @@ -87,7 +102,11 @@ namespace Umbraco.Core.DependencyInjection protected override IEnumerable GetTypes(IEnumerable types) { - return types.Union(_producers.SelectMany(x => x())).Distinct().Except(_excluded); + return types + .Union(_producers1.SelectMany(x => x())) + .Union(_producers2.SelectMany(x => x(Container))) + .Distinct() + .Except(_excluded); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs b/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs index 92b7e81a49..ca73b48145 100644 --- a/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs +++ b/src/Umbraco.Core/DependencyInjection/LightInjectExtensions.cs @@ -7,6 +7,33 @@ namespace Umbraco.Core.DependencyInjection { internal static class LightInjectExtensions { + /// + /// Configure the container for Umbraco Core usage and assign to Current. + /// + /// The container. + /// The container is now the unique application container and is now accessible via Current.Container. + public static void ConfigureUmbracoCore(this ServiceContainer container) + { + // supports annotated constructor injections + // eg to specify the service name on some services + container.EnableAnnotatedConstructorInjection(); + + // 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." + container.EnableAnnotatedPropertyInjection(); + + // self-register + container.Register(_ => container); + + // configure the current container + Current.Container = container; + } + /// /// Registers the TService with the factory that describes the dependencies of the service, as a singleton. /// @@ -136,7 +163,7 @@ namespace Umbraco.Core.DependencyInjection /// This works as of 3.0.2.2: https://github.com/seesharper/LightInject/issues/68#issuecomment-70611055 /// but means that the explicit type is registered, not the implementing type /// - public static void RegisterBuilderCollection(this IServiceContainer container, IEnumerable implementationTypes) + public static void RegisterCollection(this IServiceContainer container, IEnumerable implementationTypes) where TLifetime : ILifetime, new() { foreach (var type in implementationTypes) @@ -154,50 +181,12 @@ namespace Umbraco.Core.DependencyInjection /// This works as of 3.0.2.2: https://github.com/seesharper/LightInject/issues/68#issuecomment-70611055 /// but means that the explicit type is registered, not the implementing type /// - public static void RegisterBuilderCollection(this IServiceContainer container, IEnumerable implementationTypes) + public static void RegisterCollection(this IServiceContainer container, IEnumerable implementationTypes) { foreach (var type in implementationTypes) { container.Register(type); } } - - /// - /// Registers an injected collection. - /// - /// The type of the builder. - /// The type of the collection. - /// The type of the items. - /// A container. - public static void RegisterBuilderCollection(this IServiceRegistry container) - where TBuilder : ICollectionBuilder - where TCollection : IBuilderCollection - { - // register the builder - container.Register(new PerContainerLifetime()); - - // register the collection - container.Register(factory => factory.GetInstance().GetCollection()); - } - - /// - /// Registers an injected collection. - /// - /// The type of the collection. - /// The type of the builder. - /// A lifetime type. - /// The type of the items. - /// A container. - public static void RegisterBuilderCollection(this IServiceRegistry container) - where TBuilder : ICollectionBuilder - where TCollection : IBuilderCollection - where TLifetime : ILifetime, new() - { - // register the builder - container.Register(new PerContainerLifetime()); - - // register the collection - container.Register(factory => factory.GetInstance().GetCollection(), new TLifetime()); - } } } diff --git a/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs b/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs index e79d2024a7..dc2dbe0ac9 100644 --- a/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/DependencyInjection/OrderedCollectionBuilderBase.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.DependencyInjection /// The type of the builder. /// The type of the collection. /// The type of the items. - public abstract class OrderedCollectionBuilderBase : CollectionBuilderBase + public abstract class OrderedCollectionBuilderBase : CollectionBuilderBase where TBuilder : OrderedCollectionBuilderBase where TCollection : IBuilderCollection { diff --git a/src/Umbraco.Core/DependencyInjection/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/DependencyInjection/WeightedCollectionBuilderBase.cs index ba6d9085dd..b6a14cae20 100644 --- a/src/Umbraco.Core/DependencyInjection/WeightedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/DependencyInjection/WeightedCollectionBuilderBase.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.DependencyInjection /// The type of the builder. /// The type of the collection. /// The type of the items. - public abstract class WeightedCollectionBuilderBase : CollectionBuilderBase + public abstract class WeightedCollectionBuilderBase : CollectionBuilderBase where TBuilder : WeightedCollectionBuilderBase where TCollection : IBuilderCollection { diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs index 7a9643c3b6..e306e076d3 100644 --- a/src/Umbraco.Core/UmbracoApplicationBase.cs +++ b/src/Umbraco.Core/UmbracoApplicationBase.cs @@ -5,6 +5,7 @@ using System.Web; using System.Web.Hosting; using log4net; using LightInject; +using Umbraco.Core.DependencyInjection; using Umbraco.Core.Logging; namespace Umbraco.Core @@ -23,7 +24,7 @@ namespace Umbraco.Core /// /// Umbraco application's IoC container /// - internal ServiceContainer Container { get; } + internal ServiceContainer Container { get; private set; } private ILogger _logger; @@ -32,18 +33,13 @@ namespace Umbraco.Core /// protected UmbracoApplicationBase() { - // create the container for the application, the boot managers are responsible for registrations - Container = new ServiceContainer(); - Container.EnableAnnotatedConstructorInjection(); - - // 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." + // beware! this code can run more that once + // so it is NOT the right place to initialize global stuff such as the container // - // 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." - Container.EnableAnnotatedPropertyInjection(); + // however, it is OK to initialize app-local stuff + + if (Current.HasContainer) + Container = Current.Container; } public event EventHandler ApplicationStarting; @@ -65,6 +61,11 @@ namespace Umbraco.Core /// internal void StartApplication(object sender, EventArgs e) { + // create the container for the application, and configure. + // the boot managers are responsible for registrations + Container = new ServiceContainer(); + Container.ConfigureUmbracoCore(); + //take care of unhandled exceptions - there is nothing we can do to // prevent the entire w3wp process to go down but at least we can try // and log the exception @@ -77,6 +78,7 @@ namespace Umbraco.Core if (isTerminating) msg += " (terminating)"; LogHelper.Error(msg, exception); }; + //boot up the application GetBootManager() .Initialize() diff --git a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs index 35c339c229..6e3acfec35 100644 --- a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Cache.DistributedCache public void Setup() { var container = new ServiceContainer(); - container.EnableAnnotatedConstructorInjection(); + container.ConfigureUmbracoCore(); ServerRegistrarResolver.Current = new ServerRegistrarResolver( new TestServerRegistrar()); @@ -125,11 +125,11 @@ namespace Umbraco.Tests.Cache.DistributedCache internal class TestServerMessenger : IServerMessenger { //used for tests - public List IntIdsRefreshed = new List(); + public List IntIdsRefreshed = new List(); public List GuidIdsRefreshed = new List(); public List IntIdsRemoved = new List(); public List PayloadsRemoved = new List(); - public List PayloadsRefreshed = new List(); + public List PayloadsRefreshed = new List(); public int CountOfFullRefreshes = 0; public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, TPayload[] payload) diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 275abcdbeb..a8b644ab93 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Cache.PublishedCache protected override void FreezeResolution() { var container = new ServiceContainer(); - container.EnableAnnotatedConstructorInjection(); + container.ConfigureUmbracoCore(); container.RegisterBuilderCollection(); container.GetInstance().Append(); @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Cache.PublishedCache PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(); base.FreezeResolution(); } - + //NOTE: This is "Without_Examine" too [Test] public void Get_Root_Docs() @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var mRoot2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot2", mType, user, -1); var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot1.Id); var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot2.Id); - + var ctx = GetUmbracoContext("/test", 1234); var cache = new PublishedMediaCache(new XmlStore((XmlDocument) null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache); var roots = cache.GetAtRoot(); @@ -145,12 +145,12 @@ namespace Umbraco.Tests.Cache.PublishedCache { child1, child2 }); - + Assert.AreEqual(2, dicDoc.Children.Count()); Assert.AreEqual(222333, dicDoc.Children.ElementAt(0).Id); Assert.AreEqual(444555, dicDoc.Children.ElementAt(1).Id); } - + [Test] public void Convert_From_Search_Result() { @@ -211,7 +211,7 @@ namespace Umbraco.Tests.Cache.PublishedCache private XmlDocument GetMediaXml() { var xml = @" - @@ -219,12 +219,12 @@ namespace Umbraco.Tests.Cache.PublishedCache ]> - + - + - + "; @@ -234,8 +234,8 @@ namespace Umbraco.Tests.Cache.PublishedCache return xmlDoc; } - private Dictionary GetDictionary( - int id, + private Dictionary GetDictionary( + int id, Guid key, int parentId, string idKey, @@ -265,13 +265,13 @@ namespace Umbraco.Tests.Cache.PublishedCache {"parentID", parentId.ToString()} }; } - + private PublishedMediaCache.DictionaryPublishedContent GetDictionaryDocument( string idKey = "id", string templateKey = "template", string nodeNameKey = "nodeName", string nodeTypeAliasKey = "nodeTypeAlias", - string pathKey = "path", + string pathKey = "path", int idVal = 1234, Guid keyVal = default(Guid), int parentIdVal = 321, @@ -291,7 +291,7 @@ namespace Umbraco.Tests.Cache.PublishedCache // callback to get the children: we're not going to test this so ignore (dd, n) => new List(), // callback to get a property - (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), + (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), null, // cache provider ContentTypesCache, // no xpath @@ -301,7 +301,7 @@ namespace Umbraco.Tests.Cache.PublishedCache //callback to get the children (dd, n) => children, // callback to get a property - (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), + (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), null, // cache provider ContentTypesCache, // no xpath @@ -335,7 +335,7 @@ namespace Umbraco.Tests.Cache.PublishedCache if (!updateDateVal.HasValue) updateDateVal = DateTime.Parse("2012-01-02"); - DoAssert((IPublishedContent)dicDoc, idVal, keyVal, templateIdVal, sortOrderVal, urlNameVal, nodeTypeAliasVal, nodeTypeIdVal, writerNameVal, + DoAssert((IPublishedContent)dicDoc, idVal, keyVal, templateIdVal, sortOrderVal, urlNameVal, nodeTypeAliasVal, nodeTypeIdVal, writerNameVal, creatorNameVal, writerIdVal, creatorIdVal, pathVal, createDateVal, updateDateVal, levelVal); //now validate the parentId that has been parsed, this doesn't exist on the IPublishedContent @@ -380,9 +380,9 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(createDateVal.Value, doc.CreateDate); Assert.AreEqual(updateDateVal.Value, doc.UpdateDate); Assert.AreEqual(levelVal, doc.Level); - + } - + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/DependencyInjection/TempTests.cs b/src/Umbraco.Tests/DependencyInjection/TempTests.cs new file mode 100644 index 0000000000..88a62e4769 --- /dev/null +++ b/src/Umbraco.Tests/DependencyInjection/TempTests.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using LightInject; +using NUnit.Framework; +using Umbraco.Core.DependencyInjection; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.DependencyInjection +{ + [TestFixture] + public class TempTests + { + //[Test] + public void Test() + { + var container = new ServiceContainer(); + container.ConfigureUmbracoCore(); + UrlProviderCollectionBuilder.Register(container) + .Append(); + var col = container.GetInstance(); + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index aea65ba47c..549fccfa51 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -32,24 +32,13 @@ namespace Umbraco.Tests.PublishedContent [TestFixture, RequiresSTA] public class PublishedMediaTests : PublishedContentTestBase { - - public override void Initialize() - { - base.Initialize(); - } - - public override void TearDown() - { - base.TearDown(); - } - /// /// sets up resolvers before resolution is frozen /// protected override void FreezeResolution() { var container = new ServiceContainer(); - container.EnableAnnotatedConstructorInjection(); + container.ConfigureUmbracoCore(); container.RegisterBuilderCollection(); container.GetInstance().Append(); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 7575932112..3878e9506d 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.TestHelpers base.Initialize(); var container = new ServiceContainer(); - container.EnableAnnotatedConstructorInjection(); + container.ConfigureUmbracoCore(); Container = container; TestHelper.InitializeContentDirectories(); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index 8a9617c919..271f0ee37b 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.TestHelpers var sqlSyntax = new SqlCeSyntaxProvider(); var container = new ServiceContainer(); - container.EnableAnnotatedConstructorInjection(); + container.ConfigureUmbracoCore(); container.RegisterSingleton(factory => Mock.Of()); container.RegisterSingleton(factory => Mock.Of()); @@ -44,10 +44,10 @@ namespace Umbraco.Tests.TestHelpers () => PluginManager.Current.ResolveAssignedMapperTypes()); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - - PluginManager.Current = new PluginManager(new NullCacheProvider(), + var pluginManager = PluginManager.Current = new PluginManager(new NullCacheProvider(), logger, false); + container.RegisterInstance(pluginManager); var mappers = new MapperCollection { new PocoMapper() }; var pocoDataFactory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init()); @@ -66,6 +66,7 @@ namespace Umbraco.Tests.TestHelpers Resolution.Reset(); //MappingResolver.Reset(); PluginManager.Current = null; + Current.Reset(); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c34b9e108c..fda799fd40 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -228,6 +228,7 @@ + diff --git a/src/Umbraco.Web/Current.cs b/src/Umbraco.Web/Current.cs index 2e60943cf9..d2ad437b53 100644 --- a/src/Umbraco.Web/Current.cs +++ b/src/Umbraco.Web/Current.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using LightInject; using Umbraco.Core.Events; using Umbraco.Core.Strings; using Umbraco.Web.PublishedCache; @@ -18,36 +18,70 @@ namespace Umbraco.Web private static IUmbracoContextAccessor _umbracoContextAccessor; private static IFacadeAccessor _facadeAccessor; - // in theory with proper injection all accessors should be injected, but during the - // transitions there are places where we need them and they are not available, so - // use the following properties: - - public static IUmbracoContextAccessor UmbracoContextAccessor + static Current() { - get + CoreCurrent.Resetted += (sender, args) => { - if (_umbracoContextAccessor != null) return _umbracoContextAccessor; - return (_umbracoContextAccessor = CoreCurrent.Container.GetInstance()); - } - set { _umbracoContextAccessor = value; } // for tests + if (_umbracoContextAccessor != null) + ClearUmbracoContext(); + _umbracoContextAccessor = null; + _facadeAccessor = null; + }; } + // for UNIT TESTS exclusively! + internal static void Reset() + { + CoreCurrent.Reset(); + } + + public static ServiceContainer Container + => CoreCurrent.Container; + + // Facade + // + // is managed by the FacadeAccessor + // + // have to support setting the accessor directly (vs container) for tests + // fixme - not sure about this - should tests use a container? + public static IFacadeAccessor FacadeAccessor { get { if (_facadeAccessor != null) return _facadeAccessor; - return (_facadeAccessor = CoreCurrent.Container.GetInstance()); + return (_facadeAccessor = Container.GetInstance()); } 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 + // UmbracoContext + // + // is managed by the UmbracoContext Acceesor + // + // have to support setting the accessor directly (vs container) for tests + // fixme - note sure about this - should tests use a container? + // + // have to support setting it for now, because of 'ensure umbraco context' which can create // contexts pretty much at any time and in an uncontrolled way - and when we do not have // proper access to the accessor. + // + // have to support clear, because of the weird mixed accessor we're using that can + // store things in thread-static var that need to be cleared, else it retains rogue values. + + public static IUmbracoContextAccessor UmbracoContextAccessor + { + get + { + if (_umbracoContextAccessor != null) return _umbracoContextAccessor; + return (_umbracoContextAccessor = Container.GetInstance()); + } + set { _umbracoContextAccessor = value; } // for tests + } + + public static UmbracoContext UmbracoContext + => UmbracoContextAccessor.UmbracoContext; + public static void SetUmbracoContext(UmbracoContext value, bool canReplace) { lock (Locker) @@ -59,7 +93,6 @@ namespace Umbraco.Web } } - // this is because of the weird mixed accessor we're using that can store things in thread-static var public static void ClearUmbracoContext() { lock (Locker) @@ -69,22 +102,24 @@ namespace Umbraco.Web } } - // cannot set - it's set by whatever creates the facade, which should have the accessor injected - public static IFacade Facade + #region Web Getters + + public static IFacade Facade => FacadeAccessor.Facade; - // cannot set - this is temp - public static EventMessages EventMessages - => CoreCurrent.Container.GetInstance().GetOrDefault(); + public static EventMessages EventMessages + => Container.GetInstance().GetOrDefault(); - public static IEnumerable UrlProviders + public static UrlProviderCollection UrlProviders => CoreCurrent.Container.GetInstance(); - #region Core + #endregion - // just repeating Core for convenience + #region Core Getters - public static IEnumerable UrlSegmentProviders + // proxy Core for convenience + + public static UrlSegmentProviderCollection UrlSegmentProviders => CoreCurrent.Container.GetInstance();