diff --git a/Mvc.NoFun.sln b/Mvc.NoFun.sln
index 2164ee973e..33d7b08eff 100644
--- a/Mvc.NoFun.sln
+++ b/Mvc.NoFun.sln
@@ -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
diff --git a/samples/MvcMinimalSample.Web/HomeController.cs b/samples/MvcMinimalSample.Web/HomeController.cs
new file mode 100644
index 0000000000..a78cf8c985
--- /dev/null
+++ b/samples/MvcMinimalSample.Web/HomeController.cs
@@ -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}";
+ }
+ }
+}
diff --git a/samples/MvcMinimalSample.Web/MvcMinimalSample.Web.xproj b/samples/MvcMinimalSample.Web/MvcMinimalSample.Web.xproj
new file mode 100644
index 0000000000..e9cdd21b1e
--- /dev/null
+++ b/samples/MvcMinimalSample.Web/MvcMinimalSample.Web.xproj
@@ -0,0 +1,19 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ f21e225b-190b-4daa-8b0a-05986d231f56
+ MvcMinimalSample.Web
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+ 2.0
+ 4976
+
+
+
\ No newline at end of file
diff --git a/samples/MvcMinimalSample.Web/Startup.cs b/samples/MvcMinimalSample.Web/Startup.cs
new file mode 100644
index 0000000000..bd4908a517
--- /dev/null
+++ b/samples/MvcMinimalSample.Web/Startup.cs
@@ -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();
+ }
+ }
+}
diff --git a/samples/MvcMinimalSample.Web/project.json b/samples/MvcMinimalSample.Web/project.json
new file mode 100644
index 0000000000..2c2be227ac
--- /dev/null
+++ b/samples/MvcMinimalSample.Web/project.json
@@ -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"
+ ]
+}
diff --git a/samples/MvcMinimalSample.Web/wwwroot/Index.html b/samples/MvcMinimalSample.Web/wwwroot/Index.html
new file mode 100644
index 0000000000..7659725483
--- /dev/null
+++ b/samples/MvcMinimalSample.Web/wwwroot/Index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Minimal MVC Sample
+
+
+ Hello, World!
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc/BuilderExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/BuilderExtensions.cs
similarity index 100%
rename from src/Microsoft.AspNet.Mvc/BuilderExtensions.cs
rename to src/Microsoft.AspNet.Mvc.Core/BuilderExtensions.cs
diff --git a/src/Microsoft.AspNet.Mvc.Core/CoreMvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Core/CoreMvcOptionsSetup.cs
new file mode 100644
index 0000000000..b4bf49631f
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/CoreMvcOptionsSetup.cs
@@ -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
+{
+ ///
+ /// Sets up default options for .
+ ///
+ public class CoreMvcOptionsSetup : ConfigureOptions
+ {
+ 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));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcCoreServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcCoreServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..11544de30b
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/MvcCoreServiceCollectionExtensions.cs
@@ -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;
+ }
+
+ ///
+ /// Configures a set of for the application.
+ ///
+ /// The services available in the application.
+ /// The which need to be configured.
+ public static void ConfigureMvc(
+ [NotNull] this IServiceCollection services,
+ [NotNull] Action setupAction)
+ {
+ services.Configure(setupAction);
+ }
+
+ // To enable unit testing
+ internal static void AddMvcCoreServices(IServiceCollection services)
+ {
+ // Options
+ //
+ TryAddMultiRegistrationService(
+ services,
+ ServiceDescriptor.Transient, CoreMvcOptionsSetup>());
+
+ // Action Discovery
+ //
+ // These are consumed only when creating action descriptors, then they can be de-allocated
+ services.TryAdd(ServiceDescriptor.Transient());
+ services.TryAdd(ServiceDescriptor.Transient()); ;
+ TryAddMultiRegistrationService(
+ services,
+ ServiceDescriptor.Transient());
+ TryAddMultiRegistrationService(
+ services,
+ ServiceDescriptor.Transient());
+ services.TryAdd(ServiceDescriptor
+ .Singleton());
+
+ // Action Selection
+ //
+ services.TryAdd(ServiceDescriptor.Singleton());
+ // Performs caching
+ services.TryAdd(ServiceDescriptor
+ .Singleton());
+ // This provider needs access to the per-request services, but might be used many times for a given
+ // request.
+ TryAddMultiRegistrationService(
+ services,
+ ServiceDescriptor.Transient());
+
+ // Action Invoker
+ //
+ // This has a cache, so it needs to be a singleton
+ services.TryAdd(ServiceDescriptor.Singleton());
+ services.TryAdd(ServiceDescriptor.Transient());
+ // This accesses per-request services
+ services.TryAdd(ServiceDescriptor.Transient());
+ services.TryAdd(ServiceDescriptor
+ .Transient());
+ TryAddMultiRegistrationService(
+ services,
+ ServiceDescriptor.Transient());
+ TryAddMultiRegistrationService(
+ services,
+ ServiceDescriptor.Transient());
+ TryAddMultiRegistrationService(
+ services,
+ ServiceDescriptor.Transient());
+
+ // ModelBinding, Validation and Formatting
+ //
+ // The DefaultModelMetadataProvider does significant caching and should be a singleton.
+ services.TryAdd(ServiceDescriptor.Singleton());
+ services.TryAdd(ServiceDescriptor.Transient(serviceProvider =>
+ {
+ var options = serviceProvider.GetRequiredService>().Options;
+ return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
+ }));
+ services.TryAdd(ServiceDescriptor.Transient(serviceProvider =>
+ {
+ var options = serviceProvider.GetRequiredService>().Options;
+ var modelMetadataProvider = serviceProvider.GetRequiredService();
+ return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider);
+ }));
+
+ // Temp Data
+ //
+ services.TryAdd(ServiceDescriptor.Scoped());
+ // This does caching so it should stay singleton
+ services.TryAdd(ServiceDescriptor.Singleton());
+
+ // Random Infrastructure
+ //
+ services.TryAdd(ServiceDescriptor.Transient());
+ services.TryAdd((ServiceDescriptor.Singleton()));
+ services.TryAdd(ServiceDescriptor.Scoped(typeof(IScopedInstance<>), typeof(ScopedInstance<>)));
+ services.TryAdd(ServiceDescriptor.Scoped());
+ }
+
+ // Adds a service if the service type and implementation type hasn't been added yet. This is needed for
+ // services like IConfigureOptions or IApplicationModelProvider where you need the ability
+ // to register multiple implementation types for the same service type.
+ private static bool TryAddMultiRegistrationService(IServiceCollection services, ServiceDescriptor descriptor)
+ {
+ // This can't work when registering a factory or instance, you have to register a type.
+ // Additionally, if any existing registrations use a factory or instance, we can't check those, but we don't
+ // assert that because it might be added by user-code.
+ Debug.Assert(descriptor.ImplementationType != null);
+
+ if (services.Any(d =>
+ d.ServiceType == descriptor.ServiceType &&
+ d.ImplementationType == descriptor.ImplementationType))
+ {
+ return false;
+ }
+
+ services.Add(descriptor);
+ return true;
+ }
+
+ private static void ConfigureDefaultServices(IServiceCollection services)
+ {
+ services.AddOptions();
+ services.AddRouting();
+ services.AddCors();
+ services.AddNotifier();
+ services.Configure(
+ routeOptions => routeOptions.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs
index 1aa4ce26b9..67fb93912c 100644
--- a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs
+++ b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs
@@ -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");
}
}
diff --git a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs
index 117dc317b6..3ef0d3a0fc 100644
--- a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs
+++ b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs
@@ -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);
}
- ///
- /// Configures a set of for the application.
- ///
- /// The services available in the application.
- /// The which need to be configured.
- public static void ConfigureMvc(
- [NotNull] this IServiceCollection services,
- [NotNull] Action setupAction)
- {
- services.Configure(setupAction);
- }
-
///
/// Configures a set of for the application.
///
@@ -182,86 +163,25 @@ namespace Microsoft.Framework.DependencyInjection
ServiceDescriptor
.Transient, RazorViewEngineOptionsSetup>());
- services.TryAdd(ServiceDescriptor.Transient());
- services.TryAdd((ServiceDescriptor.Singleton()));
- 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());
- services.TryAdd(ServiceDescriptor.Transient()); ;
- TryAddMultiRegistrationService(
- services,
- ServiceDescriptor.Transient());
+ // Cors
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient());
+ services.TryAdd(ServiceDescriptor.Transient());
+
+ // Auth
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient());
- // This has a cache, so it needs to be a singleton
- services.TryAdd(ServiceDescriptor.Singleton());
-
- services.TryAdd(ServiceDescriptor.Transient());
-
- // This accesses per-request services
- services.TryAdd(ServiceDescriptor.Transient());
-
- // This provider needs access to the per-request services, but might be used many times for a given
- // request.
- TryAddMultiRegistrationService(
- services,
- ServiceDescriptor.Transient());
-
- services.TryAdd(ServiceDescriptor
- .Singleton());
- services.TryAdd(ServiceDescriptor.Singleton());
- services.TryAdd(ServiceDescriptor
- .Transient());
- services.TryAdd(ServiceDescriptor.Transient(serviceProvider =>
- {
- var options = serviceProvider.GetRequiredService>().Options;
- var modelMetadataProvider = serviceProvider.GetRequiredService();
- return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider);
- }));
-
- TryAddMultiRegistrationService(
- services,
- ServiceDescriptor.Transient());
-
- TryAddMultiRegistrationService(
- services,
- ServiceDescriptor.Transient());
-
- services.TryAdd(ServiceDescriptor
- .Singleton());
-
- TryAddMultiRegistrationService(
- services,
- ServiceDescriptor.Transient());
-
- TryAddMultiRegistrationService(
- services,
- ServiceDescriptor.Transient());
+ // Support for activating ViewDataDictionary
TryAddMultiRegistrationService(
services,
ServiceDescriptor
.Transient());
+ // Formatter Mappings
services.TryAdd(ServiceDescriptor.Transient());
- services.TryAdd(ServiceDescriptor.Transient());
-
- // Dataflow - ModelBinding, Validation and Formatting
- //
- // The DefaultModelMetadataProvider does significant caching and should be a singleton.
- services.TryAdd(ServiceDescriptor.Singleton());
- services.TryAdd(ServiceDescriptor.Transient(serviceProvider =>
- {
- var options = serviceProvider.GetRequiredService>().Options;
- return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
- }));
// JsonOutputFormatter should use the SerializerSettings on MvcOptions
services.TryAdd(ServiceDescriptor.Singleton(serviceProvider =>
@@ -311,7 +231,6 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(ServiceDescriptor.Transient());
services.TryAdd(ServiceDescriptor.Transient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>)));
services.TryAdd(ServiceDescriptor.Transient());
- services.TryAdd(ServiceDescriptor.Scoped());
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
services.TryAdd(ServiceDescriptor.Singleton());
@@ -348,11 +267,6 @@ namespace Microsoft.Framework.DependencyInjection
TryAddMultiRegistrationService(
services,
ServiceDescriptor.Transient());
-
- // Temp Data
- services.TryAdd(ServiceDescriptor.Scoped());
- // This does caching so it should stay singleton
- services.TryAdd(ServiceDescriptor.Singleton());
}
///
@@ -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.ConstraintMap.Add("exists", typeof(KnownRouteValueConstraint)));
}
}
-}
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/MvcCoreServiceCollectionExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/MvcCoreServiceCollectionExtensionsTest.cs
new file mode 100644
index 0000000000..1d698e4de4
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/MvcCoreServiceCollectionExtensionsTest.cs
@@ -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' 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 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 MutliRegistrationServiceTypes
+ {
+ get
+ {
+ return new Dictionary()
+ {
+ {
+ typeof(IConfigureOptions),
+ 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}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs
index 28e08e9328..c14efaa3c7 100644
--- a/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs
+++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/TestMvcOptions.cs
@@ -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());
}
diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs
index 7610687395..a8301cc0af 100644
--- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs
@@ -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(
+ var setup1 = new CoreMvcOptionsSetup();
+ var setup2 = new MvcOptionsSetup();
+ var setup3 = new JsonMvcOptionsSetup(new OptionsManager(
Enumerable.Empty>()));
// 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(
+ var setup1 = new CoreMvcOptionsSetup();
+ var setup2 = new MvcOptionsSetup();
+ var setup3 = new JsonMvcOptionsSetup(new OptionsManager(
Enumerable.Empty>()));
// 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(mvcOptions.ValidationExcludeFilters[i++]);
- Assert.Equal(xObjectFilter.ExcludedType, typeof(XObject));
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var typeFilter
= Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(typeFilter.ExcludedType, typeof(Type));
- Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
- var jTokenFilter
- = Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]);
- Assert.Equal(jTokenFilter.ExcludedType, typeof(JToken));
-
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var cancellationTokenFilter
= Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]);
@@ -210,6 +213,16 @@ namespace Microsoft.AspNet.Mvc
= Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]);
Assert.Equal(formCollectionFilter.ExcludedType, typeof(Http.IFormCollection));
+ Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
+ var xObjectFilter
+ = Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]);
+ Assert.Equal(xObjectFilter.ExcludedType, typeof(XObject));
+
+ Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
+ var jTokenFilter
+ = Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]);
+ Assert.Equal(jTokenFilter.ExcludedType, typeof(JToken));
+
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[i]);
var xmlNodeFilter =
Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]);
diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
index cdfc623e65..8fd50591ae 100644
--- a/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
@@ -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),
}