Adding an 'AddMinimalMvc()' for Mvc.Core and sample

This adds the ability to configure Mvc.Core for a project; MVC with
minimal services and dependencies.
This commit is contained in:
Ryan Nowak 2015-06-09 17:39:48 -07:00
Родитель a665e48826
Коммит faaba481e8
15 изменённых файлов: 605 добавлений и 195 удалений

Просмотреть файл

@ -74,6 +74,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Extens
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Extensions.Test", "test\Microsoft.AspNet.Mvc.Extensions.Test\Microsoft.AspNet.Mvc.Extensions.Test.xproj", "{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MvcMinimalSample.Web", "samples\MvcMinimalSample.Web\MvcMinimalSample.Web.xproj", "{F21E225B-190B-4DAA-8B0A-05986D231F56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -419,6 +421,18 @@ Global
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}.Release|x86.ActiveCfg = Release|Any CPU
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}.Release|x86.Build.0 = Release|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Debug|x86.ActiveCfg = Debug|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Debug|x86.Build.0 = Debug|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Release|Any CPU.Build.0 = Release|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Release|x86.ActiveCfg = Release|Any CPU
{F21E225B-190B-4DAA-8B0A-05986D231F56}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -454,5 +468,6 @@ Global
{4C2AD8AB-8AC0-46C4-80C6-C5577C7255F6} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{B2CA101A-87E6-4DD2-9BB2-28DA68EF1A94} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{F21E225B-190B-4DAA-8B0A-05986D231F56} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,18 @@
// 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.
namespace MvcMinimalSample.Web
{
public class HomeController
{
public string Index()
{
return "Hi from MVC";
}
public string GetUser(int id)
{
return $"User: {id}";
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>f21e225b-190b-4daa-8b0a-05986d231f56</ProjectGuid>
<RootNamespace>MvcMinimalSample.Web</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>4976</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -0,0 +1,21 @@
// 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.Builder;
using Microsoft.Framework.DependencyInjection;
namespace MvcMinimalSample.Web
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMinimalMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvcWithDefaultRoute();
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@
{
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Mvc.Core": "6.0.0-*",
"Microsoft.AspNet.Hosting": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*"
},
"commands": {
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
},
"frameworks": {
"dnx451": { },
"dnxcore50": { }
},
"publishExclude": [
"node_modules",
"bower_components",
"**.xproj",
"**.user",
"**.vspscc"
],
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
]
}

Просмотреть файл

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Minimal MVC Sample</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>

Просмотреть файл

@ -0,0 +1,67 @@
// 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 Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Sets up default options for <see cref="MvcOptions"/>.
/// </summary>
public class CoreMvcOptionsSetup : ConfigureOptions<MvcOptions>
{
public CoreMvcOptionsSetup()
: base(ConfigureMvc)
{
Order = DefaultOrder.DefaultFrameworkSortOrder;
}
public static void ConfigureMvc(MvcOptions options)
{
// Set up ModelBinding
options.ModelBinders.Add(new BinderTypeBasedModelBinder());
options.ModelBinders.Add(new ServicesModelBinder());
options.ModelBinders.Add(new BodyModelBinder());
options.ModelBinders.Add(new HeaderModelBinder());
options.ModelBinders.Add(new TypeConverterModelBinder());
options.ModelBinders.Add(new TypeMatchModelBinder());
options.ModelBinders.Add(new CancellationTokenModelBinder());
options.ModelBinders.Add(new ByteArrayModelBinder());
options.ModelBinders.Add(new FormFileModelBinder());
options.ModelBinders.Add(new FormCollectionModelBinder());
options.ModelBinders.Add(new GenericModelBinder());
options.ModelBinders.Add(new MutableObjectModelBinder());
options.ModelBinders.Add(new ComplexModelDtoModelBinder());
// Set up default output formatters.
options.OutputFormatters.Add(new HttpNoContentOutputFormatter());
options.OutputFormatters.Add(new StringOutputFormatter());
options.OutputFormatters.Add(new StreamOutputFormatter());
// Set up ValueProviders
options.ValueProviderFactories.Add(new RouteValueValueProviderFactory());
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
options.ValueProviderFactories.Add(new FormValueProviderFactory());
// Set up metadata providers
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider());
// Set up validators
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
// Add types to be excluded from Validation
options.ValidationExcludeFilters.Add(new SimpleTypesExcludeFilter());
options.ValidationExcludeFilters.Add(typeof(Type));
// Any 'known' types that we bind should be marked as excluded from validation.
options.ValidationExcludeFilters.Add(typeof(System.Threading.CancellationToken));
options.ValidationExcludeFilters.Add(typeof(Http.IFormFile));
options.ValidationExcludeFilters.Add(typeof(Http.IFormCollection));
}
}
}

Просмотреть файл

@ -0,0 +1,161 @@
// 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;
}
/// <summary>
/// Configures a set of <see cref="MvcOptions"/> for the application.
/// </summary>
/// <param name="services">The services available in the application.</param>
/// <param name="setupAction">The <see cref="MvcOptions"/> which need to be configured.</param>
public static void ConfigureMvc(
[NotNull] this IServiceCollection services,
[NotNull] Action<MvcOptions> setupAction)
{
services.Configure(setupAction);
}
// To enable unit testing
internal static void AddMvcCoreServices(IServiceCollection services)
{
// Options
//
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, CoreMvcOptionsSetup>());
// Action Discovery
//
// 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,
ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());
services.TryAdd(ServiceDescriptor
.Singleton<IActionDescriptorsCollectionProvider, DefaultActionDescriptorsCollectionProvider>());
// Action Selection
//
services.TryAdd(ServiceDescriptor.Singleton<IActionSelector, DefaultActionSelector>());
// Performs caching
services.TryAdd(ServiceDescriptor
.Singleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>());
// This provider needs access to the per-request services, but might be used many times for a given
// request.
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IActionConstraintProvider, DefaultActionConstraintProvider>());
// Action Invoker
//
// This has a cache, so it needs to be a singleton
services.TryAdd(ServiceDescriptor.Singleton<IControllerFactory, DefaultControllerFactory>());
services.TryAdd(ServiceDescriptor.Transient<IControllerActivator, DefaultControllerActivator>());
// This accesses per-request services
services.TryAdd(ServiceDescriptor.Transient<IActionInvokerFactory, ActionInvokerFactory>());
services.TryAdd(ServiceDescriptor
.Transient<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>());
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IFilterProvider, DefaultFilterProvider>());
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>());
// ModelBinding, Validation and Formatting
//
// The DefaultModelMetadataProvider does significant caching and should be a singleton.
services.TryAdd(ServiceDescriptor.Singleton<IModelMetadataProvider, DefaultModelMetadataProvider>());
services.TryAdd(ServiceDescriptor.Transient<ICompositeMetadataDetailsProvider>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options;
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
}));
services.TryAdd(ServiceDescriptor.Transient<IObjectModelValidator>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options;
var modelMetadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>();
return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider);
}));
// Temp Data
//
services.TryAdd(ServiceDescriptor.Scoped<ITempDataDictionary, TempDataDictionary>());
// This does caching so it should stay singleton
services.TryAdd(ServiceDescriptor.Singleton<ITempDataProvider, SessionStateTempDataProvider>());
// Random Infrastructure
//
services.TryAdd(ServiceDescriptor.Transient<MvcMarkerService, MvcMarkerService>());
services.TryAdd((ServiceDescriptor.Singleton<ITypeActivatorCache, DefaultTypeActivatorCache>()));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>)));
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.AddCors();
services.AddNotifier();
services.Configure<RouteOptions>(
routeOptions => routeOptions.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint)));
}
}
}

Просмотреть файл

@ -1,13 +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;
using System.Xml.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.Mvc
@ -20,57 +17,18 @@ namespace Microsoft.AspNet.Mvc
public MvcOptionsSetup()
: base(ConfigureMvc)
{
Order = DefaultOrder.DefaultFrameworkSortOrder;
Order = DefaultOrder.DefaultFrameworkSortOrder + 1;
}
public static void ConfigureMvc(MvcOptions options)
{
// Set up ModelBinding
options.ModelBinders.Add(new BinderTypeBasedModelBinder());
options.ModelBinders.Add(new ServicesModelBinder());
options.ModelBinders.Add(new BodyModelBinder());
options.ModelBinders.Add(new HeaderModelBinder());
options.ModelBinders.Add(new TypeConverterModelBinder());
options.ModelBinders.Add(new TypeMatchModelBinder());
options.ModelBinders.Add(new CancellationTokenModelBinder());
options.ModelBinders.Add(new ByteArrayModelBinder());
options.ModelBinders.Add(new FormFileModelBinder());
options.ModelBinders.Add(new FormCollectionModelBinder());
options.ModelBinders.Add(new GenericModelBinder());
options.ModelBinders.Add(new MutableObjectModelBinder());
options.ModelBinders.Add(new ComplexModelDtoModelBinder());
// Set up default output formatters.
options.OutputFormatters.Add(new HttpNoContentOutputFormatter());
options.OutputFormatters.Add(new StringOutputFormatter());
options.OutputFormatters.Add(new StreamOutputFormatter());
// Set up ValueProviders
options.ValueProviderFactories.Add(new RouteValueValueProviderFactory());
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
options.ValueProviderFactories.Add(new FormValueProviderFactory());
// Set up metadata providers
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DataMemberRequiredBindingMetadataProvider());
// Set up validators
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider());
// Add types to be excluded from Validation
options.ValidationExcludeFilters.Add(new SimpleTypesExcludeFilter());
options.ValidationExcludeFilters.Add(typeof(XObject));
options.ValidationExcludeFilters.Add(typeof(Type));
options.ValidationExcludeFilters.Add(typeof(JToken));
// Any 'known' types that we bind should be marked as excluded from validation.
options.ValidationExcludeFilters.Add(typeof(System.Threading.CancellationToken));
options.ValidationExcludeFilters.Add(typeof(Http.IFormFile));
options.ValidationExcludeFilters.Add(typeof(Http.IFormCollection));
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.XmlNode");
}
}

Просмотреть файл

@ -7,22 +7,13 @@ using System.Diagnostics;
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;
@ -33,6 +24,8 @@ namespace Microsoft.Framework.DependencyInjection
{
public static IServiceCollection AddMvc([NotNull] this IServiceCollection services)
{
services.AddMinimalMvc();
ConfigureDefaultServices(services);
AddMvcServices(services);
@ -52,18 +45,6 @@ namespace Microsoft.Framework.DependencyInjection
services.Configure(setupAction);
}
/// <summary>
/// Configures a set of <see cref="MvcOptions"/> for the application.
/// </summary>
/// <param name="services">The services available in the application.</param>
/// <param name="setupAction">The <see cref="MvcOptions"/> which need to be configured.</param>
public static void ConfigureMvc(
[NotNull] this IServiceCollection services,
[NotNull] Action<MvcOptions> setupAction)
{
services.Configure(setupAction);
}
/// <summary>
/// Configures a set of <see cref="MvcFormatterMappingOptions"/> for the application.
/// </summary>
@ -182,86 +163,25 @@ namespace Microsoft.Framework.DependencyInjection
ServiceDescriptor
.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
services.TryAdd(ServiceDescriptor.Transient<MvcMarkerService, MvcMarkerService>());
services.TryAdd((ServiceDescriptor.Singleton<ITypeActivatorCache, DefaultTypeActivatorCache>()));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>)));
// Core action discovery, filters and action execution.
// This 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,
ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
// Cors
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IApplicationModelProvider, CorsApplicationModelProvider>());
services.TryAdd(ServiceDescriptor.Transient<CorsAuthorizationFilter, CorsAuthorizationFilter>());
// Auth
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
// This has a cache, so it needs to be a singleton
services.TryAdd(ServiceDescriptor.Singleton<IControllerFactory, DefaultControllerFactory>());
services.TryAdd(ServiceDescriptor.Transient<IControllerActivator, DefaultControllerActivator>());
// This accesses per-request services
services.TryAdd(ServiceDescriptor.Transient<IActionInvokerFactory, ActionInvokerFactory>());
// This provider needs access to the per-request services, but might be used many times for a given
// request.
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IActionConstraintProvider, DefaultActionConstraintProvider>());
services.TryAdd(ServiceDescriptor
.Singleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>());
services.TryAdd(ServiceDescriptor.Singleton<IActionSelector, DefaultActionSelector>());
services.TryAdd(ServiceDescriptor
.Transient<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>());
services.TryAdd(ServiceDescriptor.Transient<IObjectModelValidator>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options;
var modelMetadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>();
return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider);
}));
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());
services.TryAdd(ServiceDescriptor
.Singleton<IActionDescriptorsCollectionProvider, DefaultActionDescriptorsCollectionProvider>());
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IFilterProvider, DefaultFilterProvider>());
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>());
// Support for activating ViewDataDictionary
TryAddMultiRegistrationService(
services,
ServiceDescriptor
.Transient<IControllerPropertyActivator, ViewDataDictionaryControllerPropertyActivator>());
// Formatter Mappings
services.TryAdd(ServiceDescriptor.Transient<FormatFilter, FormatFilter>());
services.TryAdd(ServiceDescriptor.Transient<CorsAuthorizationFilter, CorsAuthorizationFilter>());
// Dataflow - ModelBinding, Validation and Formatting
//
// The DefaultModelMetadataProvider does significant caching and should be a singleton.
services.TryAdd(ServiceDescriptor.Singleton<IModelMetadataProvider, DefaultModelMetadataProvider>());
services.TryAdd(ServiceDescriptor.Transient<ICompositeMetadataDetailsProvider>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Options;
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
}));
// JsonOutputFormatter should use the SerializerSettings on MvcOptions
services.TryAdd(ServiceDescriptor.Singleton<JsonOutputFormatter>(serviceProvider =>
@ -311,7 +231,6 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(ServiceDescriptor.Transient<IHtmlHelper, HtmlHelper>());
services.TryAdd(ServiceDescriptor.Transient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>)));
services.TryAdd(ServiceDescriptor.Transient<IJsonHelper, JsonHelper>());
services.TryAdd(ServiceDescriptor.Scoped<IUrlHelper, UrlHelper>());
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
services.TryAdd(ServiceDescriptor.Singleton<ITagHelperActivator, DefaultTagHelperActivator>());
@ -348,11 +267,6 @@ namespace Microsoft.Framework.DependencyInjection
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
// Temp Data
services.TryAdd(ServiceDescriptor.Scoped<ITempDataDictionary, TempDataDictionary>());
// This does caching so it should stay singleton
services.TryAdd(ServiceDescriptor.Singleton<ITempDataProvider, SessionStateTempDataProvider>());
}
/// <summary>
@ -406,15 +320,10 @@ namespace Microsoft.Framework.DependencyInjection
private static void ConfigureDefaultServices(IServiceCollection services)
{
services.AddOptions();
services.AddDataProtection();
services.AddRouting();
services.AddCors();
services.AddAuthorization();
services.AddWebEncoders();
services.AddNotifier();
services.Configure<RouteOptions>(
routeOptions => routeOptions.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint)));
}
}
}

Просмотреть файл

@ -0,0 +1,216 @@
// 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.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc.ActionConstraints;
using Microsoft.AspNet.Mvc.ApplicationModels;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class MvcCoreServiceCollectionExtensionsTest
{
// Some MVC services can be registered multiple times, for example, 'IConfigureOptions<MvcOptions>' can
// be registered by calling 'ConfigureMvc(...)' before the call to 'AddMvc()' in which case the options
// configuration is run in the order they were registered.
//
// For these kind of multi registration service types, we want to make sure that MVC will still add its
// services if the implementation type is different.
[Fact]
public void MultiRegistrationServiceTypes_AreRegistered_MultipleTimes()
{
// Arrange
var services = new ServiceCollection();
// Register a mock implementation of each service, AddMvcServices should add another implemenetation.
foreach (var serviceType in MutliRegistrationServiceTypes)
{
var mockType = typeof(Mock<>).MakeGenericType(serviceType.Key);
services.Add(ServiceDescriptor.Transient(serviceType.Key, mockType));
}
// Act
MvcCoreServiceCollectionExtensions.AddMvcCoreServices(services);
// Assert
foreach (var serviceType in MutliRegistrationServiceTypes)
{
AssertServiceCountEquals(services, serviceType.Key, serviceType.Value.Length + 1);
foreach (var implementationType in serviceType.Value)
{
AssertContainsSingle(services, serviceType.Key, implementationType);
}
}
}
[Fact]
public void SingleRegistrationServiceTypes_AreNotRegistered_MultipleTimes()
{
// Arrange
var services = new ServiceCollection();
// Register a mock implementation of each service, AddMvcServices should not replace it.
foreach (var serviceType in SingleRegistrationServiceTypes)
{
var mockType = typeof(Mock<>).MakeGenericType(serviceType);
services.Add(ServiceDescriptor.Transient(serviceType, mockType));
}
// Act
MvcCoreServiceCollectionExtensions.AddMvcCoreServices(services);
// Assert
foreach (var singleRegistrationType in SingleRegistrationServiceTypes)
{
AssertServiceCountEquals(services, singleRegistrationType, 1);
}
}
[Fact]
public void AddMvcServicesTwice_DoesNotAddDuplicates()
{
// Arrange
var services = new ServiceCollection();
// Act
MvcCoreServiceCollectionExtensions.AddMvcCoreServices(services);
MvcCoreServiceCollectionExtensions.AddMvcCoreServices(services);
// Assert
var singleRegistrationServiceTypes = SingleRegistrationServiceTypes;
foreach (var service in services)
{
if (singleRegistrationServiceTypes.Contains(service.ServiceType))
{
// 'single-registration' services should only have one implementation registered.
AssertServiceCountEquals(services, service.ServiceType, 1);
}
else
{
// 'multi-registration' services should only have one *instance* of each implementation registered.
AssertContainsSingle(services, service.ServiceType, service.ImplementationType);
}
}
}
private IEnumerable<Type> SingleRegistrationServiceTypes
{
get
{
var services = new ServiceCollection();
MvcCoreServiceCollectionExtensions.AddMvcCoreServices(services);
var multiRegistrationServiceTypes = MutliRegistrationServiceTypes;
return services
.Where(sd => !multiRegistrationServiceTypes.Keys.Contains(sd.ServiceType))
.Select(sd => sd.ServiceType);
}
}
private Dictionary<Type, Type[]> MutliRegistrationServiceTypes
{
get
{
return new Dictionary<Type, Type[]>()
{
{
typeof(IConfigureOptions<MvcOptions>),
new Type[]
{
typeof(CoreMvcOptionsSetup),
}
},
{
typeof(IActionConstraintProvider),
new Type[]
{
typeof(DefaultActionConstraintProvider),
}
},
{
typeof(IActionDescriptorProvider),
new Type[]
{
typeof(ControllerActionDescriptorProvider),
}
},
{
typeof(IActionInvokerProvider),
new Type[]
{
typeof(ControllerActionInvokerProvider),
}
},
{
typeof(IFilterProvider),
new Type[]
{
typeof(DefaultFilterProvider),
}
},
{
typeof(IControllerPropertyActivator),
new Type[]
{
typeof(DefaultControllerPropertyActivator),
}
},
{
typeof(IApplicationModelProvider),
new Type[]
{
typeof(DefaultApplicationModelProvider),
}
},
};
}
}
private void AssertServiceCountEquals(
IServiceCollection services,
Type serviceType,
int expectedServiceRegistrationCount)
{
var serviceDescriptors = services.Where(serviceDescriptor => serviceDescriptor.ServiceType == serviceType);
var actual = serviceDescriptors.Count();
Assert.True(
(expectedServiceRegistrationCount == actual),
$"Expected service type '{serviceType}' to be registered {expectedServiceRegistrationCount}" +
$" time(s) but was actually registered {actual} time(s).");
}
private void AssertContainsSingle(
IServiceCollection services,
Type serviceType,
Type implementationType)
{
var matches = services
.Where(sd =>
sd.ServiceType == serviceType &&
sd.ImplementationType == implementationType)
.ToArray();
if (matches.Length == 0)
{
Assert.True(
false,
$"Could not find an instance of {implementationType} registered as {serviceType}");
}
else if (matches.Length > 1)
{
Assert.True(
false,
$"Found multiple instances of {implementationType} registered as {serviceType}");
}
}
}
}

Просмотреть файл

@ -12,6 +12,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public TestMvcOptions()
{
Options = new MvcOptions();
CoreMvcOptionsSetup.ConfigureMvc(Options);
MvcOptionsSetup.ConfigureMvc(Options);
JsonMvcOptionsSetup.ConfigureMvc(Options, SerializerSettingsProvider.CreateSerializerSettings());
}

Просмотреть файл

@ -35,10 +35,12 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var mvcOptions = new MvcOptions();
var setup = new MvcOptionsSetup();
var setup1 = new CoreMvcOptionsSetup();
var setup2 = new MvcOptionsSetup();
// Act
setup.Configure(mvcOptions);
setup1.Configure(mvcOptions);
setup2.Configure(mvcOptions);
// Assert
var i = 0;
@ -63,10 +65,12 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var mvcOptions = new MvcOptions();
var setup = new MvcOptionsSetup();
var setup1 = new CoreMvcOptionsSetup();
var setup2 = new MvcOptionsSetup();
// Act
setup.Configure(mvcOptions);
setup1.Configure(mvcOptions);
setup2.Configure(mvcOptions);
// Assert
var valueProviders = mvcOptions.ValueProviderFactories;
@ -81,13 +85,15 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var mvcOptions = new MvcOptions();
var setup1 = new MvcOptionsSetup();
var setup2 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
var setup1 = new CoreMvcOptionsSetup();
var setup2 = new MvcOptionsSetup();
var setup3 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
Enumerable.Empty<IConfigureOptions<MvcJsonOptions>>()));
// Act
setup1.Configure(mvcOptions);
setup2.Configure(mvcOptions);
setup3.Configure(mvcOptions);
// Assert
Assert.Equal(4, mvcOptions.OutputFormatters.Count);
@ -102,13 +108,15 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var mvcOptions = new MvcOptions();
var setup1 = new MvcOptionsSetup();
var setup2 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
var setup1 = new CoreMvcOptionsSetup();
var setup2 = new MvcOptionsSetup();
var setup3 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
Enumerable.Empty<IConfigureOptions<MvcJsonOptions>>()));
// Act
setup1.Configure(mvcOptions);
setup2.Configure(mvcOptions);
setup3.Configure(mvcOptions);
// Assert
Assert.Equal(2, mvcOptions.InputFormatters.Count);
@ -121,10 +129,12 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var mvcOptions = new MvcOptions();
var setup = new MvcOptionsSetup();
var setup1 = new CoreMvcOptionsSetup();
var setup2 = new MvcOptionsSetup();
// Act
setup.Configure(mvcOptions);
setup1.Configure(mvcOptions);
setup2.Configure(mvcOptions);
// Assert
Assert.Equal(2, mvcOptions.ModelValidatorProviders.Count);
@ -153,7 +163,7 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var mvcOptions = new MvcOptions();
var setup = new MvcOptionsSetup();
var setup = new CoreMvcOptionsSetup();
// Act
setup.Configure(mvcOptions);
@ -167,10 +177,12 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var mvcOptions = new MvcOptions();
var setup = new MvcOptionsSetup();
var setup1 = new CoreMvcOptionsSetup();
var setup2 = new MvcOptionsSetup();
// Act
setup.Configure(mvcOptions);
setup1.Configure(mvcOptions);
setup2.Configure(mvcOptions);
// Assert
Assert.Equal(8, mvcOptions.ValidationExcludeFilters.Count);
@ -178,21 +190,12 @@ namespace Microsoft.AspNet.Mvc
// Verify if the delegates registered by default exclude the given types.
Assert.IsType(typeof(SimpleTypesExcludeFilter), mvcOptions.ValidationExcludeFilters[i++]);
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var xObjectFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(xObjectFilter.ExcludedType, typeof(XObject));
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var typeFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(typeFilter.ExcludedType, typeof(Type));
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var jTokenFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(jTokenFilter.ExcludedType, typeof(JToken));
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var cancellationTokenFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
@ -210,6 +213,16 @@ namespace Microsoft.AspNet.Mvc
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(formCollectionFilter.ExcludedType, typeof(Http.IFormCollection));
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var xObjectFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(xObjectFilter.ExcludedType, typeof(XObject));
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var jTokenFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(jTokenFilter.ExcludedType, typeof(JToken));
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var xmlNodeFilter =
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);

Просмотреть файл

@ -219,34 +219,6 @@ namespace Microsoft.AspNet.Mvc
typeof(RazorViewEngineOptionsSetup),
}
},
{
typeof(IActionConstraintProvider),
new Type[]
{
typeof(DefaultActionConstraintProvider),
}
},
{
typeof(IActionDescriptorProvider),
new Type[]
{
typeof(ControllerActionDescriptorProvider),
}
},
{
typeof(IActionInvokerProvider),
new Type[]
{
typeof(ControllerActionInvokerProvider),
}
},
{
typeof(IFilterProvider),
new Type[]
{
typeof(DefaultFilterProvider),
}
},
{
typeof(IApiDescriptionProvider),
new Type[]
@ -258,7 +230,6 @@ namespace Microsoft.AspNet.Mvc
typeof(IControllerPropertyActivator),
new Type[]
{
typeof(DefaultControllerPropertyActivator),
typeof(ViewDataDictionaryControllerPropertyActivator),
}
},
@ -266,7 +237,6 @@ namespace Microsoft.AspNet.Mvc
typeof(IApplicationModelProvider),
new Type[]
{
typeof(DefaultApplicationModelProvider),
typeof(CorsApplicationModelProvider),
typeof(AuthorizationApplicationModelProvider),
}