#5305: Fixing the issue where httpcontext would be resolved from the wrong lifetime scope container in certain cases.

This changeset fixes a number of issues:
- Fixes the Autofac.Core.DependencyResolutionException: A delegate registered to create instances of 'System.Web.HttpContextBase' returned null error in VS 2015.
- Fixes indexing of layout part. The issue was that elements being rendered from a background task context and an element template uses an HTML helper, that would cause an exception since HttpContext.Current would normally be null.
- Replaces the need for injecting HttpContextBase with injecting IHttpContextAccessor (actually one should never inject HttpContextBase anymore - use IHttpContextAccessor instead).
This commit is contained in:
Sipke Schoorstra
2015-05-27 16:36:51 +02:00
parent 6183c8783e
commit ce630f980c
18 changed files with 197 additions and 61 deletions

View File

@@ -1,4 +1,5 @@
using System.Web; using System.Web;
using Autofac;
using Orchard.Mvc; using Orchard.Mvc;
namespace Orchard.Tests.Stubs { namespace Orchard.Tests.Stubs {
@@ -16,6 +17,10 @@ namespace Orchard.Tests.Stubs {
return _httpContext; return _httpContext;
} }
public HttpContextBase CreateContext(ILifetimeScope lifetimeScope) {
return _httpContext;
}
public void Set(HttpContextBase httpContext) { public void Set(HttpContextBase httpContext) {
_httpContext = httpContext; _httpContext = httpContext;
} }

View File

@@ -13,7 +13,7 @@ namespace Orchard.Layouts.Handlers {
private readonly IContentPartDisplay _contentPartDisplay; private readonly IContentPartDisplay _contentPartDisplay;
private readonly IShapeDisplay _shapeDisplay; private readonly IShapeDisplay _shapeDisplay;
private readonly ILayoutSerializer _serializer; private readonly ILayoutSerializer _serializer;
private readonly ISignals _signals; private readonly IStaticHttpContextScopeFactory _staticHttpContextScopeFactory;
public LayoutPartHandler( public LayoutPartHandler(
IRepository<LayoutPartRecord> repository, IRepository<LayoutPartRecord> repository,
@@ -22,14 +22,14 @@ namespace Orchard.Layouts.Handlers {
IContentPartDisplay contentPartDisplay, IContentPartDisplay contentPartDisplay,
IShapeDisplay shapeDisplay, IShapeDisplay shapeDisplay,
ILayoutSerializer serializer, ILayoutSerializer serializer,
ISignals signals) { IStaticHttpContextScopeFactory staticHttpContextScopeFactory) {
_layoutManager = layoutManager; _layoutManager = layoutManager;
_contentManager = contentManager; _contentManager = contentManager;
_contentPartDisplay = contentPartDisplay; _contentPartDisplay = contentPartDisplay;
_shapeDisplay = shapeDisplay; _shapeDisplay = shapeDisplay;
_serializer = serializer; _serializer = serializer;
_signals = signals; _staticHttpContextScopeFactory = staticHttpContextScopeFactory;
Filters.Add(StorageFilter.For(repository)); Filters.Add(StorageFilter.For(repository));
OnPublished<LayoutPart>(UpdateTemplateClients); OnPublished<LayoutPart>(UpdateTemplateClients);
@@ -38,13 +38,22 @@ namespace Orchard.Layouts.Handlers {
private void IndexLayout(IndexContentContext context, LayoutPart part) { private void IndexLayout(IndexContentContext context, LayoutPart part) {
var layoutShape = _contentPartDisplay.BuildDisplay(part); var layoutShape = _contentPartDisplay.BuildDisplay(part);
var layoutHtml = _shapeDisplay.Display(layoutShape); var layoutHtml = RenderShape(layoutShape);
context.DocumentIndex context.DocumentIndex
.Add("body", layoutHtml).RemoveTags().Analyze() .Add("body", layoutHtml).RemoveTags().Analyze()
.Add("format", "html").Store(); .Add("format", "html").Store();
} }
/// <summary>
/// This method of rendering is safe even in background tasks.
/// </summary>
private string RenderShape(dynamic shape) {
using (_staticHttpContextScopeFactory.CreateStaticScope()) {
return _shapeDisplay.Display(shape);
}
}
private void UpdateTemplateClients(PublishContentContext context, LayoutPart part) { private void UpdateTemplateClients(PublishContentContext context, LayoutPart part) {
UpdateTemplateClients(part); UpdateTemplateClients(part);
} }

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Web;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.UI; using System.Web.UI;
using System.Web.WebPages; using System.Web.WebPages;
@@ -15,7 +14,6 @@ namespace Orchard.Templates.Services {
[OrchardFeature("Orchard.Templates.Razor")] [OrchardFeature("Orchard.Templates.Razor")]
public class RazorTemplateProcessor : TemplateProcessorImpl { public class RazorTemplateProcessor : TemplateProcessorImpl {
private readonly IRazorCompiler _compiler; private readonly IRazorCompiler _compiler;
private readonly HttpContextBase _httpContextBase;
private readonly IWorkContextAccessor _wca; private readonly IWorkContextAccessor _wca;
public override string Type { public override string Type {
@@ -24,11 +22,9 @@ namespace Orchard.Templates.Services {
public RazorTemplateProcessor( public RazorTemplateProcessor(
IRazorCompiler compiler, IRazorCompiler compiler,
HttpContextBase httpContextBase,
IWorkContextAccessor wca) { IWorkContextAccessor wca) {
_compiler = compiler; _compiler = compiler;
_httpContextBase = httpContextBase;
_wca = wca; _wca = wca;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
@@ -41,7 +37,7 @@ namespace Orchard.Templates.Services {
public override string Process(string template, string name, DisplayContext context = null, dynamic model = null) { public override string Process(string template, string name, DisplayContext context = null, dynamic model = null) {
if (String.IsNullOrEmpty(template)) if (String.IsNullOrEmpty(template))
return string.Empty; return String.Empty;
var compiledTemplate = _compiler.CompileRazor(template, name, new Dictionary<string, object>()); var compiledTemplate = _compiler.CompileRazor(template, name, new Dictionary<string, object>());
var result = ActivateAndRenderTemplate(compiledTemplate, context, null, model); var result = ActivateAndRenderTemplate(compiledTemplate, context, null, model);
@@ -73,9 +69,10 @@ namespace Orchard.Templates.Services {
// Setup a fake view context in order to support razor syntax inside of HTML attributes, // Setup a fake view context in order to support razor syntax inside of HTML attributes,
// for instance: <a href="@WorkContext.CurrentSite.BaseUrl">Homepage</a>. // for instance: <a href="@WorkContext.CurrentSite.BaseUrl">Homepage</a>.
var viewData = new ViewDataDictionary(model); var viewData = new ViewDataDictionary(model);
var httpContext = _wca.GetContext().HttpContext;
obj.ViewContext = new ViewContext( obj.ViewContext = new ViewContext(
new ControllerContext( new ControllerContext(
_httpContextBase.Request.RequestContext, httpContext.Request.RequestContext,
new StubController()), new StubController()),
new StubView(), new StubView(),
viewData, viewData,
@@ -83,7 +80,7 @@ namespace Orchard.Templates.Services {
htmlWriter); htmlWriter);
obj.ViewData = viewData; obj.ViewData = viewData;
obj.WebPageContext = new WebPageContext(_httpContextBase, obj as WebPageRenderingBase, model); obj.WebPageContext = new WebPageContext(httpContext, obj as WebPageRenderingBase, model);
obj.WorkContext = _wca.GetContext(); obj.WorkContext = _wca.GetContext();
} }

View File

@@ -179,8 +179,8 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
private ControllerContext CreateControllerContext() { private ControllerContext CreateControllerContext() {
var controller = new StubController(); var controller = new StubController();
var httpContext = _workContextAccessor.GetContext().Resolve<HttpContextBase>(); var httpContext = _workContextAccessor.GetContext().HttpContext;
var requestContext = _workContextAccessor.GetContext().Resolve<RequestContext>(); var requestContext = httpContext.Request.RequestContext;
var routeData = requestContext.RouteData; var routeData = requestContext.RouteData;
routeData.DataTokens["IWorkContextAccessor"] = _workContextAccessor; routeData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;

View File

@@ -1,8 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Web;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Routing;
using Orchard.DisplayManagement.Implementation; using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement.Shapes; using Orchard.DisplayManagement.Shapes;
@@ -10,28 +8,24 @@ namespace Orchard.DisplayManagement {
public class ShapeDisplay : IShapeDisplay { public class ShapeDisplay : IShapeDisplay {
private readonly IDisplayHelperFactory _displayHelperFactory; private readonly IDisplayHelperFactory _displayHelperFactory;
private readonly IWorkContextAccessor _workContextAccessor; private readonly IWorkContextAccessor _workContextAccessor;
private readonly HttpContextBase _httpContextBase;
private readonly RequestContext _requestContext;
public ShapeDisplay( public ShapeDisplay(
IDisplayHelperFactory displayHelperFactory, IDisplayHelperFactory displayHelperFactory,
IWorkContextAccessor workContextAccessor, IWorkContextAccessor workContextAccessor) {
HttpContextBase httpContextBase,
RequestContext requestContext) {
_displayHelperFactory = displayHelperFactory; _displayHelperFactory = displayHelperFactory;
_workContextAccessor = workContextAccessor; _workContextAccessor = workContextAccessor;
_httpContextBase = httpContextBase;
_requestContext = requestContext;
} }
public string Display(Shape shape) { public string Display(Shape shape) {
return Display((object) shape); return Display((object)shape);
} }
public string Display(object shape) { public string Display(object shape) {
var workContext = _workContextAccessor.GetContext();
var httpContext = workContext.HttpContext;
var viewContext = new ViewContext { var viewContext = new ViewContext {
HttpContext = _httpContextBase, HttpContext = httpContext,
RequestContext = _requestContext RequestContext = httpContext.Request.RequestContext
}; };
viewContext.RouteData.DataTokens["IWorkContextAccessor"] = _workContextAccessor; viewContext.RouteData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;
var display = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer()); var display = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer());

View File

@@ -63,7 +63,7 @@ namespace Orchard.Environment {
builder.RegisterType<AppDomainAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance(); builder.RegisterType<AppDomainAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
builder.RegisterType<GacAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance(); builder.RegisterType<GacAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
builder.RegisterType<OrchardFrameworkAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance(); builder.RegisterType<OrchardFrameworkAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().InstancePerDependency(); builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
builder.RegisterType<ViewsBackgroundCompilation>().As<IViewsBackgroundCompilation>().SingleInstance(); builder.RegisterType<ViewsBackgroundCompilation>().As<IViewsBackgroundCompilation>().SingleInstance();
builder.RegisterType<DefaultExceptionPolicy>().As<IExceptionPolicy>().SingleInstance(); builder.RegisterType<DefaultExceptionPolicy>().As<IExceptionPolicy>().SingleInstance();
builder.RegisterType<DefaultCriticalErrorProvider>().As<ICriticalErrorProvider>().SingleInstance(); builder.RegisterType<DefaultCriticalErrorProvider>().As<ICriticalErrorProvider>().SingleInstance();

View File

@@ -60,11 +60,14 @@ namespace Orchard.Environment {
return CreateWorkContextScope(httpContext); return CreateWorkContextScope(httpContext);
var workLifetime = _lifetimeScope.BeginLifetimeScope("work"); var workLifetime = _lifetimeScope.BeginLifetimeScope("work");
httpContext = _httpContextAccessor.CreateContext(workLifetime);
workLifetime.Resolve<WorkContextProperty<HttpContextBase>>().Value = httpContext;
var events = workLifetime.Resolve<IEnumerable<IWorkContextEvents>>(); var events = workLifetime.Resolve<IEnumerable<IWorkContextEvents>>();
events.Invoke(e => e.Started(), NullLogger.Instance); events.Invoke(e => e.Started(), NullLogger.Instance);
return new ThreadStaticScopeImplementation( return new ThreadStaticScopeImplementation(
httpContext,
events, events,
workLifetime, workLifetime,
EnsureThreadStaticContexts(), EnsureThreadStaticContexts(),
@@ -113,8 +116,9 @@ namespace Orchard.Environment {
readonly WorkContext _workContext; readonly WorkContext _workContext;
readonly Action _disposer; readonly Action _disposer;
public ThreadStaticScopeImplementation(IEnumerable<IWorkContextEvents> events, ILifetimeScope lifetimeScope, ConcurrentDictionary<object, WorkContext> contexts, object workContextKey) { public ThreadStaticScopeImplementation(HttpContextBase httpContext, IEnumerable<IWorkContextEvents> events, ILifetimeScope lifetimeScope, ConcurrentDictionary<object, WorkContext> contexts, object workContextKey) {
_workContext = lifetimeScope.Resolve<WorkContext>(); _workContext = lifetimeScope.Resolve<WorkContext>();
httpContext.Items[workContextKey] = _workContext;
contexts.AddOrUpdate(workContextKey, _workContext, (a, b) => _workContext); contexts.AddOrUpdate(workContextKey, _workContext, (a, b) => _workContext);
_disposer = () => { _disposer = () => {
@@ -123,6 +127,11 @@ namespace Orchard.Environment {
WorkContext removedContext; WorkContext removedContext;
contexts.TryRemove(workContextKey, out removedContext); contexts.TryRemove(workContextKey, out removedContext);
lifetimeScope.Dispose(); lifetimeScope.Dispose();
var staticHttpContext = httpContext as IDisposable;
if(staticHttpContext != null)
staticHttpContext.Dispose();
}; };
} }

View File

@@ -0,0 +1,15 @@
using System;
namespace Orchard {
/// <summary>
/// A factory class that creates an <see cref="StaticHttpContextScope"/> instance and initializes the HttpContext.Current property with that instance until the scope is disposed of.
/// This is useful when rendering views from a background thread, as some Html Helpers access HttpContext.Current directly, thus preventing a NullReferenceException.
/// </summary>
public interface IStaticHttpContextScopeFactory : IDependency {
/// <summary>
/// Creates a disposable static HttpContext scope. This is safe to use even if there is an actual HttpContext.Current instance.
/// </summary>
/// <returns></returns>
IDisposable CreateStaticScope();
}
}

View File

@@ -1,9 +0,0 @@
using System.Web;
namespace Orchard.Mvc.Extensions {
public static class HttpContextBaseExtensions {
public static bool IsBackgroundContext(this HttpContextBase httpContextBase) {
return httpContextBase == null || httpContextBase is MvcModule.HttpContextPlaceholder;
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Web;
namespace Orchard.Mvc.Extensions {
public static class HttpContextExtensions {
public static bool IsBackgroundContext(this HttpContextBase httpContext) {
return httpContext == null || httpContext is MvcModule.HttpContextPlaceholder;
}
public static bool IsBackgroundContext(this HttpContext httpContext) {
return httpContext == null || httpContext.Items.Contains(StaticHttpContextScopeFactory.IsBackgroundHttpContextKey);
}
}
}

View File

@@ -1,18 +1,39 @@
using System.Web; using System;
using System.Collections.Concurrent;
using System.Web;
using Autofac; using Autofac;
using Orchard.Mvc.Extensions;
using Orchard.Settings;
namespace Orchard.Mvc { namespace Orchard.Mvc {
public class HttpContextAccessor : IHttpContextAccessor { public class HttpContextAccessor : IHttpContextAccessor {
private readonly IComponentContext _context; readonly object _contextKey = new object();
public HttpContextAccessor(IComponentContext context) { [ThreadStatic]
_context = context; static ConcurrentDictionary<object, HttpContextBase> _threadStaticContexts;
}
public HttpContextBase Current() { public HttpContextBase Current() {
// TODO: HttpContextBase is not registred in the "shell" lifetime scope, so resolving it will cause an exception. if (!HttpContext.Current.IsBackgroundContext())
return new HttpContextWrapper(HttpContext.Current);
return HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) : null;
return GetContext();
}
public HttpContextBase CreateContext(ILifetimeScope lifetimeScope) {
return new MvcModule.HttpContextPlaceholder(_threadStaticContexts, _contextKey, () => {
return lifetimeScope.Resolve<ISiteService>().GetSiteSettings().BaseUrl;
});
}
private HttpContextBase GetContext() {
HttpContextBase context;
return ThreadStaticContexts.TryGetValue(_contextKey, out context) ? context : null;
}
static ConcurrentDictionary<object, HttpContextBase> ThreadStaticContexts {
get {
return _threadStaticContexts ?? (_threadStaticContexts = new ConcurrentDictionary<object, HttpContextBase>());
}
} }
} }
} }

View File

@@ -1,7 +1,10 @@
using System.Web; using System;
using System.Web;
using Autofac;
namespace Orchard.Mvc { namespace Orchard.Mvc {
public interface IHttpContextAccessor { public interface IHttpContextAccessor {
HttpContextBase Current(); HttpContextBase Current();
HttpContextBase CreateContext(ILifetimeScope lifetimeScope);
} }
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Globalization; using System.Globalization;
@@ -9,6 +10,7 @@ using System.Web.Instrumentation;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Routing; using System.Web.Routing;
using Autofac; using Autofac;
using Orchard.Mvc.Extensions;
using Orchard.Mvc.Routes; using Orchard.Mvc.Routes;
using Orchard.Settings; using Orchard.Settings;
@@ -28,7 +30,7 @@ namespace Orchard.Mvc {
return false; return false;
try { try {
// The "Request" property throws at application startup on IIS integrated pipeline mode // The "Request" property throws at application startup on IIS integrated pipeline mode.
var req = HttpContext.Current.Request; var req = HttpContext.Current.Request;
} }
catch (Exception) { catch (Exception) {
@@ -50,7 +52,7 @@ namespace Orchard.Mvc {
// which requires activating the Site content item, which in turn requires a UrlHelper, which in turn requires a RequestContext, // which requires activating the Site content item, which in turn requires a UrlHelper, which in turn requires a RequestContext,
// thus preventing a StackOverflowException. // thus preventing a StackOverflowException.
var baseUrl = new Func<string>(() => siteService.GetSiteSettings().BaseUrl); var baseUrl = new Func<string>(() => siteService.GetSiteSettings().BaseUrl);
var httpContextBase = new HttpContextPlaceholder(baseUrl); var httpContextBase = context.Resolve<IHttpContextAccessor>().Current();
context.Resolve<IWorkContextAccessor>().CreateWorkContextScope(httpContextBase); context.Resolve<IWorkContextAccessor>().CreateWorkContextScope(httpContextBase);
return httpContextBase; return httpContextBase;
} }
@@ -58,7 +60,7 @@ namespace Orchard.Mvc {
static RequestContext RequestContextFactory(IComponentContext context) { static RequestContext RequestContextFactory(IComponentContext context) {
var httpContextAccessor = context.Resolve<IHttpContextAccessor>(); var httpContextAccessor = context.Resolve<IHttpContextAccessor>();
var httpContext = httpContextAccessor.Current(); var httpContext = httpContextAccessor.Current();
if (httpContext != null) { if (!httpContext.IsBackgroundContext()) {
var mvcHandler = httpContext.Handler as MvcHandler; var mvcHandler = httpContext.Handler as MvcHandler;
if (mvcHandler != null) { if (mvcHandler != null) {
@@ -85,16 +87,23 @@ namespace Orchard.Mvc {
/// <summary> /// <summary>
/// Standin context for background tasks. /// Standin context for background tasks.
/// </summary> /// </summary>
public class HttpContextPlaceholder : HttpContextBase { public class HttpContextPlaceholder : HttpContextBase, IDisposable {
private readonly Lazy<string> _baseUrl; private readonly Lazy<string> _baseUrl;
private readonly IDictionary _items = new Dictionary<object, object>(); private readonly IDictionary _items = new Dictionary<object, object>();
readonly Action _disposer;
public HttpContextPlaceholder(Func<string> baseUrl) { public HttpContextPlaceholder(ConcurrentDictionary<object, HttpContextBase> contexts, object contextKey, Func<string> baseUrl) {
_baseUrl = new Lazy<string>(baseUrl); _baseUrl = new Lazy<string>(baseUrl);
contexts.AddOrUpdate(contextKey, this, (a, b) => this);
_disposer = () => {
HttpContextBase removedContext;
contexts.TryRemove(contextKey, out removedContext);
};
} }
public override HttpRequestBase Request { public override HttpRequestBase Request {
get { return new HttpRequestPlaceholder(new Uri(_baseUrl.Value)); } get { return new HttpRequestPlaceholder(this, new Uri(_baseUrl.Value)); }
} }
public override IHttpHandler Handler { get; set; } public override IHttpHandler Handler { get; set; }
@@ -103,6 +112,10 @@ namespace Orchard.Mvc {
get { return new HttpResponsePlaceholder(); } get { return new HttpResponsePlaceholder(); }
} }
public override HttpSessionStateBase Session {
get { return null; }
}
public override IDictionary Items { public override IDictionary Items {
get { return _items; } get { return _items; }
} }
@@ -118,6 +131,10 @@ namespace Orchard.Mvc {
public override object GetService(Type serviceType) { public override object GetService(Type serviceType) {
return null; return null;
} }
public void Dispose() {
_disposer();
}
} }
public class HttpResponsePlaceholder : HttpResponseBase { public class HttpResponsePlaceholder : HttpResponseBase {
@@ -136,9 +153,12 @@ namespace Orchard.Mvc {
/// standin context for background tasks. /// standin context for background tasks.
/// </summary> /// </summary>
public class HttpRequestPlaceholder : HttpRequestBase { public class HttpRequestPlaceholder : HttpRequestBase {
private readonly HttpContextBase _httpContext;
private readonly Uri _uri; private readonly Uri _uri;
private RequestContext _requestContext;
public HttpRequestPlaceholder(Uri uri) { public HttpRequestPlaceholder(HttpContextBase httpContext, Uri uri) {
_httpContext = httpContext;
_uri = uri; _uri = uri;
} }
@@ -185,7 +205,7 @@ namespace Orchard.Mvc {
return new NameValueCollection { return new NameValueCollection {
{ "SERVER_PORT", _uri.Port.ToString(CultureInfo.InvariantCulture) }, { "SERVER_PORT", _uri.Port.ToString(CultureInfo.InvariantCulture) },
{ "HTTP_HOST", _uri.Authority.ToString(CultureInfo.InvariantCulture) }, { "HTTP_HOST", _uri.Authority.ToString(CultureInfo.InvariantCulture) },
}; };
} }
} }
@@ -215,6 +235,16 @@ namespace Orchard.Mvc {
return new HttpBrowserCapabilitiesPlaceholder(); return new HttpBrowserCapabilitiesPlaceholder();
} }
} }
public override RequestContext RequestContext {
get {
if (_requestContext == null) {
_requestContext = new RequestContext(_httpContext, new RouteData());
}
return _requestContext;
}
set { _requestContext = value; }
}
} }
public class HttpBrowserCapabilitiesPlaceholder : HttpBrowserCapabilitiesBase { public class HttpBrowserCapabilitiesPlaceholder : HttpBrowserCapabilitiesBase {

View File

@@ -149,6 +149,8 @@
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="StaticHttpContextScope.cs" />
<Compile Include="StaticHttpContextScopeFactory.cs" />
<Compile Include="Caching\DefaultCacheContextAccessor.cs" /> <Compile Include="Caching\DefaultCacheContextAccessor.cs" />
<Compile Include="Caching\DefaultParallelCacheContext.cs" /> <Compile Include="Caching\DefaultParallelCacheContext.cs" />
<Compile Include="Caching\ICacheContextAccessor.cs" /> <Compile Include="Caching\ICacheContextAccessor.cs" />
@@ -317,7 +319,7 @@
<Compile Include="Mvc\DataAnnotations\LocalizedModelValidatorProvider.cs" /> <Compile Include="Mvc\DataAnnotations\LocalizedModelValidatorProvider.cs" />
<Compile Include="Mvc\DataAnnotations\LocalizedRequiredAttribute.cs" /> <Compile Include="Mvc\DataAnnotations\LocalizedRequiredAttribute.cs" />
<Compile Include="Mvc\Extensions\RouteExtension.cs" /> <Compile Include="Mvc\Extensions\RouteExtension.cs" />
<Compile Include="Mvc\Extensions\HttpContextBaseExtensions.cs" /> <Compile Include="Mvc\Extensions\HttpContextExtensions.cs" />
<Compile Include="Mvc\FormValueRequiredAttribute.cs" /> <Compile Include="Mvc\FormValueRequiredAttribute.cs" />
<Compile Include="Mvc\HttpContextAccessor.cs" /> <Compile Include="Mvc\HttpContextAccessor.cs" />
<Compile Include="Mvc\HttpContextWorkContext.cs" /> <Compile Include="Mvc\HttpContextWorkContext.cs" />
@@ -647,6 +649,7 @@
<Compile Include="WebApi\Filters\OrchardApiActionFilterDispatcher.cs" /> <Compile Include="WebApi\Filters\OrchardApiActionFilterDispatcher.cs" />
<Compile Include="WebApi\Routes\IHttpRouteProvider.cs" /> <Compile Include="WebApi\Routes\IHttpRouteProvider.cs" />
<Compile Include="WebApi\Routes\StandardExtensionHttpRouteProvider.cs" /> <Compile Include="WebApi\Routes\StandardExtensionHttpRouteProvider.cs" />
<Compile Include="IStaticHttpContextScopeFactory.cs" />
<Compile Include="WorkContextExtensions.cs" /> <Compile Include="WorkContextExtensions.cs" />
<Compile Include="Mvc\ViewEngines\Razor\RazorCompilationEventsShim.cs" /> <Compile Include="Mvc\ViewEngines\Razor\RazorCompilationEventsShim.cs" />
<Compile Include="Mvc\ViewEngines\Razor\RazorViewEngineProvider.cs" /> <Compile Include="Mvc\ViewEngines\Razor\RazorViewEngineProvider.cs" />

View File

@@ -0,0 +1,17 @@
using System;
using System.Web;
namespace Orchard {
public class StaticHttpContextScope : IDisposable {
private readonly HttpContext _previousHttpContext;
public StaticHttpContextScope(HttpContext stub) {
_previousHttpContext = HttpContext.Current;
HttpContext.Current = stub;
}
public void Dispose() {
HttpContext.Current = _previousHttpContext;
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.IO;
using System.Web;
using Orchard.Settings;
namespace Orchard {
public class StaticHttpContextScopeFactory : IStaticHttpContextScopeFactory {
private readonly Func<ISiteService> _siteService;
public StaticHttpContextScopeFactory(Func<ISiteService> siteService) {
_siteService = siteService;
}
public const string IsBackgroundHttpContextKey = "IsBackgroundHttpContext";
public IDisposable CreateStaticScope() {
// If there already is a current HttpContext, use that one as the stub.
if(HttpContext.Current != null)
return new StaticHttpContextScope(HttpContext.Current);
// We're in a background task (or some other static context like the console),
// so create a stub context so that Html Helpers can still be executed when rendering shapes in background tasks
// (sadly enought some Html Helpers access HttpContext.Current directly).
var url = _siteService().GetSiteSettings().BaseUrl;
var stub = new HttpContext(new HttpRequest("", url, ""), new HttpResponse(new StringWriter()));
stub.Items[IsBackgroundHttpContextKey] = true;
return new StaticHttpContextScope(stub);
}
}
}

View File

@@ -15,17 +15,15 @@ namespace Orchard.Tasks {
private readonly IEnumerable<IBackgroundTask> _tasks; private readonly IEnumerable<IBackgroundTask> _tasks;
private readonly ITransactionManager _transactionManager; private readonly ITransactionManager _transactionManager;
private readonly string _shellName; private readonly string _shellName;
private readonly IContentManager _contentManager;
public BackgroundService( public BackgroundService(
IEnumerable<IBackgroundTask> tasks, IEnumerable<IBackgroundTask> tasks,
ITransactionManager transactionManager, ITransactionManager transactionManager,
ShellSettings shellSettings, ShellSettings shellSettings) {
IContentManager contentManager) {
_tasks = tasks; _tasks = tasks;
_transactionManager = transactionManager; _transactionManager = transactionManager;
_shellName = shellSettings.Name; _shellName = shellSettings.Name;
_contentManager = contentManager;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }

View File

@@ -41,7 +41,7 @@ namespace Orchard.Tasks {
} }
void Elapsed(object sender, ElapsedEventArgs e) { void Elapsed(object sender, ElapsedEventArgs e) {
// current implementation disallows re-entrancy // Current implementation disallows re-entrancy.
if (!System.Threading.Monitor.TryEnter(_timer)) if (!System.Threading.Monitor.TryEnter(_timer))
return; return;
@@ -60,7 +60,7 @@ namespace Orchard.Tasks {
public void DoWork() { public void DoWork() {
using (var scope = _workContextAccessor.CreateWorkContextScope()) { using (var scope = _workContextAccessor.CreateWorkContextScope()) {
// resolve the manager and invoke it // Resolve the manager and invoke it.
var manager = scope.Resolve<IBackgroundService>(); var manager = scope.Resolve<IBackgroundService>();
manager.Sweep(); manager.Sweep();
} }