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.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc

View File

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

View File

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

View File

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

View File

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

View File

@ -1,23 +1,19 @@
// 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.
using System;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentInvokerFactory : IViewComponentInvokerFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator;
public DefaultViewComponentInvokerFactory(
IServiceProvider serviceProvider,
ITypeActivatorCache typeActivatorCache,
IViewComponentActivator viewComponentActivator)
{
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache;
_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 might do this some day to cache which method we select, so resist the urge to 'clean' this without
// considering that possibility.
public IViewComponentInvoker CreateInstance(
[NotNull] ViewComponentDescriptor viewComponentDescriptor,
object[] args)
public IViewComponentInvoker CreateInstance([NotNull] ViewComponentContext context)
{
return new DefaultViewComponentInvoker(
_serviceProvider,
_typeActivatorCache,
_viewComponentActivator);
}

View File

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

View File

@ -222,7 +222,7 @@ namespace Microsoft.Framework.DependencyInjection
// View and rendering helpers
services.TryAddTransient<IHtmlHelper, 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.
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.
services.TryAddSingleton<IMemoryCache, MemoryCache>();
// DefaultHtmlGenerator is pretty much stateless but depends on Scoped services such as IUrlHelper and
// IActionBindingContextProvider. Therefore it too is scoped.
services.TryAddTransient<IHtmlGenerator, DefaultHtmlGenerator>();
// DefaultHtmlGenerator is pretty much stateless but depends on IUrlHelper, which is scoped.
// Therefore it too is scoped.
services.TryAddScoped<IHtmlGenerator, DefaultHtmlGenerator>();
// These do caching so they should stay singleton
services.TryAddSingleton<IViewComponentSelector, DefaultViewComponentSelector>();
@ -242,7 +242,7 @@ namespace Microsoft.Framework.DependencyInjection
DefaultViewComponentDescriptorCollectionProvider>();
services.TryAddTransient<IViewComponentDescriptorProvider, DefaultViewComponentDescriptorProvider>();
services.TryAddTransient<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
services.TryAddSingleton<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
services.TryAddTransient<IViewComponentHelper, DefaultViewComponentHelper>();
// Security and Authorization
@ -292,4 +292,4 @@ namespace Microsoft.Framework.DependencyInjection
services.AddWebEncoders();
}
}
}
}

View File

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