Tweak lifetimes for a few commonly resolved services

This is some low hanging fruit for reducing the number of resolves we have
per request.

DefaultHtmlGenerator: Lots of these are created by RazorPage. It needs
IUrlHelper, so scoped is the best we can do for now. For an example, on
the front page of our sample, 48 of these are created for each request.
48! This takes it down to 1-per-request.

JsonResult: Again, multiple created per request (12 for the sample). This
class is totally stateless, so we can get down to 0-per-request.

DefaultViewComponentInvokerFactory: Same story as JsonResult.

DefaultObjectValidator/MvcMarkerService/DefaultFilterProvider:
these are stateless and pretty much guaranteed to be used by every request.
Getting them off the table.
This commit is contained in:
Ryan Nowak 2015-06-24 19:30:49 -07:00
parent 08068a85be
commit d2908e7b7b
9 changed files with 27 additions and 45 deletions

View File

@ -2,13 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc

View File

@ -10,18 +10,11 @@ namespace Microsoft.AspNet.Mvc.Filters
{ {
public class DefaultFilterProvider : IFilterProvider public class DefaultFilterProvider : IFilterProvider
{ {
public DefaultFilterProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public int Order public int Order
{ {
get { return DefaultOrder.DefaultFrameworkSortOrder; } get { return DefaultOrder.DefaultFrameworkSortOrder; }
} }
protected IServiceProvider ServiceProvider { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public void OnProvidersExecuting([NotNull] FilterProviderContext context) public void OnProvidersExecuting([NotNull] FilterProviderContext context)
{ {
@ -55,7 +48,8 @@ namespace Microsoft.AspNet.Mvc.Filters
} }
else else
{ {
filterItem.Filter = filterFactory.CreateInstance(ServiceProvider); var services = context.ActionContext.HttpContext.RequestServices;
filterItem.Filter = filterFactory.CreateInstance(services);
if (filterItem.Filter == null) if (filterItem.Filter == null)
{ {

View File

@ -88,13 +88,15 @@ namespace Microsoft.Framework.DependencyInjection
// //
// Action Invoker // Action Invoker
// //
// This accesses per-request services // These two access per-request services
services.TryAddTransient<IActionInvokerFactory, ActionInvokerFactory>(); services.TryAddTransient<IActionInvokerFactory, ActionInvokerFactory>();
services.TryAddTransient<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>();
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>()); ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());
// These are stateless
services.TryAddSingleton<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>();
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IFilterProvider, DefaultFilterProvider>()); ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>());
// //
// ModelBinding, Validation and Formatting // ModelBinding, Validation and Formatting
@ -106,7 +108,7 @@ namespace Microsoft.Framework.DependencyInjection
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options; var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options;
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders); return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
})); }));
services.TryAdd(ServiceDescriptor.Transient<IObjectModelValidator>(serviceProvider => services.TryAdd(ServiceDescriptor.Singleton<IObjectModelValidator>(serviceProvider =>
{ {
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options; var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options;
var modelMetadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>(); var modelMetadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>();
@ -125,7 +127,7 @@ namespace Microsoft.Framework.DependencyInjection
// //
// Random Infrastructure // Random Infrastructure
// //
services.TryAddTransient<MvcMarkerService, MvcMarkerService>(); services.TryAddSingleton<MvcMarkerService, MvcMarkerService>();
services.TryAddSingleton<ITypeActivatorCache, DefaultTypeActivatorCache>(); services.TryAddSingleton<ITypeActivatorCache, DefaultTypeActivatorCache>();
services.TryAddScoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>)); services.TryAddScoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>));
services.TryAddScoped<IUrlHelper, UrlHelper>(); services.TryAddScoped<IUrlHelper, UrlHelper>();
@ -141,4 +143,4 @@ namespace Microsoft.Framework.DependencyInjection
ServiceDescriptor.Transient<IConfigureOptions<RouteOptions>, MvcRouteOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<RouteOptions>, MvcRouteOptionsSetup>());
} }
} }
} }

View File

@ -132,14 +132,15 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
[NotNull] ViewComponentDescriptor descriptor, [NotNull] ViewComponentDescriptor descriptor,
object[] arguments) object[] arguments)
{ {
var invoker = _invokerFactory.CreateInstance(descriptor, arguments); var context = new ViewComponentContext(descriptor, arguments, _viewContext, writer);
var invoker = _invokerFactory.CreateInstance(context);
if (invoker == null) if (invoker == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(descriptor.Type.FullName)); Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(descriptor.Type.FullName));
} }
var context = new ViewComponentContext(descriptor, arguments, _viewContext, writer);
await invoker.InvokeAsync(context); await invoker.InvokeAsync(context);
} }
@ -148,14 +149,15 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
[NotNull] ViewComponentDescriptor descriptor, [NotNull] ViewComponentDescriptor descriptor,
object[] arguments) object[] arguments)
{ {
var invoker = _invokerFactory.CreateInstance(descriptor, arguments); var context = new ViewComponentContext(descriptor, arguments, _viewContext, writer);
var invoker = _invokerFactory.CreateInstance(context);
if (invoker == null) if (invoker == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(descriptor.Type.FullName)); Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(descriptor.Type.FullName));
} }
var context = new ViewComponentContext(descriptor, arguments, _viewContext, writer);
invoker.Invoke(context); invoker.Invoke(context);
} }
} }

View File

@ -13,16 +13,13 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
{ {
public class DefaultViewComponentInvoker : IViewComponentInvoker public class DefaultViewComponentInvoker : IViewComponentInvoker
{ {
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache; private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator; private readonly IViewComponentActivator _viewComponentActivator;
public DefaultViewComponentInvoker( public DefaultViewComponentInvoker(
[NotNull] IServiceProvider serviceProvider,
[NotNull] ITypeActivatorCache typeActivatorCache, [NotNull] ITypeActivatorCache typeActivatorCache,
[NotNull] IViewComponentActivator viewComponentActivator) [NotNull] IViewComponentActivator viewComponentActivator)
{ {
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache; _typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator; _viewComponentActivator = viewComponentActivator;
} }
@ -77,8 +74,9 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
private object CreateComponent([NotNull] ViewComponentContext context) private object CreateComponent([NotNull] ViewComponentContext context)
{ {
var services = context.ViewContext.HttpContext.RequestServices;
var component = _typeActivatorCache.CreateInstance<object>( var component = _typeActivatorCache.CreateInstance<object>(
_serviceProvider, services,
context.ViewComponentDescriptor.Type); context.ViewComponentDescriptor.Type);
_viewComponentActivator.Activate(component, context); _viewComponentActivator.Activate(component, context);
return component; return component;

View File

@ -1,23 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents namespace Microsoft.AspNet.Mvc.ViewComponents
{ {
public class DefaultViewComponentInvokerFactory : IViewComponentInvokerFactory public class DefaultViewComponentInvokerFactory : IViewComponentInvokerFactory
{ {
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache; private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator; private readonly IViewComponentActivator _viewComponentActivator;
public DefaultViewComponentInvokerFactory( public DefaultViewComponentInvokerFactory(
IServiceProvider serviceProvider,
ITypeActivatorCache typeActivatorCache, ITypeActivatorCache typeActivatorCache,
IViewComponentActivator viewComponentActivator) IViewComponentActivator viewComponentActivator)
{ {
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache; _typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator; _viewComponentActivator = viewComponentActivator;
} }
@ -26,12 +22,9 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
// We don't currently make use of the descriptor or the arguments here (they are available on the context). // We don't currently make use of the descriptor or the arguments here (they are available on the context).
// We might do this some day to cache which method we select, so resist the urge to 'clean' this without // We might do this some day to cache which method we select, so resist the urge to 'clean' this without
// considering that possibility. // considering that possibility.
public IViewComponentInvoker CreateInstance( public IViewComponentInvoker CreateInstance([NotNull] ViewComponentContext context)
[NotNull] ViewComponentDescriptor viewComponentDescriptor,
object[] args)
{ {
return new DefaultViewComponentInvoker( return new DefaultViewComponentInvoker(
_serviceProvider,
_typeActivatorCache, _typeActivatorCache,
_viewComponentActivator); _viewComponentActivator);
} }

View File

@ -7,8 +7,6 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
{ {
public interface IViewComponentInvokerFactory public interface IViewComponentInvokerFactory
{ {
IViewComponentInvoker CreateInstance( IViewComponentInvoker CreateInstance([NotNull] ViewComponentContext context);
[NotNull] ViewComponentDescriptor viewComponentDescriptor,
object[] args);
} }
} }

View File

@ -222,7 +222,7 @@ namespace Microsoft.Framework.DependencyInjection
// View and rendering helpers // View and rendering helpers
services.TryAddTransient<IHtmlHelper, HtmlHelper>(); services.TryAddTransient<IHtmlHelper, HtmlHelper>();
services.TryAddTransient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>)); services.TryAddTransient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>));
services.TryAddTransient<IJsonHelper, JsonHelper>(); services.TryAddSingleton<IJsonHelper, JsonHelper>();
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict. // Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
services.TryAddSingleton<ITagHelperActivator, DefaultTagHelperActivator>(); services.TryAddSingleton<ITagHelperActivator, DefaultTagHelperActivator>();
@ -230,9 +230,9 @@ namespace Microsoft.Framework.DependencyInjection
// Consumed by the Cache tag helper to cache results across the lifetime of the application. // Consumed by the Cache tag helper to cache results across the lifetime of the application.
services.TryAddSingleton<IMemoryCache, MemoryCache>(); services.TryAddSingleton<IMemoryCache, MemoryCache>();
// DefaultHtmlGenerator is pretty much stateless but depends on Scoped services such as IUrlHelper and // DefaultHtmlGenerator is pretty much stateless but depends on IUrlHelper, which is scoped.
// IActionBindingContextProvider. Therefore it too is scoped. // Therefore it too is scoped.
services.TryAddTransient<IHtmlGenerator, DefaultHtmlGenerator>(); services.TryAddScoped<IHtmlGenerator, DefaultHtmlGenerator>();
// These do caching so they should stay singleton // These do caching so they should stay singleton
services.TryAddSingleton<IViewComponentSelector, DefaultViewComponentSelector>(); services.TryAddSingleton<IViewComponentSelector, DefaultViewComponentSelector>();
@ -242,7 +242,7 @@ namespace Microsoft.Framework.DependencyInjection
DefaultViewComponentDescriptorCollectionProvider>(); DefaultViewComponentDescriptorCollectionProvider>();
services.TryAddTransient<IViewComponentDescriptorProvider, DefaultViewComponentDescriptorProvider>(); services.TryAddTransient<IViewComponentDescriptorProvider, DefaultViewComponentDescriptorProvider>();
services.TryAddTransient<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>(); services.TryAddSingleton<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
services.TryAddTransient<IViewComponentHelper, DefaultViewComponentHelper>(); services.TryAddTransient<IViewComponentHelper, DefaultViewComponentHelper>();
// Security and Authorization // Security and Authorization
@ -292,4 +292,4 @@ namespace Microsoft.Framework.DependencyInjection
services.AddWebEncoders(); services.AddWebEncoders();
} }
} }
} }

View File

@ -137,9 +137,7 @@ namespace Microsoft.AspNet.Mvc.Filters
private DefaultFilterProvider CreateProvider() private DefaultFilterProvider CreateProvider()
{ {
var services = new ServiceContainer(); return new DefaultFilterProvider();
return new DefaultFilterProvider(services);
} }
private FilterProviderContext CreateFilterContext(List<FilterItem> items) private FilterProviderContext CreateFilterContext(List<FilterItem> items)