PERF: CreateWorkContextScope shouldn't call InstancePerMatchingLifetimeScope

http://orchard.codeplex.com/workitem/16869
CreateWorkContextScope now creates a child container with no alteration to the component registry
WorkContextModule added to group the related set of component registrations
HttpContextBase still injectable, but it's exposed in a way that doesn't require updating the child container per request

--HG--
branch : perf
This commit is contained in:
Louis DeJardin
2010-11-24 18:57:42 -08:00
parent 540723d492
commit 493fb03afe
15 changed files with 96 additions and 75 deletions

View File

@@ -46,7 +46,7 @@ namespace Orchard.Core.Tests.Routable.Services {
builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterInstance(new Mock<IContentDisplay>().Object);
builder.RegisterType<StubHttpContextAccessor>().As<IHttpContextAccessor>();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<OrchardServices>().As<IOrchardServices>();
builder.RegisterType<ThingHandler>().As<IContentHandler>();

View File

@@ -22,7 +22,7 @@ namespace Orchard.Tests.Environment {
}
protected override void Register(ContainerBuilder builder) {
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterAutoMocking();
}

View File

@@ -21,7 +21,7 @@ namespace Orchard.Tests.Environment.ShellBuilders {
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterType<ShellContextFactory>().As<IShellContextFactory>();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterAutoMocking(Moq.MockBehavior.Strict);
_container = builder.Build();
}

View File

@@ -23,7 +23,7 @@ namespace Orchard.Tests.Environment.State {
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterType<DefaultProcessingEngine>().As<IProcessingEngine>();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterAutoMocking();
_container = builder.Build();

View File

@@ -51,7 +51,7 @@ namespace Orchard.Tests.Localization {
builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterInstance(new Mock<IContentDisplay>().Object);
builder.RegisterType<StubHttpContextAccessor>().As<IHttpContextAccessor>();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
builder.RegisterType<OrchardServices>().As<IOrchardServices>();

View File

@@ -36,7 +36,7 @@ namespace Orchard.Tests.Mvc.Routes {
rootBuilder.Register(ctx => _routes);
rootBuilder.RegisterType<ShellRoute>().InstancePerDependency();
rootBuilder.RegisterType<RunningShellTable>().As<IRunningShellTable>().SingleInstance();
rootBuilder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>().InstancePerMatchingLifetimeScope("shell");
rootBuilder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>().InstancePerMatchingLifetimeScope("shell");
rootBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>();
_rootContainer = rootBuilder.Build();

View File

@@ -12,7 +12,7 @@ namespace Orchard.Tests.Tasks {
public class SweepGeneratorTests : ContainerTestBase {
protected override void Register(ContainerBuilder builder) {
builder.RegisterAutoMocking(MockBehavior.Loose);
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<SweepGenerator>();
}

View File

@@ -19,7 +19,7 @@ namespace Orchard.Tests.UI {
protected override void Register(ContainerBuilder builder) {
builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<LayoutWorkContext>().As<IWorkContextStateProvider>();

View File

@@ -45,6 +45,8 @@ namespace Orchard.Setup {
// standard services needed in setup mode
builder.RegisterModule(new MvcModule());
builder.RegisterModule(new CommandModule());
builder.RegisterModule(new WorkContextModule());
builder.RegisterType<RoutePublisher>().As<IRoutePublisher>().InstancePerLifetimeScope();
builder.RegisterType<ModelBinderPublisher>().As<IModelBinderPublisher>().InstancePerLifetimeScope();
builder.RegisterType<WebFormViewEngineProvider>().As<IViewEngineProvider>().As<IShapeTemplateViewEngine>().InstancePerLifetimeScope();
@@ -58,7 +60,7 @@ namespace Orchard.Setup {
builder.RegisterType<DataServicesProviderFactory>().As<IDataServicesProviderFactory>().InstancePerLifetimeScope();
builder.RegisterType<DefaultCommandManager>().As<ICommandManager>().InstancePerLifetimeScope();
builder.RegisterType<HelpCommand>().As<ICommandHandler>().InstancePerLifetimeScope();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>().InstancePerMatchingLifetimeScope("shell");
builder.RegisterType<WorkContextAccessor>().As<IWorkContextAccessor>().InstancePerMatchingLifetimeScope("shell");
builder.RegisterType<ResourceManifest>().As<IResourceManifestProvider>().InstancePerLifetimeScope();
builder.RegisterType<ResourceManager>().As<IResourceManager>().InstancePerLifetimeScope();
builder.RegisterType<ResourceFilter>().As<IFilterProvider>().InstancePerLifetimeScope();

View File

@@ -1,13 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Autofac;
using Orchard.Mvc;
namespace Orchard.Environment {
public class DefaultWorkContextAccessor : IWorkContextAccessor {
public class WorkContextAccessor : IWorkContextAccessor {
readonly ILifetimeScope _lifetimeScope;
readonly IHttpContextAccessor _httpContextAccessor;
@@ -18,7 +16,7 @@ namespace Orchard.Environment {
[ThreadStatic]
static ConcurrentDictionary<object, WorkContext> _threadStaticContexts;
public DefaultWorkContextAccessor(
public WorkContextAccessor(
IHttpContextAccessor httpContextAccessor,
ILifetimeScope lifetimeScope) {
_httpContextAccessor = httpContextAccessor;
@@ -39,36 +37,23 @@ namespace Orchard.Environment {
}
public IWorkContextScope CreateWorkContextScope(HttpContextBase httpContext) {
var workLifetime = _lifetimeScope.BeginLifetimeScope("work");
workLifetime.Resolve<WorkContextProperty<HttpContextBase>>().Value = httpContext;
var workLifetime = SpawnWorkLifetime(builder => {
builder.Register(ctx => httpContext)
.As<HttpContextBase>();
builder.Register(ctx => new WorkContextImplementation(ctx))
.As<WorkContext>()
.InstancePerMatchingLifetimeScope("work");
});
return new HttpContextScopeImplementation(
workLifetime,
httpContext,
_workContextKey);
}
public IWorkContextScope CreateWorkContextScope() {
var httpContext = _httpContextAccessor.Current();
if (httpContext != null)
return CreateWorkContextScope(httpContext);
var workLifetime = SpawnWorkLifetime(builder => {
builder.Register(ctx => httpContext)
.As<HttpContextBase>();
builder.Register(ctx => new WorkContextImplementation(ctx))
.As<WorkContext>()
.InstancePerMatchingLifetimeScope("work");
});
return new ThreadStaticScopeImplementation(
workLifetime,
_lifetimeScope.BeginLifetimeScope("work"),
EnsureThreadStaticContexts(),
_workContextKey);
}
@@ -77,49 +62,6 @@ namespace Orchard.Environment {
return _threadStaticContexts ?? (_threadStaticContexts = new ConcurrentDictionary<object, WorkContext>());
}
private ILifetimeScope SpawnWorkLifetime(Action<ContainerBuilder> configurationAction) {
return _lifetimeScope.BeginLifetimeScope("work", configurationAction);
}
class WorkContextImplementation : WorkContext {
readonly IComponentContext _componentContext;
readonly ConcurrentDictionary<string, Func<object>> _stateResolvers = new ConcurrentDictionary<string, Func<object>>();
readonly IEnumerable<IWorkContextStateProvider> _workContextStateProviders;
public WorkContextImplementation(IComponentContext componentContext) {
_componentContext = componentContext;
_workContextStateProviders = componentContext.Resolve<IEnumerable<IWorkContextStateProvider>>();
}
public override T Resolve<T>() {
return _componentContext.Resolve<T>();
}
public override bool TryResolve<T>(out T service) {
return _componentContext.TryResolve(out service);
}
public override T GetState<T>(string name) {
var resolver = _stateResolvers.GetOrAdd(name, FindResolverForState<T>);
return (T)resolver();
}
Func<object> FindResolverForState<T>(string name) {
var resolver = _workContextStateProviders.Select(wcsp => wcsp.Get<T>(name))
.FirstOrDefault(value => !Equals(value, default(T)));
if (resolver == null) {
return () => default(T);
}
return () => resolver(this);
}
public override void SetState<T>(string name, T value) {
_stateResolvers[name] = () => value;
}
}
class HttpContextScopeImplementation : IWorkContextScope {
readonly WorkContext _workContext;

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Autofac;
namespace Orchard.Environment {
class WorkContextImplementation : WorkContext {
readonly IComponentContext _componentContext;
readonly ConcurrentDictionary<string, Func<object>> _stateResolvers = new ConcurrentDictionary<string, Func<object>>();
readonly IEnumerable<IWorkContextStateProvider> _workContextStateProviders;
public WorkContextImplementation(IComponentContext componentContext) {
_componentContext = componentContext;
_workContextStateProviders = componentContext.Resolve<IEnumerable<IWorkContextStateProvider>>();
}
public override T Resolve<T>() {
return _componentContext.Resolve<T>();
}
public override bool TryResolve<T>(out T service) {
return _componentContext.TryResolve(out service);
}
public override T GetState<T>(string name) {
var resolver = _stateResolvers.GetOrAdd(name, FindResolverForState<T>);
return (T)resolver();
}
Func<object> FindResolverForState<T>(string name) {
var resolver = Enumerable.FirstOrDefault(_workContextStateProviders.Select(wcsp => wcsp.Get<T>(name)), value => !Equals(value, default(T)));
if (resolver == null) {
return () => default(T);
}
return () => resolver(this);
}
public override void SetState<T>(string name, T value) {
_stateResolvers[name] = () => value;
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Web;
using Autofac;
namespace Orchard.Environment {
public class WorkContextModule : Module {
protected override void Load(ContainerBuilder builder) {
builder.RegisterType<WorkContextAccessor>()
.As<IWorkContextAccessor>()
.InstancePerMatchingLifetimeScope("shell");
builder.Register(ctx => new WorkContextImplementation(ctx))
.As<WorkContext>()
.InstancePerMatchingLifetimeScope("work");
builder.RegisterType<WorkContextProperty<HttpContextBase>>()
.As<WorkContextProperty<HttpContextBase>>()
.InstancePerMatchingLifetimeScope("work");
builder.Register(ctx => ctx.Resolve<WorkContextProperty<HttpContextBase>>().Value)
.As<HttpContextBase>()
.InstancePerDependency();
}
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Environment {
class WorkContextProperty<T> {
public T Value { get; set; }
}
}

View File

@@ -2,7 +2,7 @@
using System.Web;
namespace Orchard {
public interface IWorkContextAccessor : ISingletonDependency {
public interface IWorkContextAccessor {
WorkContext GetContext(HttpContextBase httpContext);
IWorkContextScope CreateWorkContextScope(HttpContextBase httpContext);

View File

@@ -158,6 +158,9 @@
<Compile Include="Environment\Extensions\Loaders\RawThemeExtensionLoader.cs" />
<Compile Include="Environment\Features\FeatureManager.cs" />
<Compile Include="Environment\IAssemblyLoader.cs" />
<Compile Include="Environment\WorkContextImplementation.cs" />
<Compile Include="Environment\WorkContextModule.cs" />
<Compile Include="Environment\WorkContextProperty.cs" />
<Compile Include="Localization\Services\CurrentCultureWorkContext.cs" />
<Compile Include="Localization\Services\DefaultLocalizedStringManager.cs" />
<Compile Include="Localization\Services\ILocalizedStringManager.cs" />
@@ -419,7 +422,7 @@
<Compile Include="DisplayManagement\Implementation\IDisplayManager.cs" />
<Compile Include="DisplayManagement\IShapeFactory.cs" />
<Compile Include="DisplayManagement\Descriptors\Interfaces.cs" />
<Compile Include="Environment\DefaultWorkContextAccessor.cs" />
<Compile Include="Environment\WorkContextAccessor.cs" />
<Compile Include="Environment\IHostLocalRestart.cs" />
<Compile Include="Environment\IShellContainerRegistrations.cs" />
<Compile Include="FileSystems\Dependencies\DynamicModuleVirtualPathProvider.cs" />