mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
#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:
@@ -1,4 +1,5 @@
|
||||
using System.Web;
|
||||
using Autofac;
|
||||
using Orchard.Mvc;
|
||||
|
||||
namespace Orchard.Tests.Stubs {
|
||||
@@ -16,6 +17,10 @@ namespace Orchard.Tests.Stubs {
|
||||
return _httpContext;
|
||||
}
|
||||
|
||||
public HttpContextBase CreateContext(ILifetimeScope lifetimeScope) {
|
||||
return _httpContext;
|
||||
}
|
||||
|
||||
public void Set(HttpContextBase httpContext) {
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ namespace Orchard.Layouts.Handlers {
|
||||
private readonly IContentPartDisplay _contentPartDisplay;
|
||||
private readonly IShapeDisplay _shapeDisplay;
|
||||
private readonly ILayoutSerializer _serializer;
|
||||
private readonly ISignals _signals;
|
||||
private readonly IStaticHttpContextScopeFactory _staticHttpContextScopeFactory;
|
||||
|
||||
public LayoutPartHandler(
|
||||
IRepository<LayoutPartRecord> repository,
|
||||
@@ -22,14 +22,14 @@ namespace Orchard.Layouts.Handlers {
|
||||
IContentPartDisplay contentPartDisplay,
|
||||
IShapeDisplay shapeDisplay,
|
||||
ILayoutSerializer serializer,
|
||||
ISignals signals) {
|
||||
IStaticHttpContextScopeFactory staticHttpContextScopeFactory) {
|
||||
|
||||
_layoutManager = layoutManager;
|
||||
_contentManager = contentManager;
|
||||
_contentPartDisplay = contentPartDisplay;
|
||||
_shapeDisplay = shapeDisplay;
|
||||
_serializer = serializer;
|
||||
_signals = signals;
|
||||
_staticHttpContextScopeFactory = staticHttpContextScopeFactory;
|
||||
|
||||
Filters.Add(StorageFilter.For(repository));
|
||||
OnPublished<LayoutPart>(UpdateTemplateClients);
|
||||
@@ -38,13 +38,22 @@ namespace Orchard.Layouts.Handlers {
|
||||
|
||||
private void IndexLayout(IndexContentContext context, LayoutPart part) {
|
||||
var layoutShape = _contentPartDisplay.BuildDisplay(part);
|
||||
var layoutHtml = _shapeDisplay.Display(layoutShape);
|
||||
var layoutHtml = RenderShape(layoutShape);
|
||||
|
||||
context.DocumentIndex
|
||||
.Add("body", layoutHtml).RemoveTags().Analyze()
|
||||
.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) {
|
||||
UpdateTemplateClients(part);
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.UI;
|
||||
using System.Web.WebPages;
|
||||
@@ -15,7 +14,6 @@ namespace Orchard.Templates.Services {
|
||||
[OrchardFeature("Orchard.Templates.Razor")]
|
||||
public class RazorTemplateProcessor : TemplateProcessorImpl {
|
||||
private readonly IRazorCompiler _compiler;
|
||||
private readonly HttpContextBase _httpContextBase;
|
||||
private readonly IWorkContextAccessor _wca;
|
||||
|
||||
public override string Type {
|
||||
@@ -24,11 +22,9 @@ namespace Orchard.Templates.Services {
|
||||
|
||||
public RazorTemplateProcessor(
|
||||
IRazorCompiler compiler,
|
||||
HttpContextBase httpContextBase,
|
||||
IWorkContextAccessor wca) {
|
||||
|
||||
_compiler = compiler;
|
||||
_httpContextBase = httpContextBase;
|
||||
_wca = wca;
|
||||
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) {
|
||||
if (String.IsNullOrEmpty(template))
|
||||
return string.Empty;
|
||||
return String.Empty;
|
||||
|
||||
var compiledTemplate = _compiler.CompileRazor(template, name, new Dictionary<string, object>());
|
||||
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,
|
||||
// for instance: <a href="@WorkContext.CurrentSite.BaseUrl">Homepage</a>.
|
||||
var viewData = new ViewDataDictionary(model);
|
||||
var httpContext = _wca.GetContext().HttpContext;
|
||||
obj.ViewContext = new ViewContext(
|
||||
new ControllerContext(
|
||||
_httpContextBase.Request.RequestContext,
|
||||
httpContext.Request.RequestContext,
|
||||
new StubController()),
|
||||
new StubView(),
|
||||
viewData,
|
||||
@@ -83,7 +80,7 @@ namespace Orchard.Templates.Services {
|
||||
htmlWriter);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@@ -179,8 +179,8 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
|
||||
|
||||
private ControllerContext CreateControllerContext() {
|
||||
var controller = new StubController();
|
||||
var httpContext = _workContextAccessor.GetContext().Resolve<HttpContextBase>();
|
||||
var requestContext = _workContextAccessor.GetContext().Resolve<RequestContext>();
|
||||
var httpContext = _workContextAccessor.GetContext().HttpContext;
|
||||
var requestContext = httpContext.Request.RequestContext;
|
||||
var routeData = requestContext.RouteData;
|
||||
|
||||
routeData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;
|
||||
|
@@ -1,8 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.DisplayManagement.Shapes;
|
||||
|
||||
@@ -10,28 +8,24 @@ namespace Orchard.DisplayManagement {
|
||||
public class ShapeDisplay : IShapeDisplay {
|
||||
private readonly IDisplayHelperFactory _displayHelperFactory;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly HttpContextBase _httpContextBase;
|
||||
private readonly RequestContext _requestContext;
|
||||
|
||||
public ShapeDisplay(
|
||||
IDisplayHelperFactory displayHelperFactory,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
HttpContextBase httpContextBase,
|
||||
RequestContext requestContext) {
|
||||
IWorkContextAccessor workContextAccessor) {
|
||||
_displayHelperFactory = displayHelperFactory;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_httpContextBase = httpContextBase;
|
||||
_requestContext = requestContext;
|
||||
}
|
||||
|
||||
public string Display(Shape shape) {
|
||||
return Display((object) shape);
|
||||
return Display((object)shape);
|
||||
}
|
||||
|
||||
public string Display(object shape) {
|
||||
var workContext = _workContextAccessor.GetContext();
|
||||
var httpContext = workContext.HttpContext;
|
||||
var viewContext = new ViewContext {
|
||||
HttpContext = _httpContextBase,
|
||||
RequestContext = _requestContext
|
||||
HttpContext = httpContext,
|
||||
RequestContext = httpContext.Request.RequestContext
|
||||
};
|
||||
viewContext.RouteData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;
|
||||
var display = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer());
|
||||
|
@@ -63,7 +63,7 @@ namespace Orchard.Environment {
|
||||
builder.RegisterType<AppDomainAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
|
||||
builder.RegisterType<GacAssemblyNameResolver>().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<DefaultExceptionPolicy>().As<IExceptionPolicy>().SingleInstance();
|
||||
builder.RegisterType<DefaultCriticalErrorProvider>().As<ICriticalErrorProvider>().SingleInstance();
|
||||
|
@@ -60,11 +60,14 @@ namespace Orchard.Environment {
|
||||
return CreateWorkContextScope(httpContext);
|
||||
|
||||
var workLifetime = _lifetimeScope.BeginLifetimeScope("work");
|
||||
httpContext = _httpContextAccessor.CreateContext(workLifetime);
|
||||
workLifetime.Resolve<WorkContextProperty<HttpContextBase>>().Value = httpContext;
|
||||
|
||||
var events = workLifetime.Resolve<IEnumerable<IWorkContextEvents>>();
|
||||
events.Invoke(e => e.Started(), NullLogger.Instance);
|
||||
|
||||
return new ThreadStaticScopeImplementation(
|
||||
httpContext,
|
||||
events,
|
||||
workLifetime,
|
||||
EnsureThreadStaticContexts(),
|
||||
@@ -113,8 +116,9 @@ namespace Orchard.Environment {
|
||||
readonly WorkContext _workContext;
|
||||
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>();
|
||||
httpContext.Items[workContextKey] = _workContext;
|
||||
contexts.AddOrUpdate(workContextKey, _workContext, (a, b) => _workContext);
|
||||
|
||||
_disposer = () => {
|
||||
@@ -123,6 +127,11 @@ namespace Orchard.Environment {
|
||||
WorkContext removedContext;
|
||||
contexts.TryRemove(workContextKey, out removedContext);
|
||||
lifetimeScope.Dispose();
|
||||
|
||||
var staticHttpContext = httpContext as IDisposable;
|
||||
|
||||
if(staticHttpContext != null)
|
||||
staticHttpContext.Dispose();
|
||||
};
|
||||
}
|
||||
|
||||
|
15
src/Orchard/IStaticHttpContextScopeFactory.cs
Normal file
15
src/Orchard/IStaticHttpContextScopeFactory.cs
Normal 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();
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
13
src/Orchard/Mvc/Extensions/HttpContextExtensions.cs
Normal file
13
src/Orchard/Mvc/Extensions/HttpContextExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,18 +1,39 @@
|
||||
using System.Web;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Web;
|
||||
using Autofac;
|
||||
using Orchard.Mvc.Extensions;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.Mvc {
|
||||
public class HttpContextAccessor : IHttpContextAccessor {
|
||||
private readonly IComponentContext _context;
|
||||
readonly object _contextKey = new object();
|
||||
|
||||
public HttpContextAccessor(IComponentContext context) {
|
||||
_context = context;
|
||||
}
|
||||
[ThreadStatic]
|
||||
static ConcurrentDictionary<object, HttpContextBase> _threadStaticContexts;
|
||||
|
||||
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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,10 @@
|
||||
using System.Web;
|
||||
using System;
|
||||
using System.Web;
|
||||
using Autofac;
|
||||
|
||||
namespace Orchard.Mvc {
|
||||
public interface IHttpContextAccessor {
|
||||
HttpContextBase Current();
|
||||
HttpContextBase CreateContext(ILifetimeScope lifetimeScope);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
@@ -9,6 +10,7 @@ using System.Web.Instrumentation;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Autofac;
|
||||
using Orchard.Mvc.Extensions;
|
||||
using Orchard.Mvc.Routes;
|
||||
using Orchard.Settings;
|
||||
|
||||
@@ -28,7 +30,7 @@ namespace Orchard.Mvc {
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
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,
|
||||
// thus preventing a StackOverflowException.
|
||||
var baseUrl = new Func<string>(() => siteService.GetSiteSettings().BaseUrl);
|
||||
var httpContextBase = new HttpContextPlaceholder(baseUrl);
|
||||
var httpContextBase = context.Resolve<IHttpContextAccessor>().Current();
|
||||
context.Resolve<IWorkContextAccessor>().CreateWorkContextScope(httpContextBase);
|
||||
return httpContextBase;
|
||||
}
|
||||
@@ -58,7 +60,7 @@ namespace Orchard.Mvc {
|
||||
static RequestContext RequestContextFactory(IComponentContext context) {
|
||||
var httpContextAccessor = context.Resolve<IHttpContextAccessor>();
|
||||
var httpContext = httpContextAccessor.Current();
|
||||
if (httpContext != null) {
|
||||
if (!httpContext.IsBackgroundContext()) {
|
||||
|
||||
var mvcHandler = httpContext.Handler as MvcHandler;
|
||||
if (mvcHandler != null) {
|
||||
@@ -85,16 +87,23 @@ namespace Orchard.Mvc {
|
||||
/// <summary>
|
||||
/// Standin context for background tasks.
|
||||
/// </summary>
|
||||
public class HttpContextPlaceholder : HttpContextBase {
|
||||
public class HttpContextPlaceholder : HttpContextBase, IDisposable {
|
||||
private readonly Lazy<string> _baseUrl;
|
||||
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);
|
||||
contexts.AddOrUpdate(contextKey, this, (a, b) => this);
|
||||
|
||||
_disposer = () => {
|
||||
HttpContextBase removedContext;
|
||||
contexts.TryRemove(contextKey, out removedContext);
|
||||
};
|
||||
}
|
||||
|
||||
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; }
|
||||
@@ -103,6 +112,10 @@ namespace Orchard.Mvc {
|
||||
get { return new HttpResponsePlaceholder(); }
|
||||
}
|
||||
|
||||
public override HttpSessionStateBase Session {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override IDictionary Items {
|
||||
get { return _items; }
|
||||
}
|
||||
@@ -118,6 +131,10 @@ namespace Orchard.Mvc {
|
||||
public override object GetService(Type serviceType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
_disposer();
|
||||
}
|
||||
}
|
||||
|
||||
public class HttpResponsePlaceholder : HttpResponseBase {
|
||||
@@ -136,9 +153,12 @@ namespace Orchard.Mvc {
|
||||
/// standin context for background tasks.
|
||||
/// </summary>
|
||||
public class HttpRequestPlaceholder : HttpRequestBase {
|
||||
private readonly HttpContextBase _httpContext;
|
||||
private readonly Uri _uri;
|
||||
private RequestContext _requestContext;
|
||||
|
||||
public HttpRequestPlaceholder(Uri uri) {
|
||||
public HttpRequestPlaceholder(HttpContextBase httpContext, Uri uri) {
|
||||
_httpContext = httpContext;
|
||||
_uri = uri;
|
||||
}
|
||||
|
||||
@@ -215,6 +235,16 @@ namespace Orchard.Mvc {
|
||||
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 {
|
||||
|
@@ -149,6 +149,8 @@
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="StaticHttpContextScope.cs" />
|
||||
<Compile Include="StaticHttpContextScopeFactory.cs" />
|
||||
<Compile Include="Caching\DefaultCacheContextAccessor.cs" />
|
||||
<Compile Include="Caching\DefaultParallelCacheContext.cs" />
|
||||
<Compile Include="Caching\ICacheContextAccessor.cs" />
|
||||
@@ -317,7 +319,7 @@
|
||||
<Compile Include="Mvc\DataAnnotations\LocalizedModelValidatorProvider.cs" />
|
||||
<Compile Include="Mvc\DataAnnotations\LocalizedRequiredAttribute.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\HttpContextAccessor.cs" />
|
||||
<Compile Include="Mvc\HttpContextWorkContext.cs" />
|
||||
@@ -647,6 +649,7 @@
|
||||
<Compile Include="WebApi\Filters\OrchardApiActionFilterDispatcher.cs" />
|
||||
<Compile Include="WebApi\Routes\IHttpRouteProvider.cs" />
|
||||
<Compile Include="WebApi\Routes\StandardExtensionHttpRouteProvider.cs" />
|
||||
<Compile Include="IStaticHttpContextScopeFactory.cs" />
|
||||
<Compile Include="WorkContextExtensions.cs" />
|
||||
<Compile Include="Mvc\ViewEngines\Razor\RazorCompilationEventsShim.cs" />
|
||||
<Compile Include="Mvc\ViewEngines\Razor\RazorViewEngineProvider.cs" />
|
||||
|
17
src/Orchard/StaticHttpContextScope.cs
Normal file
17
src/Orchard/StaticHttpContextScope.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
src/Orchard/StaticHttpContextScopeFactory.cs
Normal file
31
src/Orchard/StaticHttpContextScopeFactory.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,17 +15,15 @@ namespace Orchard.Tasks {
|
||||
private readonly IEnumerable<IBackgroundTask> _tasks;
|
||||
private readonly ITransactionManager _transactionManager;
|
||||
private readonly string _shellName;
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public BackgroundService(
|
||||
IEnumerable<IBackgroundTask> tasks,
|
||||
ITransactionManager transactionManager,
|
||||
ShellSettings shellSettings,
|
||||
IContentManager contentManager) {
|
||||
ShellSettings shellSettings) {
|
||||
|
||||
_tasks = tasks;
|
||||
_transactionManager = transactionManager;
|
||||
_shellName = shellSettings.Name;
|
||||
_contentManager = contentManager;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
|
@@ -41,7 +41,7 @@ namespace Orchard.Tasks {
|
||||
}
|
||||
|
||||
void Elapsed(object sender, ElapsedEventArgs e) {
|
||||
// current implementation disallows re-entrancy
|
||||
// Current implementation disallows re-entrancy.
|
||||
if (!System.Threading.Monitor.TryEnter(_timer))
|
||||
return;
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Orchard.Tasks {
|
||||
|
||||
public void DoWork() {
|
||||
using (var scope = _workContextAccessor.CreateWorkContextScope()) {
|
||||
// resolve the manager and invoke it
|
||||
// Resolve the manager and invoke it.
|
||||
var manager = scope.Resolve<IBackgroundService>();
|
||||
manager.Sweep();
|
||||
}
|
||||
|
Reference in New Issue
Block a user