diff --git a/src/Umbraco.Web/IUmbracoContextFactory.cs b/src/Umbraco.Web/IUmbracoContextFactory.cs
new file mode 100644
index 0000000000..6d89578da7
--- /dev/null
+++ b/src/Umbraco.Web/IUmbracoContextFactory.cs
@@ -0,0 +1,32 @@
+using System.Web;
+
+namespace Umbraco.Web
+{
+ ///
+ /// Creates and manages instances.
+ ///
+ public interface IUmbracoContextFactory
+ {
+ ///
+ /// Ensures that a current exists.
+ ///
+ ///
+ /// If an is already registered in the
+ /// , returns a non-root reference to it.
+ /// Otherwise, create a new instance, registers it, and return a root reference
+ /// to it.
+ /// If is null, the factory tries to use
+ /// if it exists. Otherwise, it uses a dummy
+ /// .
+ ///
+ ///
+ /// using (var contextReference = contextFactory.EnsureUmbracoContext())
+ /// {
+ /// var umbracoContext = contextReference.UmbracoContext;
+ /// // use umbracoContext...
+ /// }
+ ///
+ /// An optional http context.
+ UmbracoContextReference EnsureUmbracoContext(HttpContextBase httpContext = null);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/UmbracoContextFactory.cs b/src/Umbraco.Web/UmbracoContextFactory.cs
new file mode 100644
index 0000000000..86b8740931
--- /dev/null
+++ b/src/Umbraco.Web/UmbracoContextFactory.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Web;
+using System.Web.Hosting;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.Services;
+using Umbraco.Web.PublishedCache;
+using Umbraco.Web.Routing;
+using Umbraco.Web.Security;
+
+namespace Umbraco.Web
+{
+ ///
+ /// Creates and manages instances.
+ ///
+ public class UmbracoContextFactory : IUmbracoContextFactory
+ {
+ private static readonly NulWriter NulWriterInstance = new NulWriter();
+
+ private readonly IUmbracoContextAccessor _umbracoContextAccessor;
+ private readonly IPublishedSnapshotService _publishedSnapshotService;
+ private readonly IVariationContextAccessor _variationContextAccessor;
+ private readonly IDefaultCultureAccessor _defaultCultureAccessor;
+
+ private readonly IUmbracoSettingsSection _umbracoSettings;
+ private readonly IGlobalSettings _globalSettings;
+ private readonly IEnumerable _urlProviders;
+ private readonly IUserService _userService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UmbracoContextFactory(IUmbracoContextAccessor umbracoContextAccessor, IPublishedSnapshotService publishedSnapshotService, IVariationContextAccessor variationContextAccessor, IDefaultCultureAccessor defaultCultureAccessor, IUmbracoSettingsSection umbracoSettings, IGlobalSettings globalSettings, IEnumerable urlProviders, IUserService userService)
+ {
+ _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
+ _publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService));
+ _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
+ _defaultCultureAccessor = defaultCultureAccessor ?? throw new ArgumentNullException(nameof(defaultCultureAccessor));
+
+ _umbracoSettings = umbracoSettings ?? throw new ArgumentNullException(nameof(umbracoSettings));
+ _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings));
+ _urlProviders = urlProviders ?? throw new ArgumentNullException(nameof(urlProviders));
+ _userService = userService ?? throw new ArgumentNullException(nameof(userService));
+ }
+
+ private UmbracoContext CreateUmbracoContext(HttpContextBase httpContext)
+ {
+ // make sure we have a variation context
+ if (_variationContextAccessor.VariationContext == null)
+ _variationContextAccessor.VariationContext = new VariationContext(_defaultCultureAccessor.DefaultCulture);
+
+ var webSecurity = new WebSecurity(httpContext, _userService, _globalSettings);
+
+ return new UmbracoContext(httpContext, _publishedSnapshotService, webSecurity, _umbracoSettings, _urlProviders, _globalSettings, _variationContextAccessor);
+ }
+
+ ///
+ public UmbracoContextReference EnsureUmbracoContext(HttpContextBase httpContext = null)
+ {
+ var currentUmbracoContext = _umbracoContextAccessor.UmbracoContext;
+ if (currentUmbracoContext != null)
+ return new UmbracoContextReference(currentUmbracoContext, false, _umbracoContextAccessor);
+
+ httpContext = httpContext ?? new HttpContextWrapper(HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("nul.aspx", "", NulWriterInstance)));
+
+ var umbracoContext = CreateUmbracoContext(httpContext);
+ _umbracoContextAccessor.UmbracoContext = umbracoContext;
+
+ return new UmbracoContextReference(umbracoContext, true, _umbracoContextAccessor);
+ }
+
+ // dummy TextWriter that does not write
+ private class NulWriter : TextWriter
+ {
+ public override Encoding Encoding => Encoding.UTF8;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/UmbracoContextReference.cs b/src/Umbraco.Web/UmbracoContextReference.cs
new file mode 100644
index 0000000000..be0458c3a6
--- /dev/null
+++ b/src/Umbraco.Web/UmbracoContextReference.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace Umbraco.Web
+{
+ ///
+ /// Represents a reference to an instance.
+ ///
+ ///
+ /// A reference points to an and it may own it (when it
+ /// is a root reference) or just reference it. A reference must be disposed after it has
+ /// been used. Disposing does nothing if the reference is not a root reference. Otherwise,
+ /// it disposes the and clears the
+ /// .
+ ///
+ public class UmbracoContextReference : IDisposable
+ {
+ private readonly IUmbracoContextAccessor _umbracoContextAccessor;
+ private bool _disposed;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ internal UmbracoContextReference(UmbracoContext umbracoContext, bool isRoot, IUmbracoContextAccessor umbracoContextAccessor)
+ {
+ UmbracoContext = umbracoContext;
+ IsRoot = isRoot;
+
+ _umbracoContextAccessor = umbracoContextAccessor;
+ }
+
+ ///
+ /// Gets the .
+ ///
+ public UmbracoContext UmbracoContext { get; }
+
+ ///
+ /// Gets a value indicating whether the reference is a root reference.
+ ///
+ ///
+ ///
+ ///
+ public bool IsRoot { get; }
+
+ ///
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+ _disposed = true;
+
+ if (IsRoot)
+ {
+ UmbracoContext.Dispose();
+ _umbracoContextAccessor.UmbracoContext = null;
+ }
+
+ GC.SuppressFinalize(this);
+ }
+ }
+}
\ No newline at end of file