From f055618c8cae24a02b9a7a93caab953d77f8301b Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 23 Jun 2015 13:03:24 -0700 Subject: [PATCH] Update MVC Enumerable service registrations Updates MVC service registration code to use the new TryAddEnumerable overload (idempotence++). Some other misc cleanup to improve idempotence. --- .../MvcCoreServiceCollectionExtensions.cs | 49 ++++--------------- .../MvcRouteOptionsSetup.cs | 29 +++++++++++ .../WebApiCompatShimOptionsSetup.cs | 3 ++ ...piCompatShimServiceCollectionExtensions.cs | 13 +++-- .../MvcServiceCollectionExtensions.cs | 48 ++++-------------- 5 files changed, 57 insertions(+), 85 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Core/MvcRouteOptionsSetup.cs diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcCoreServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcCoreServiceCollectionExtensions.cs index 1a8d82aa57..b10ad7891b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcCoreServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcCoreServiceCollectionExtensions.cs @@ -2,8 +2,6 @@ // 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; @@ -48,8 +46,7 @@ namespace Microsoft.Framework.DependencyInjection { // Options // - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient, CoreMvcOptionsSetup>()); // Action Discovery @@ -57,11 +54,9 @@ namespace Microsoft.Framework.DependencyInjection // These are consumed only when creating action descriptors, then they can be de-allocated services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor.Transient()); ; - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor .Singleton()); @@ -74,8 +69,7 @@ namespace Microsoft.Framework.DependencyInjection .Singleton()); // This provider needs access to the per-request services, but might be used many times for a given // request. - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); // Action Invoker @@ -87,14 +81,11 @@ namespace Microsoft.Framework.DependencyInjection services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor .Transient()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); // ModelBinding, Validation and Formatting @@ -127,34 +118,14 @@ namespace Microsoft.Framework.DependencyInjection 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))); + + services.TryAddEnumerable( + ServiceDescriptor.Transient, MvcRouteOptionsSetup>()); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcRouteOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Core/MvcRouteOptionsSetup.cs new file mode 100644 index 0000000000..1ae7647a28 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/MvcRouteOptionsSetup.cs @@ -0,0 +1,29 @@ +// 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.Routing; +using Microsoft.Framework.OptionsModel; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// Sets up MVC default options for . + /// + public class MvcRouteOptionsSetup : ConfigureOptions + { + public MvcRouteOptionsSetup() + : base(ConfigureRouting) + { + Order = DefaultOrder.DefaultFrameworkSortOrder; + } + + /// + /// Configures the . + /// + /// The . + public static void ConfigureRouting(RouteOptions options) + { + options.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint)); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimOptionsSetup.cs index 6bc49934a8..1fa40be83f 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimOptionsSetup.cs @@ -1,6 +1,7 @@ // 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.Net.Http; using System.Net.Http.Formatting; using Microsoft.Framework.OptionsModel; @@ -35,6 +36,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim // Add a formatter to write out an HttpResponseMessage to the response options.OutputFormatters.Insert(0, new HttpResponseMessageOutputFormatter()); + + options.ValidationExcludeFilters.Add(typeof(HttpResponseMessage)); } public void Configure(WebApiCompatShimOptions options, string name = "") diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimServiceCollectionExtensions.cs index 1bf4d5a7f6..fd03bd5566 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/WebApiCompatShimServiceCollectionExtensions.cs @@ -1,10 +1,10 @@ // 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.Net.Http; using System.Net.Http.Formatting; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.WebApiCompatShim; +using Microsoft.Framework.OptionsModel; namespace Microsoft.Framework.DependencyInjection { @@ -12,15 +12,14 @@ namespace Microsoft.Framework.DependencyInjection { public static IServiceCollection AddWebApiConventions(this IServiceCollection services) { - services.ConfigureOptions(); + services.TryAddEnumerable( + ServiceDescriptor.Transient, WebApiCompatShimOptionsSetup>()); + services.TryAddEnumerable( + ServiceDescriptor.Transient, WebApiCompatShimOptionsSetup>()); // The constructors on DefaultContentNegotiator aren't DI friendly, so just // new it up. - services.AddInstance(new DefaultContentNegotiator()); - services.Configure(options => - { - options.ValidationExcludeFilters.Add(typeof(HttpRequestMessage)); - }); + services.TryAdd(ServiceDescriptor.Instance(new DefaultContentNegotiator())); return services; } diff --git a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs index 3ef0d3a0fc..84754443f5 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs @@ -145,38 +145,30 @@ namespace Microsoft.Framework.DependencyInjection internal static void AddMvcServices(IServiceCollection services) { // Options - all of these are multi-registration - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient, MvcOptionsSetup>()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient, JsonMvcOptionsSetup>()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor .Transient, JsonMvcFormatterMappingOptionsSetup>()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient, MvcViewOptionsSetup>()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor .Transient, RazorViewEngineOptionsSetup>()); // Cors - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor.Transient()); // Auth - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); // Support for activating ViewDataDictionary - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor .Transient()); @@ -264,8 +256,7 @@ namespace Microsoft.Framework.DependencyInjection // Api Description services.TryAdd(ServiceDescriptor .Singleton()); - TryAddMultiRegistrationService( - services, + services.TryAddEnumerable( ServiceDescriptor.Transient()); } @@ -297,27 +288,6 @@ namespace Microsoft.Framework.DependencyInjection return services; } - // 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.AddDataProtection();