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.
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<IConfigureOptions<MvcOptions>, 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<IAssemblyProvider, DefaultAssemblyProvider>());
services.TryAdd(ServiceDescriptor.Transient<IControllerTypeProvider, DefaultControllerTypeProvider>()); ;
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());
services.TryAdd(ServiceDescriptor
.Singleton<IActionDescriptorsCollectionProvider, DefaultActionDescriptorsCollectionProvider>());
@ -74,8 +69,7 @@ namespace Microsoft.Framework.DependencyInjection
.Singleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>());
// 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<IActionConstraintProvider, DefaultActionConstraintProvider>());
// Action Invoker
@ -87,14 +81,11 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(ServiceDescriptor.Transient<IActionInvokerFactory, ActionInvokerFactory>());
services.TryAdd(ServiceDescriptor
.Transient<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IFilterProvider, DefaultFilterProvider>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>());
// ModelBinding, Validation and Formatting
@ -127,34 +118,14 @@ namespace Microsoft.Framework.DependencyInjection
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)
{
services.AddOptions();
services.AddRouting();
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.
// 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 = "")

View File

@ -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<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
// new it up.
services.AddInstance<IContentNegotiator>(new DefaultContentNegotiator());
services.Configure<MvcOptions>(options =>
{
options.ValidationExcludeFilters.Add(typeof(HttpRequestMessage));
});
services.TryAdd(ServiceDescriptor.Instance<IContentNegotiator>(new DefaultContentNegotiator()));
return services;
}

View File

@ -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<IConfigureOptions<MvcOptions>, MvcOptionsSetup>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, JsonMvcOptionsSetup>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor
.Transient<IConfigureOptions<MvcFormatterMappingOptions>, JsonMvcFormatterMappingOptionsSetup>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcViewOptions>, MvcViewOptionsSetup>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor
.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
// Cors
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, CorsApplicationModelProvider>());
services.TryAdd(ServiceDescriptor.Transient<CorsAuthorizationFilter, CorsAuthorizationFilter>());
// Auth
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
// Support for activating ViewDataDictionary
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor
.Transient<IControllerPropertyActivator, ViewDataDictionaryControllerPropertyActivator>());
@ -264,8 +256,7 @@ namespace Microsoft.Framework.DependencyInjection
// Api Description
services.TryAdd(ServiceDescriptor
.Singleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>());
TryAddMultiRegistrationService(
services,
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
}
@ -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<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)
{
services.AddDataProtection();