From 284c8992333d5eeede4bfd9b34974994606e5c8b Mon Sep 17 00:00:00 2001 From: Kiran Challa Date: Thu, 30 Apr 2015 15:01:05 -0700 Subject: [PATCH] Register default services directly in AddMvc and remove MvcServices --- .../MvcServiceCollectionExtensions.cs | 172 ++++++++++++++++- src/Microsoft.AspNet.Mvc/MvcServices.cs | 176 ------------------ .../ModelBindingTestHelper.cs | 4 +- 3 files changed, 173 insertions(+), 179 deletions(-) delete mode 100644 src/Microsoft.AspNet.Mvc/MvcServices.cs diff --git a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs index 3d2e540386..17c9c4e6b3 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs @@ -6,8 +6,25 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.Mvc.ActionConstraints; +using Microsoft.AspNet.Mvc.ApiExplorer; +using Microsoft.AspNet.Mvc.ApplicationModels; +using Microsoft.AspNet.Mvc.Core; +using Microsoft.AspNet.Mvc.Filters; +using Microsoft.AspNet.Mvc.Internal; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.ModelBinding.Metadata; +using Microsoft.AspNet.Mvc.ModelBinding.Validation; +using Microsoft.AspNet.Mvc.Razor; +using Microsoft.AspNet.Mvc.Razor.Compilation; +using Microsoft.AspNet.Mvc.Razor.Directives; +using Microsoft.AspNet.Mvc.Rendering; +using Microsoft.AspNet.Mvc.Routing; +using Microsoft.AspNet.Mvc.ViewComponents; using Microsoft.AspNet.Routing; +using Microsoft.Framework.Caching.Memory; using Microsoft.Framework.Internal; +using Microsoft.Framework.OptionsModel; namespace Microsoft.Framework.DependencyInjection { @@ -16,7 +33,158 @@ namespace Microsoft.Framework.DependencyInjection public static IServiceCollection AddMvc([NotNull] this IServiceCollection services) { ConfigureDefaultServices(services); - services.TryAdd(MvcServices.GetDefaultServices()); + + // Options and core services. + services.TryAdd(ServiceDescriptor.Transient, MvcOptionsSetup>()); + services.TryAdd( + ServiceDescriptor.Transient, RazorViewEngineOptionsSetup>()); + + services.TryAdd(ServiceDescriptor.Transient()); + + services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd((ServiceDescriptor.Singleton())); + services.TryAdd(ServiceDescriptor.Scoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>))); + + // Core action discovery, filters and action execution. + + // These are consumed only when creating action descriptors, then they can be de-allocated + services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd(ServiceDescriptor.Transient()); + + // This has a cache, so it needs to be a singleton + services.TryAdd(ServiceDescriptor.Singleton()); + + services.TryAdd(ServiceDescriptor.Transient()); + + // This accesses per-request services + services.TryAdd(ServiceDescriptor.Transient()); + + // This provider needs access to the per-request services, but might be used many times for a given + // request. + services.TryAdd(ServiceDescriptor.Transient()); + + services.TryAdd(ServiceDescriptor + .Singleton()); + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor + .Transient()); + services.TryAdd(ServiceDescriptor.Transient(serviceProvider => + { + var options = serviceProvider.GetRequiredService>().Options; + var modelMetadataProvider = serviceProvider.GetRequiredService(); + return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider); + })); + + services.TryAdd(ServiceDescriptor + .Transient()); + + services.TryAdd(ServiceDescriptor.Transient()); + + services.TryAdd(ServiceDescriptor + .Singleton()); + + // The IGlobalFilterProvider is used to build the action descriptors (likely once) and so should + // remain transient to avoid keeping it in memory. + services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd(ServiceDescriptor.Transient()); + + services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd(ServiceDescriptor.Transient()); + + // Dataflow - ModelBinding, Validation and Formatting + // + // The DefaultModelMetadataProvider does significant caching and should be a singleton. + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor.Transient(serviceProvider => + { + var options = serviceProvider.GetRequiredService>().Options; + return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders); + })); + + services.TryAdd(ServiceDescriptor.Instance(typeof(JsonOutputFormatter), new JsonOutputFormatter())); + + // Razor, Views and runtime compilation + + // The provider is inexpensive to initialize and provides ViewEngines that may require request + // specific services. + services.TryAdd(ServiceDescriptor.Scoped()); + + // Caches view locations that are valid for the lifetime of the application. + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor.Singleton(serviceProvider => + { + var cachedFileProvider = serviceProvider.GetRequiredService>(); + return new DefaultCodeTreeCache(cachedFileProvider.Options.FileProvider); + })); + + // The host is designed to be discarded after consumption and is very inexpensive to initialize. + services.TryAdd(ServiceDescriptor.Transient()); + + // Caches compilation artifacts across the lifetime of the application. + services.TryAdd(ServiceDescriptor.Singleton()); + + // This caches compilation related details that are valid across the lifetime of the application + // and is required to be a singleton. + services.TryAdd(ServiceDescriptor.Singleton()); + + // Both the compiler cache and roslyn compilation service hold on the compilation related + // caches. RazorCompilation service is just an adapter service, and it is transient to ensure + // the IMvcRazorHost dependency does not maintain state. + services.TryAdd(ServiceDescriptor.Transient()); + + // The ViewStartProvider needs to be able to consume scoped instances of IRazorPageFactory + services.TryAdd(ServiceDescriptor.Scoped()); + services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd(ServiceDescriptor.Singleton()); + + // Virtual path view factory needs to stay scoped so views can get get scoped services. + services.TryAdd(ServiceDescriptor.Scoped()); + + // View and rendering helpers + services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd(ServiceDescriptor.Transient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>))); + services.TryAdd(ServiceDescriptor.Scoped()); + + // Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict. + services.TryAdd(ServiceDescriptor.Singleton()); + + // Consumed by the Cache tag helper to cache results across the lifetime of the application. + services.TryAdd(ServiceDescriptor.Singleton()); + + // DefaultHtmlGenerator is pretty much stateless but depends on Scoped services such as IUrlHelper and + // IActionBindingContextProvider. Therefore it too is scoped. + services.TryAdd(ServiceDescriptor.Transient()); + + // These do caching so they should stay singleton + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor.Singleton< + IViewComponentDescriptorCollectionProvider, + DefaultViewComponentDescriptorCollectionProvider>()); + + services.TryAdd(ServiceDescriptor + .Transient()); + services.TryAdd(ServiceDescriptor + .Transient()); + services.TryAdd(ServiceDescriptor.Transient()); + + // Security and Authorization + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor.Singleton()); + services.TryAdd(ServiceDescriptor + .Singleton()); + + // Api Description + services.TryAdd(ServiceDescriptor + .Singleton()); + services.TryAdd(ServiceDescriptor.Transient()); + + // Temp Data + services.TryAdd(ServiceDescriptor.Scoped()); + // This does caching so it should stay singleton + services.TryAdd(ServiceDescriptor.Singleton()); + return services; } @@ -47,7 +215,7 @@ namespace Microsoft.Framework.DependencyInjection var controllerTypeProvider = new FixedSetControllerTypeProvider(); foreach (var type in controllerTypes) { - services.AddTransient(type); + services.TryAdd(ServiceDescriptor.Transient(type, type)); controllerTypeProvider.ControllerTypes.Add(type.GetTypeInfo()); } diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs deleted file mode 100644 index e744edf786..0000000000 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ /dev/null @@ -1,176 +0,0 @@ -// 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 Microsoft.AspNet.Mvc.ActionConstraints; -using Microsoft.AspNet.Mvc.ApiExplorer; -using Microsoft.AspNet.Mvc.ApplicationModels; -using Microsoft.AspNet.Mvc.Core; -using Microsoft.AspNet.Mvc.Filters; -using Microsoft.AspNet.Mvc.Internal; -using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Mvc.ModelBinding.Metadata; -using Microsoft.AspNet.Mvc.ModelBinding.Validation; -using Microsoft.AspNet.Mvc.Razor; -using Microsoft.AspNet.Mvc.Razor.Compilation; -using Microsoft.AspNet.Mvc.Razor.Directives; -using Microsoft.AspNet.Mvc.Rendering; -using Microsoft.AspNet.Mvc.Routing; -using Microsoft.AspNet.Mvc.ViewComponents; -using Microsoft.Framework.Caching.Memory; -using Microsoft.Framework.DependencyInjection; -using Microsoft.Framework.OptionsModel; - -namespace Microsoft.AspNet.Mvc -{ - public class MvcServices - { - public static IServiceCollection GetDefaultServices() - { - var services = new ServiceCollection(); - - // Options and core services. - services.AddTransient, MvcOptionsSetup>(); - services.AddTransient, RazorViewEngineOptionsSetup>(); - - // TryAdd() so functional tests can override this particular service. Test setup runs before this method. - services.TryAdd(ServiceDescriptor.Transient()); - - services.AddTransient(); - services.AddSingleton(); - services.AddScoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>)); - - // Core action discovery, filters and action execution. - - // These are consumed only when creating action descriptors, then they can be de-allocated - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - - // This has a cache, so it needs to be a singleton - services.AddSingleton(); - - services.AddTransient(); - - // This accesses per-reqest services - services.AddTransient(); - - // This provider needs access to the per-request services, but might be used many times for a given - // request. - services.AddTransient(); - - services.AddSingleton(); - services.AddSingleton(); - services.AddTransient(); - services.AddTransient(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Options; - var modelMetadataProvider = serviceProvider.GetRequiredService(); - return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider); - }); - - services.AddTransient(); - - services.AddTransient(); - - services.AddSingleton(); - - // The IGlobalFilterProvider is used to build the action descriptors (likely once) and so should - // remain transient to avoid keeping it in memory. - services.AddTransient(); - services.AddTransient(); - - services.AddTransient(); - services.AddTransient(); - - // Dataflow - ModelBinding, Validation and Formatting - // - // The DefaultModelMetadataProvider does significant caching and should be a singleton. - services.AddSingleton(); - services.AddTransient(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Options; - return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders); - }); - - services.AddInstance(new JsonOutputFormatter()); - - // Razor, Views and runtime compilation - - // The provider is inexpensive to initialize and provides ViewEngines that may require request - // specific services. - services.AddScoped(); - - // Caches view locations that are valid for the lifetime of the application. - services.AddSingleton(); - services.AddSingleton(serviceProvider => - { - var cachedFileProvider = serviceProvider.GetRequiredService>(); - return new DefaultCodeTreeCache(cachedFileProvider.Options.FileProvider); - }); - - // The host is designed to be discarded after consumption and is very inexpensive to initialize. - services.AddTransient(); - - // Caches compilation artifacts across the lifetime of the application. - services.AddSingleton(); - - // This caches compilation related details that are valid across the lifetime of the application - // and is required to be a singleton. - services.AddSingleton(); - - // Both the compiler cache and roslyn compilation service hold on the compilation related - // caches. RazorCompilation service is just an adapter service, and it is transient to ensure - // the IMvcRazorHost dependency does not maintain state. - services.AddTransient(); - - // The ViewStartProvider needs to be able to consume scoped instances of IRazorPageFactory - services.AddScoped(); - services.AddTransient(); - services.AddSingleton(); - - // Virtual path view factory needs to stay scoped so views can get get scoped services. - services.AddScoped(); - - // View and rendering helpers - services.AddTransient(); - services.AddTransient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>)); - services.AddScoped(); - - // Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict. - services.AddSingleton(); - - // Consumed by the Cache tag helper to cache results across the lifetime of the application. - services.AddSingleton(); - - // DefaultHtmlGenerator is pretty much stateless but depends on Scoped services such as IUrlHelper and - // IActionBindingContextProvider. Therefore it too is scoped. - services.AddTransient(); - - // These do caching so they should stay singleton - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - - // Security and Authorization - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - // Api Description - services.AddSingleton(); - services.AddTransient(); - - // Temp Data - services.AddScoped(); - // This does caching so it should stay singleton - services.AddSingleton(); - - return services; - } - } -} diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ModelBindingTestHelper.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ModelBindingTestHelper.cs index de07fd6a3f..21a82f7032 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ModelBindingTestHelper.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ModelBindingTestHelper.cs @@ -61,7 +61,9 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests private static void InitializeServices(HttpContext httpContext, Action updateOptions = null) { - var serviceCollection = MvcServices.GetDefaultServices(); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddMvc(); + httpContext.RequestServices = serviceCollection.BuildServiceProvider(); var actionContext = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());