// 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 System.Diagnostics; using System.Linq; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.ActionConstraints; 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.Routing; using Microsoft.AspNet.Routing; using Microsoft.Framework.Internal; using Microsoft.Framework.OptionsModel; namespace Microsoft.Framework.DependencyInjection { public static class MvcCoreServiceCollectionExtensions { public static IServiceCollection AddMinimalMvc([NotNull] this IServiceCollection services) { ConfigureDefaultServices(services); AddMvcCoreServices(services); return services; } /// /// Configures a set of for the application. /// /// The services available in the application. /// The which need to be configured. public static void ConfigureMvc( [NotNull] this IServiceCollection services, [NotNull] Action setupAction) { services.Configure(setupAction); } // To enable unit testing internal static void AddMvcCoreServices(IServiceCollection services) { // Options // TryAddMultiRegistrationService( services, ServiceDescriptor.Transient, CoreMvcOptionsSetup>()); // Action Discovery // // These are consumed only when creating action descriptors, then they can be de-allocated services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor.Transient()); ; TryAddMultiRegistrationService( services, ServiceDescriptor.Transient()); TryAddMultiRegistrationService( services, ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor .Singleton()); // Action Selection // services.TryAdd(ServiceDescriptor.Singleton()); // Performs caching services.TryAdd(ServiceDescriptor .Singleton()); // This provider needs access to the per-request services, but might be used many times for a given // request. TryAddMultiRegistrationService( services, ServiceDescriptor.Transient()); // Action Invoker // // 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()); services.TryAdd(ServiceDescriptor .Transient()); TryAddMultiRegistrationService( services, ServiceDescriptor.Transient()); TryAddMultiRegistrationService( services, ServiceDescriptor.Transient()); TryAddMultiRegistrationService( services, ServiceDescriptor.Transient()); // 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.Transient(serviceProvider => { var options = serviceProvider.GetRequiredService>().Options; var modelMetadataProvider = serviceProvider.GetRequiredService(); return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider); })); // Temp Data // services.TryAdd(ServiceDescriptor.Scoped()); // This does caching so it should stay singleton services.TryAdd(ServiceDescriptor.Singleton()); // Random Infrastructure // services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd((ServiceDescriptor.Singleton())); services.TryAdd(ServiceDescriptor.Scoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>))); services.TryAdd(ServiceDescriptor.Scoped()); } // Adds a service if the service type and implementation type hasn't been added yet. This is needed for // services like IConfigureOptions or IApplicationModelProvider where you need the ability // to register multiple implementation types for the same service type. private static bool TryAddMultiRegistrationService(IServiceCollection services, ServiceDescriptor descriptor) { // This can't work when registering a factory or instance, you have to register a type. // Additionally, if any existing registrations use a factory or instance, we can't check those, but we don't // assert that because it might be added by user-code. Debug.Assert(descriptor.ImplementationType != null); if (services.Any(d => d.ServiceType == descriptor.ServiceType && d.ImplementationType == descriptor.ImplementationType)) { return false; } services.Add(descriptor); return true; } private static void ConfigureDefaultServices(IServiceCollection services) { services.AddOptions(); services.AddRouting(); services.AddNotifier(); services.Configure( routeOptions => routeOptions.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint))); } } }