зеркало из https://github.com/aspnet/Mvc.git
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:
Родитель
a665e48826
Коммит
faaba481e8
|
@ -74,6 +74,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Extens
|
||||||
EndProject
|
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}"
|
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
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MvcMinimalSample.Web", "samples\MvcMinimalSample.Web\MvcMinimalSample.Web.xproj", "{F21E225B-190B-4DAA-8B0A-05986D231F56}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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|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.ActiveCfg = Release|Any CPU
|
||||||
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}.Release|x86.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -454,5 +468,6 @@ Global
|
||||||
{4C2AD8AB-8AC0-46C4-80C6-C5577C7255F6} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{4C2AD8AB-8AC0-46C4-80C6-C5577C7255F6} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
{B2CA101A-87E6-4DD2-9BB2-28DA68EF1A94} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
{B2CA101A-87E6-4DD2-9BB2-28DA68EF1A94} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
||||||
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
|
{F21E225B-190B-4DAA-8B0A-05986D231F56} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
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.
|
// 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;
|
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Microsoft.Net.Http.Headers;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
@ -20,57 +17,18 @@ namespace Microsoft.AspNet.Mvc
|
||||||
public MvcOptionsSetup()
|
public MvcOptionsSetup()
|
||||||
: base(ConfigureMvc)
|
: base(ConfigureMvc)
|
||||||
{
|
{
|
||||||
Order = DefaultOrder.DefaultFrameworkSortOrder;
|
Order = DefaultOrder.DefaultFrameworkSortOrder + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConfigureMvc(MvcOptions options)
|
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 DataAnnotationsMetadataProvider());
|
||||||
options.ModelMetadataDetailsProviders.Add(new DataMemberRequiredBindingMetadataProvider());
|
options.ModelMetadataDetailsProviders.Add(new DataMemberRequiredBindingMetadataProvider());
|
||||||
|
|
||||||
// Set up validators
|
|
||||||
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
|
|
||||||
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider());
|
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(XObject));
|
||||||
options.ValidationExcludeFilters.Add(typeof(Type));
|
|
||||||
options.ValidationExcludeFilters.Add(typeof(JToken));
|
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");
|
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.XmlNode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,13 @@ using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Mvc.ActionConstraints;
|
|
||||||
using Microsoft.AspNet.Mvc.ApiExplorer;
|
using Microsoft.AspNet.Mvc.ApiExplorer;
|
||||||
using Microsoft.AspNet.Mvc.ApplicationModels;
|
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;
|
||||||
using Microsoft.AspNet.Mvc.Razor.Compilation;
|
using Microsoft.AspNet.Mvc.Razor.Compilation;
|
||||||
using Microsoft.AspNet.Mvc.Razor.Directives;
|
using Microsoft.AspNet.Mvc.Razor.Directives;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
|
||||||
using Microsoft.AspNet.Mvc.ViewComponents;
|
using Microsoft.AspNet.Mvc.ViewComponents;
|
||||||
using Microsoft.AspNet.Routing;
|
|
||||||
using Microsoft.Framework.Caching.Memory;
|
using Microsoft.Framework.Caching.Memory;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
@ -33,6 +24,8 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddMvc([NotNull] this IServiceCollection services)
|
public static IServiceCollection AddMvc([NotNull] this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
services.AddMinimalMvc();
|
||||||
|
|
||||||
ConfigureDefaultServices(services);
|
ConfigureDefaultServices(services);
|
||||||
|
|
||||||
AddMvcServices(services);
|
AddMvcServices(services);
|
||||||
|
@ -52,18 +45,6 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
services.Configure(setupAction);
|
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>
|
/// <summary>
|
||||||
/// Configures a set of <see cref="MvcFormatterMappingOptions"/> for the application.
|
/// Configures a set of <see cref="MvcFormatterMappingOptions"/> for the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -182,86 +163,25 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
ServiceDescriptor
|
ServiceDescriptor
|
||||||
.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
|
.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
|
||||||
|
|
||||||
services.TryAdd(ServiceDescriptor.Transient<MvcMarkerService, MvcMarkerService>());
|
// Cors
|
||||||
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>());
|
|
||||||
TryAddMultiRegistrationService(
|
TryAddMultiRegistrationService(
|
||||||
services,
|
services,
|
||||||
ServiceDescriptor.Transient<IApplicationModelProvider, CorsApplicationModelProvider>());
|
ServiceDescriptor.Transient<IApplicationModelProvider, CorsApplicationModelProvider>());
|
||||||
|
services.TryAdd(ServiceDescriptor.Transient<CorsAuthorizationFilter, CorsAuthorizationFilter>());
|
||||||
|
|
||||||
|
// Auth
|
||||||
TryAddMultiRegistrationService(
|
TryAddMultiRegistrationService(
|
||||||
services,
|
services,
|
||||||
ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
|
ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
|
||||||
|
|
||||||
// This has a cache, so it needs to be a singleton
|
// Support for activating ViewDataDictionary
|
||||||
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>());
|
|
||||||
TryAddMultiRegistrationService(
|
TryAddMultiRegistrationService(
|
||||||
services,
|
services,
|
||||||
ServiceDescriptor
|
ServiceDescriptor
|
||||||
.Transient<IControllerPropertyActivator, ViewDataDictionaryControllerPropertyActivator>());
|
.Transient<IControllerPropertyActivator, ViewDataDictionaryControllerPropertyActivator>());
|
||||||
|
|
||||||
|
// Formatter Mappings
|
||||||
services.TryAdd(ServiceDescriptor.Transient<FormatFilter, FormatFilter>());
|
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
|
// JsonOutputFormatter should use the SerializerSettings on MvcOptions
|
||||||
services.TryAdd(ServiceDescriptor.Singleton<JsonOutputFormatter>(serviceProvider =>
|
services.TryAdd(ServiceDescriptor.Singleton<JsonOutputFormatter>(serviceProvider =>
|
||||||
|
@ -311,7 +231,6 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
services.TryAdd(ServiceDescriptor.Transient<IHtmlHelper, HtmlHelper>());
|
services.TryAdd(ServiceDescriptor.Transient<IHtmlHelper, HtmlHelper>());
|
||||||
services.TryAdd(ServiceDescriptor.Transient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>)));
|
services.TryAdd(ServiceDescriptor.Transient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>)));
|
||||||
services.TryAdd(ServiceDescriptor.Transient<IJsonHelper, JsonHelper>());
|
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.
|
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
|
||||||
services.TryAdd(ServiceDescriptor.Singleton<ITagHelperActivator, DefaultTagHelperActivator>());
|
services.TryAdd(ServiceDescriptor.Singleton<ITagHelperActivator, DefaultTagHelperActivator>());
|
||||||
|
@ -348,11 +267,6 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
TryAddMultiRegistrationService(
|
TryAddMultiRegistrationService(
|
||||||
services,
|
services,
|
||||||
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
|
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>
|
/// <summary>
|
||||||
|
@ -406,15 +320,10 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
|
|
||||||
private static void ConfigureDefaultServices(IServiceCollection services)
|
private static void ConfigureDefaultServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddOptions();
|
|
||||||
services.AddDataProtection();
|
services.AddDataProtection();
|
||||||
services.AddRouting();
|
|
||||||
services.AddCors();
|
services.AddCors();
|
||||||
services.AddAuthorization();
|
services.AddAuthorization();
|
||||||
services.AddWebEncoders();
|
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()
|
public TestMvcOptions()
|
||||||
{
|
{
|
||||||
Options = new MvcOptions();
|
Options = new MvcOptions();
|
||||||
|
CoreMvcOptionsSetup.ConfigureMvc(Options);
|
||||||
MvcOptionsSetup.ConfigureMvc(Options);
|
MvcOptionsSetup.ConfigureMvc(Options);
|
||||||
JsonMvcOptionsSetup.ConfigureMvc(Options, SerializerSettingsProvider.CreateSerializerSettings());
|
JsonMvcOptionsSetup.ConfigureMvc(Options, SerializerSettingsProvider.CreateSerializerSettings());
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcOptions = new MvcOptions();
|
var mvcOptions = new MvcOptions();
|
||||||
var setup = new MvcOptionsSetup();
|
var setup1 = new CoreMvcOptionsSetup();
|
||||||
|
var setup2 = new MvcOptionsSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
setup.Configure(mvcOptions);
|
setup1.Configure(mvcOptions);
|
||||||
|
setup2.Configure(mvcOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
@ -63,10 +65,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcOptions = new MvcOptions();
|
var mvcOptions = new MvcOptions();
|
||||||
var setup = new MvcOptionsSetup();
|
var setup1 = new CoreMvcOptionsSetup();
|
||||||
|
var setup2 = new MvcOptionsSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
setup.Configure(mvcOptions);
|
setup1.Configure(mvcOptions);
|
||||||
|
setup2.Configure(mvcOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var valueProviders = mvcOptions.ValueProviderFactories;
|
var valueProviders = mvcOptions.ValueProviderFactories;
|
||||||
|
@ -81,13 +85,15 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcOptions = new MvcOptions();
|
var mvcOptions = new MvcOptions();
|
||||||
var setup1 = new MvcOptionsSetup();
|
var setup1 = new CoreMvcOptionsSetup();
|
||||||
var setup2 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
|
var setup2 = new MvcOptionsSetup();
|
||||||
|
var setup3 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
|
||||||
Enumerable.Empty<IConfigureOptions<MvcJsonOptions>>()));
|
Enumerable.Empty<IConfigureOptions<MvcJsonOptions>>()));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
setup1.Configure(mvcOptions);
|
setup1.Configure(mvcOptions);
|
||||||
setup2.Configure(mvcOptions);
|
setup2.Configure(mvcOptions);
|
||||||
|
setup3.Configure(mvcOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(4, mvcOptions.OutputFormatters.Count);
|
Assert.Equal(4, mvcOptions.OutputFormatters.Count);
|
||||||
|
@ -102,13 +108,15 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcOptions = new MvcOptions();
|
var mvcOptions = new MvcOptions();
|
||||||
var setup1 = new MvcOptionsSetup();
|
var setup1 = new CoreMvcOptionsSetup();
|
||||||
var setup2 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
|
var setup2 = new MvcOptionsSetup();
|
||||||
|
var setup3 = new JsonMvcOptionsSetup(new OptionsManager<MvcJsonOptions>(
|
||||||
Enumerable.Empty<IConfigureOptions<MvcJsonOptions>>()));
|
Enumerable.Empty<IConfigureOptions<MvcJsonOptions>>()));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
setup1.Configure(mvcOptions);
|
setup1.Configure(mvcOptions);
|
||||||
setup2.Configure(mvcOptions);
|
setup2.Configure(mvcOptions);
|
||||||
|
setup3.Configure(mvcOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(2, mvcOptions.InputFormatters.Count);
|
Assert.Equal(2, mvcOptions.InputFormatters.Count);
|
||||||
|
@ -121,10 +129,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcOptions = new MvcOptions();
|
var mvcOptions = new MvcOptions();
|
||||||
var setup = new MvcOptionsSetup();
|
var setup1 = new CoreMvcOptionsSetup();
|
||||||
|
var setup2 = new MvcOptionsSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
setup.Configure(mvcOptions);
|
setup1.Configure(mvcOptions);
|
||||||
|
setup2.Configure(mvcOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(2, mvcOptions.ModelValidatorProviders.Count);
|
Assert.Equal(2, mvcOptions.ModelValidatorProviders.Count);
|
||||||
|
@ -153,7 +163,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcOptions = new MvcOptions();
|
var mvcOptions = new MvcOptions();
|
||||||
var setup = new MvcOptionsSetup();
|
var setup = new CoreMvcOptionsSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
setup.Configure(mvcOptions);
|
setup.Configure(mvcOptions);
|
||||||
|
@ -167,10 +177,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mvcOptions = new MvcOptions();
|
var mvcOptions = new MvcOptions();
|
||||||
var setup = new MvcOptionsSetup();
|
var setup1 = new CoreMvcOptionsSetup();
|
||||||
|
var setup2 = new MvcOptionsSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
setup.Configure(mvcOptions);
|
setup1.Configure(mvcOptions);
|
||||||
|
setup2.Configure(mvcOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(8, mvcOptions.ValidationExcludeFilters.Count);
|
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.
|
// Verify if the delegates registered by default exclude the given types.
|
||||||
Assert.IsType(typeof(SimpleTypesExcludeFilter), mvcOptions.ValidationExcludeFilters[i++]);
|
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]);
|
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
|
||||||
var typeFilter
|
var typeFilter
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
||||||
Assert.Equal(typeFilter.ExcludedType, typeof(Type));
|
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]);
|
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
|
||||||
var cancellationTokenFilter
|
var cancellationTokenFilter
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
||||||
|
@ -210,6 +213,16 @@ namespace Microsoft.AspNet.Mvc
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
||||||
Assert.Equal(formCollectionFilter.ExcludedType, typeof(Http.IFormCollection));
|
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]);
|
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
|
||||||
var xmlNodeFilter =
|
var xmlNodeFilter =
|
||||||
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[i++]);
|
||||||
|
|
|
@ -219,34 +219,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
typeof(RazorViewEngineOptionsSetup),
|
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),
|
typeof(IApiDescriptionProvider),
|
||||||
new Type[]
|
new Type[]
|
||||||
|
@ -258,7 +230,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
typeof(IControllerPropertyActivator),
|
typeof(IControllerPropertyActivator),
|
||||||
new Type[]
|
new Type[]
|
||||||
{
|
{
|
||||||
typeof(DefaultControllerPropertyActivator),
|
|
||||||
typeof(ViewDataDictionaryControllerPropertyActivator),
|
typeof(ViewDataDictionaryControllerPropertyActivator),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -266,7 +237,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
typeof(IApplicationModelProvider),
|
typeof(IApplicationModelProvider),
|
||||||
new Type[]
|
new Type[]
|
||||||
{
|
{
|
||||||
typeof(DefaultApplicationModelProvider),
|
|
||||||
typeof(CorsApplicationModelProvider),
|
typeof(CorsApplicationModelProvider),
|
||||||
typeof(AuthorizationApplicationModelProvider),
|
typeof(AuthorizationApplicationModelProvider),
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче