Update MVC Enumerable service registrations

Updates MVC service registration code to use the new TryAddEnumerable
overload (idempotence++).

Some other misc cleanup to improve idempotence.
This commit is contained in:
Ryan Nowak 2015-06-23 13:03:24 -07:00
parent 37f056ce2b
commit f055618c8c
5 changed files with 57 additions and 85 deletions

View File

@ -2,8 +2,6 @@
// 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.Diagnostics;
using System.Linq;
using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ActionConstraints; using Microsoft.AspNet.Mvc.ActionConstraints;
using Microsoft.AspNet.Mvc.ApplicationModels; using Microsoft.AspNet.Mvc.ApplicationModels;
@ -48,8 +46,7 @@ namespace Microsoft.Framework.DependencyInjection
{ {
// Options // Options
// //
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, CoreMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, CoreMvcOptionsSetup>());
// Action Discovery // Action Discovery
@ -57,11 +54,9 @@ namespace Microsoft.Framework.DependencyInjection
// These are consumed only when creating action descriptors, then they can be de-allocated // These are consumed only when creating action descriptors, then they can be de-allocated
services.TryAdd(ServiceDescriptor.Transient<IAssemblyProvider, DefaultAssemblyProvider>()); services.TryAdd(ServiceDescriptor.Transient<IAssemblyProvider, DefaultAssemblyProvider>());
services.TryAdd(ServiceDescriptor.Transient<IControllerTypeProvider, DefaultControllerTypeProvider>()); ; services.TryAdd(ServiceDescriptor.Transient<IControllerTypeProvider, DefaultControllerTypeProvider>()); ;
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>()); ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>()); ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());
services.TryAdd(ServiceDescriptor services.TryAdd(ServiceDescriptor
.Singleton<IActionDescriptorsCollectionProvider, DefaultActionDescriptorsCollectionProvider>()); .Singleton<IActionDescriptorsCollectionProvider, DefaultActionDescriptorsCollectionProvider>());
@ -74,8 +69,7 @@ namespace Microsoft.Framework.DependencyInjection
.Singleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>()); .Singleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>());
// This provider needs access to the per-request services, but might be used many times for a given // This provider needs access to the per-request services, but might be used many times for a given
// request. // request.
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IActionConstraintProvider, DefaultActionConstraintProvider>()); ServiceDescriptor.Transient<IActionConstraintProvider, DefaultActionConstraintProvider>());
// Action Invoker // Action Invoker
@ -87,14 +81,11 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(ServiceDescriptor.Transient<IActionInvokerFactory, ActionInvokerFactory>()); services.TryAdd(ServiceDescriptor.Transient<IActionInvokerFactory, ActionInvokerFactory>());
services.TryAdd(ServiceDescriptor services.TryAdd(ServiceDescriptor
.Transient<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>()); .Transient<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>()); ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IFilterProvider, DefaultFilterProvider>()); ServiceDescriptor.Transient<IFilterProvider, DefaultFilterProvider>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>()); ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>());
// ModelBinding, Validation and Formatting // ModelBinding, Validation and Formatting
@ -127,34 +118,14 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(ServiceDescriptor.Scoped<IUrlHelper, UrlHelper>()); services.TryAdd(ServiceDescriptor.Scoped<IUrlHelper, UrlHelper>());
} }
// Adds a service if the service type and implementation type hasn't been added yet. This is needed for
// services like IConfigureOptions<MvcOptions> 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) private static void ConfigureDefaultServices(IServiceCollection services)
{ {
services.AddOptions(); services.AddOptions();
services.AddRouting(); services.AddRouting();
services.AddNotifier(); services.AddNotifier();
services.Configure<RouteOptions>(
routeOptions => routeOptions.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint))); services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<RouteOptions>, MvcRouteOptionsSetup>());
} }
} }
} }

View File

@ -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
{
/// <summary>
/// Sets up MVC default options for <see cref="RouteOptions"/>.
/// </summary>
public class MvcRouteOptionsSetup : ConfigureOptions<RouteOptions>
{
public MvcRouteOptionsSetup()
: base(ConfigureRouting)
{
Order = DefaultOrder.DefaultFrameworkSortOrder;
}
/// <summary>
/// Configures the <see cref="RouteOptions"/>.
/// </summary>
/// <param name="options">The <see cref="RouteOptions"/>.</param>
public static void ConfigureRouting(RouteOptions options)
{
options.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint));
}
}
}

View File

@ -1,6 +1,7 @@
// 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.Net.Http;
using System.Net.Http.Formatting; using System.Net.Http.Formatting;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
@ -35,6 +36,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
// Add a formatter to write out an HttpResponseMessage to the response // Add a formatter to write out an HttpResponseMessage to the response
options.OutputFormatters.Insert(0, new HttpResponseMessageOutputFormatter()); options.OutputFormatters.Insert(0, new HttpResponseMessageOutputFormatter());
options.ValidationExcludeFilters.Add(typeof(HttpResponseMessage));
} }
public void Configure(WebApiCompatShimOptions options, string name = "") public void Configure(WebApiCompatShimOptions options, string name = "")

View File

@ -1,10 +1,10 @@
// 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.Net.Http;
using System.Net.Http.Formatting; using System.Net.Http.Formatting;
using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.WebApiCompatShim; using Microsoft.AspNet.Mvc.WebApiCompatShim;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.Framework.DependencyInjection namespace Microsoft.Framework.DependencyInjection
{ {
@ -12,15 +12,14 @@ namespace Microsoft.Framework.DependencyInjection
{ {
public static IServiceCollection AddWebApiConventions(this IServiceCollection services) public static IServiceCollection AddWebApiConventions(this IServiceCollection services)
{ {
services.ConfigureOptions<WebApiCompatShimOptionsSetup>(); services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, WebApiCompatShimOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<WebApiCompatShimOptions>, WebApiCompatShimOptionsSetup>());
// The constructors on DefaultContentNegotiator aren't DI friendly, so just // The constructors on DefaultContentNegotiator aren't DI friendly, so just
// new it up. // new it up.
services.AddInstance<IContentNegotiator>(new DefaultContentNegotiator()); services.TryAdd(ServiceDescriptor.Instance<IContentNegotiator>(new DefaultContentNegotiator()));
services.Configure<MvcOptions>(options =>
{
options.ValidationExcludeFilters.Add(typeof(HttpRequestMessage));
});
return services; return services;
} }

View File

@ -145,38 +145,30 @@ namespace Microsoft.Framework.DependencyInjection
internal static void AddMvcServices(IServiceCollection services) internal static void AddMvcServices(IServiceCollection services)
{ {
// Options - all of these are multi-registration // Options - all of these are multi-registration
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcOptionsSetup>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, JsonMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, JsonMvcOptionsSetup>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor ServiceDescriptor
.Transient<IConfigureOptions<MvcFormatterMappingOptions>, JsonMvcFormatterMappingOptionsSetup>()); .Transient<IConfigureOptions<MvcFormatterMappingOptions>, JsonMvcFormatterMappingOptionsSetup>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IConfigureOptions<MvcViewOptions>, MvcViewOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcViewOptions>, MvcViewOptionsSetup>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor ServiceDescriptor
.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>()); .Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
// Cors // Cors
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IApplicationModelProvider, CorsApplicationModelProvider>()); ServiceDescriptor.Transient<IApplicationModelProvider, CorsApplicationModelProvider>());
services.TryAdd(ServiceDescriptor.Transient<CorsAuthorizationFilter, CorsAuthorizationFilter>()); services.TryAdd(ServiceDescriptor.Transient<CorsAuthorizationFilter, CorsAuthorizationFilter>());
// Auth // Auth
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>()); ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
// Support for activating ViewDataDictionary // Support for activating ViewDataDictionary
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor ServiceDescriptor
.Transient<IControllerPropertyActivator, ViewDataDictionaryControllerPropertyActivator>()); .Transient<IControllerPropertyActivator, ViewDataDictionaryControllerPropertyActivator>());
@ -264,8 +256,7 @@ namespace Microsoft.Framework.DependencyInjection
// Api Description // Api Description
services.TryAdd(ServiceDescriptor services.TryAdd(ServiceDescriptor
.Singleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>()); .Singleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>());
TryAddMultiRegistrationService( services.TryAddEnumerable(
services,
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>()); ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
} }
@ -297,27 +288,6 @@ namespace Microsoft.Framework.DependencyInjection
return services; return services;
} }
// Adds a service if the service type and implementation type hasn't been added yet. This is needed for
// services like IConfigureOptions<MvcOptions> 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) private static void ConfigureDefaultServices(IServiceCollection services)
{ {
services.AddDataProtection(); services.AddDataProtection();