Update to CR feedback
This commit is contained in:
parent
a725e4c9b5
commit
400240673b
|
|
@ -1,8 +1,5 @@
|
|||
using Microsoft.AspNet.Mvc;
|
||||
<<<<<<< HEAD
|
||||
using MvcSample.Models;
|
||||
=======
|
||||
>>>>>>> Support per process caching of controller discovery
|
||||
|
||||
namespace MvcSample
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ namespace MvcSample
|
|||
|
||||
var mvcServices = new MvcServices(appRoot);
|
||||
|
||||
mvcServices.Finalize();
|
||||
|
||||
var handler = (MvcHandler)(ActivatorUtilities.CreateInstance(mvcServices.Services, typeof(MvcHandler)));
|
||||
var handler = ActivatorUtilities.CreateInstance<MvcHandler>(mvcServices.Services);
|
||||
|
||||
builder.Run(async context =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.DependencyInjection;
|
||||
using Microsoft.AspNet.FileSystems;
|
||||
using Microsoft.AspNet.Mvc.Razor;
|
||||
|
|
@ -21,92 +18,38 @@ namespace Microsoft.AspNet.Mvc.Startup
|
|||
{
|
||||
Services = new ServiceProvider();
|
||||
|
||||
AddAndRegisterForFinalization<IControllerFactory, DefaultControllerFactory>();
|
||||
AddAndRegisterForFinalization<IActionInvokerFactory, ActionInvokerFactory>();
|
||||
AddAndRegisterForFinalization<IActionResultHelper, ActionResultHelper>();
|
||||
AddAndRegisterForFinalization<IActionResultFactory, ActionResultFactory>();
|
||||
AddAndRegisterForFinalization<IRouteContextProvider, ControllerActionBasedRouteContextProvider>();
|
||||
AddAndRegisterForFinalization<IActionInvokerProvider, ActionInvokerProvider>();
|
||||
Add<IControllerFactory, DefaultControllerFactory>();
|
||||
Add<IActionInvokerFactory, ActionInvokerFactory>();
|
||||
Add<IActionResultHelper, ActionResultHelper>();
|
||||
Add<IActionResultFactory, ActionResultFactory>();
|
||||
Add<IRouteContextProvider, ControllerActionBasedRouteContextProvider>();
|
||||
Add<IActionInvokerProvider, ActionInvokerProvider>();
|
||||
|
||||
// need singleton support here.
|
||||
// AddAndRegisterForFinalization<SkipAssemblies, DefaultSkipAssemblies>();
|
||||
AddInstanceAndRegisterForFinalization<ControllerCache>(new DefaultControllerCache(new DefaultSkipAssemblies()));
|
||||
AddInstanceAndRegisterForFinalization<IFileSystem>(new PhysicalFileSystem(appRoot));
|
||||
AddInstanceAndRegisterForFinalization<IMvcRazorHost>(new MvcRazorHost("Microsoft.AspNet.Mvc.Razor.RazorView<dynamic>"));
|
||||
// need a design for immutable caches at startup
|
||||
var provider = new DefaultControllerDescriptorProvider(new AppDomainControllerAssemblyProvider());
|
||||
provider.FinalizeSetup();
|
||||
|
||||
AddInstance<IControllerDescriptorProvider>(provider);
|
||||
AddInstance<IFileSystem>(new PhysicalFileSystem(appRoot));
|
||||
AddInstance<IMvcRazorHost>(new MvcRazorHost("Microsoft.AspNet.Mvc.Razor.RazorView<dynamic>"));
|
||||
|
||||
#if NET45
|
||||
AddAndRegisterForFinalization<ICompilationService, CscBasedCompilationService>();
|
||||
Add<ICompilationService, CscBasedCompilationService>();
|
||||
#endif
|
||||
AddAndRegisterForFinalization<IRazorCompilationService, RazorCompilationService>();
|
||||
AddAndRegisterForFinalization<IVirtualPathViewFactory, VirtualPathViewFactory>();
|
||||
AddAndRegisterForFinalization<IViewEngine, RazorViewEngine>();
|
||||
Add<IRazorCompilationService, RazorCompilationService>();
|
||||
Add<IVirtualPathViewFactory, VirtualPathViewFactory>();
|
||||
Add<IViewEngine, RazorViewEngine>();
|
||||
}
|
||||
|
||||
public void AddAndRegisterForFinalization<T, U>() where U : T
|
||||
private void Add<T, U>() where U : T
|
||||
{
|
||||
Services.Add<T, U>();
|
||||
#if NET45
|
||||
if (typeof(IFinalizeSetup).IsAssignableFrom(typeof(U)))
|
||||
#else
|
||||
if (typeof(IFinalizeSetup).GetTypeInfo().IsAssignableFrom(typeof(U).GetTypeInfo()))
|
||||
#endif
|
||||
{
|
||||
_typesToFinalize.Add(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddInstanceAndRegisterForFinalization<T>(object instance)
|
||||
private void AddInstance<T>(object instance)
|
||||
{
|
||||
Services.AddInstance<T>(instance);
|
||||
|
||||
if ((instance as IFinalizeSetup) != null)
|
||||
{
|
||||
_typesToFinalize.Add(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
public void Finalize()
|
||||
{
|
||||
if (_typesToFinalize == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to lock around here so finalization happens just once.
|
||||
// This is not a code intended to be used during request, so the lock is just a safety precaution.
|
||||
lock (_lock)
|
||||
{
|
||||
if (_typesToFinalize == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var markerType in _typesToFinalize)
|
||||
{
|
||||
var services = this.Services.GetService(markerType);
|
||||
|
||||
var serviceToFinalize = services as IFinalizeSetup;
|
||||
|
||||
if (serviceToFinalize != null)
|
||||
{
|
||||
serviceToFinalize.FinalizeSetup();
|
||||
}
|
||||
else
|
||||
{
|
||||
var setOfServices = services as IEnumerable;
|
||||
|
||||
if (setOfServices != null)
|
||||
{
|
||||
foreach (var service in setOfServices.OfType<IFinalizeSetup>())
|
||||
{
|
||||
service.FinalizeSetup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_typesToFinalize = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class AppDomainControllerAssemblyProvider : ControllerAssemblyProvider
|
||||
{
|
||||
public IEnumerable<Assembly> Assemblies
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(AllowAssembly))
|
||||
{
|
||||
yield return assembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool AllowAssembly(Assembly assembly)
|
||||
{
|
||||
// consider mechanisms to filter assemblies upfront, so scanning cost is minimized and startup improved.
|
||||
// 1 - Does assembly reference the WebFx assembly (directly or indirectly). - Down side, object only controller not supported.
|
||||
// 2 - Remove well known assemblies (maintenance and composability cost)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public interface ControllerAssemblyProvider
|
||||
{
|
||||
IEnumerable<Assembly> Assemblies { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public abstract class ControllerCache
|
||||
{
|
||||
public abstract IEnumerable<ControllerDescriptor> GetController(string controllerName);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,24 +5,34 @@ using System.Reflection;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class DefaultControllerCache : ControllerCache, IFinalizeSetup
|
||||
public class DefaultControllerDescriptorProvider : IControllerDescriptorProvider
|
||||
{
|
||||
private readonly SkipAssemblies _skipAssemblies;
|
||||
private readonly ControllerAssemblyProvider _controllerAssemblyProvider;
|
||||
|
||||
public IReadOnlyDictionary<string, IEnumerable<ControllerDescriptor>> Controllers { get; protected set; }
|
||||
|
||||
public virtual void FinalizeSetup()
|
||||
public void FinalizeSetup()
|
||||
{
|
||||
Controllers = ScanAppDomain();
|
||||
}
|
||||
|
||||
public DefaultControllerCache(SkipAssemblies skipAssemblies)
|
||||
public DefaultControllerDescriptorProvider(ControllerAssemblyProvider controllerAssemblyProvider)
|
||||
{
|
||||
_skipAssemblies = skipAssemblies ?? new SkipNoAssemblies();
|
||||
if (controllerAssemblyProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException("controllerAssemblyProvider");
|
||||
}
|
||||
|
||||
_controllerAssemblyProvider = controllerAssemblyProvider;
|
||||
}
|
||||
|
||||
public override IEnumerable<ControllerDescriptor> GetController(string controllerName)
|
||||
public IEnumerable<ControllerDescriptor> GetControllers(string controllerName)
|
||||
{
|
||||
if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
controllerName += "Controller";
|
||||
}
|
||||
|
||||
if (Controllers == null)
|
||||
{
|
||||
throw new InvalidOperationException("Finalizing the setup must happen prior to accessing controllers");
|
||||
|
|
@ -35,14 +45,14 @@ namespace Microsoft.AspNet.Mvc
|
|||
return descriptors;
|
||||
}
|
||||
|
||||
return null;
|
||||
return Enumerable.Empty<ControllerDescriptor>();
|
||||
}
|
||||
|
||||
public Dictionary<string, IEnumerable<ControllerDescriptor>> ScanAppDomain()
|
||||
{
|
||||
var dictionary = new Dictionary<string, IEnumerable<ControllerDescriptor>>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(AllowAssembly))
|
||||
foreach (var assembly in _controllerAssemblyProvider.Assemblies)
|
||||
{
|
||||
foreach (var type in assembly.DefinedTypes.Where(IsController).Select(info => info.AsType()))
|
||||
{
|
||||
|
|
@ -70,17 +80,12 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
|
||||
bool validController = typeInfo.IsClass &&
|
||||
!typeInfo.IsAbstract &&
|
||||
!typeInfo.ContainsGenericParameters;
|
||||
!typeInfo.IsAbstract &&
|
||||
!typeInfo.ContainsGenericParameters;
|
||||
|
||||
validController = validController && typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return validController;
|
||||
}
|
||||
|
||||
private bool AllowAssembly(Assembly assembly)
|
||||
{
|
||||
return !_skipAssemblies.Skip(assembly, SkipAssemblies.ControllerDiscoveryScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,35 +9,33 @@ namespace Microsoft.AspNet.Mvc
|
|||
public class DefaultControllerFactory : IControllerFactory
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ControllerCache _controllerCache;
|
||||
private readonly IControllerDescriptorProvider _controllerDescriptorProvider;
|
||||
|
||||
public DefaultControllerFactory(IServiceProvider serviceProvider, ControllerCache cache)
|
||||
public DefaultControllerFactory(IServiceProvider serviceProvider, IControllerDescriptorProvider controllerDescriptorProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_controllerCache = cache;
|
||||
_controllerDescriptorProvider = controllerDescriptorProvider;
|
||||
}
|
||||
|
||||
public object CreateController(HttpContext context, string controllerName)
|
||||
{
|
||||
if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
controllerName += "Controller";
|
||||
}
|
||||
|
||||
var controllers = _controllerCache.GetController(controllerName);
|
||||
{
|
||||
var controllers = _controllerDescriptorProvider.GetControllers(controllerName);
|
||||
|
||||
if (controllers != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = controllers.Single().ControllerType;
|
||||
var descriptor = controllers.SingleOrDefault();
|
||||
|
||||
try
|
||||
{
|
||||
return ActivatorUtilities.CreateInstance(_serviceProvider, type);
|
||||
}
|
||||
catch (ReflectionTypeLoadException)
|
||||
if (descriptor != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ActivatorUtilities.CreateInstance(_serviceProvider, descriptor.ControllerType);
|
||||
}
|
||||
catch (ReflectionTypeLoadException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
|
|
@ -51,7 +49,6 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
public void ReleaseController(object controller)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,141 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class DefaultSkipAssemblies : SkipAssemblies
|
||||
{
|
||||
private HashSet<string> _hash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public DefaultSkipAssemblies(IEnumerable<string> assemblyNames)
|
||||
{
|
||||
InitializeHash(assemblyNames);
|
||||
}
|
||||
|
||||
public DefaultSkipAssemblies()
|
||||
{
|
||||
#if NET45
|
||||
InitializeHash(@"
|
||||
klr.net45.managed
|
||||
Microsoft.Net.Runtime.Interfaces
|
||||
klr.host
|
||||
System
|
||||
System.Core
|
||||
System.Configuration
|
||||
System.Xml
|
||||
Microsoft.Net.ApplicationHost
|
||||
Microsoft.Net.Runtime
|
||||
Newtonsoft.Json
|
||||
System.Numerics
|
||||
System.ComponentModel.DataAnnotations
|
||||
System.Runtime.Serialization
|
||||
System.Xml.Linq
|
||||
System.Data
|
||||
Microsoft.CodeAnalysis
|
||||
System.Collections.Immutable
|
||||
System.Runtime
|
||||
Microsoft.CodeAnalysis.CSharp
|
||||
System.IO.Compression
|
||||
Microsoft.AspNet.FileSystems
|
||||
Microsoft.AspNet.Abstractions
|
||||
Microsoft.AspNet.DependencyInjection
|
||||
Microsoft.AspNet.Razor
|
||||
Newtonsoft.Json
|
||||
System.Linq
|
||||
System.Collections
|
||||
System.Runtime.Extensions
|
||||
System.Threading
|
||||
System.Reflection.Metadata.Ecma335
|
||||
Microsoft.AspNet.Mvc.ModelBinding
|
||||
Microsoft.AspNet.Mvc.Rendering
|
||||
Microsoft.AspNet.Mvc
|
||||
Microsoft.AspNet.Mvc.Razor.Host
|
||||
Microsoft.AspNet.Mvc.Razor
|
||||
Microsoft.AspNet.Mvc.Startup
|
||||
Owin
|
||||
Microsoft.Owin
|
||||
Microsoft.Owin.Diagnostics
|
||||
Microsoft.Owin.Hosting
|
||||
Microsoft.Owin.Host.HttpListener
|
||||
Microsoft.AspNet.AppBuilderSupport
|
||||
Anonymously Hosted DynamicMethods Assembly
|
||||
Microsoft.AspNet.PipelineCore
|
||||
Microsoft.AspNet.FeatureModel
|
||||
mscorlib
|
||||
klr.net45.managed
|
||||
Microsoft.Net.Runtime.Interfaces
|
||||
klr.host
|
||||
System
|
||||
System.Core
|
||||
System.Configuration
|
||||
System.Xml
|
||||
Microsoft.Net.ApplicationHost
|
||||
Microsoft.Net.Runtime
|
||||
Newtonsoft.Json
|
||||
System.Numerics
|
||||
System.ComponentModel.DataAnnotations
|
||||
System.Runtime.Serialization
|
||||
System.Xml.Linq
|
||||
System.Data
|
||||
Microsoft.CodeAnalysis
|
||||
System.Collections.Immutable
|
||||
System.Runtime
|
||||
Microsoft.CodeAnalysis.CSharp
|
||||
System.IO.Compression
|
||||
Microsoft.AspNet.FileSystems
|
||||
Microsoft.AspNet.Abstractions
|
||||
Microsoft.AspNet.DependencyInjection
|
||||
Microsoft.AspNet.Razor
|
||||
Newtonsoft.Json
|
||||
System.Linq
|
||||
System.Collections
|
||||
System.Runtime.Extensions
|
||||
System.Threading
|
||||
System.Reflection.Metadata.Ecma335
|
||||
Microsoft.AspNet.Mvc.ModelBinding
|
||||
Microsoft.AspNet.Mvc.Rendering
|
||||
Microsoft.AspNet.Mvc
|
||||
Microsoft.AspNet.Mvc.Razor.Host
|
||||
Microsoft.AspNet.Mvc.Razor
|
||||
Microsoft.AspNet.Mvc.Startup
|
||||
Owin
|
||||
Microsoft.Owin
|
||||
Microsoft.Owin.Diag".Split(new char[] { '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries));
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeHash(IEnumerable<string> assemblyNames)
|
||||
{
|
||||
if (assemblyNames == null)
|
||||
{
|
||||
throw new ArgumentNullException("assemblyNames");
|
||||
}
|
||||
|
||||
foreach (var assemblyName in assemblyNames)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(assemblyName))
|
||||
{
|
||||
_hash.Add(assemblyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Skip(Assembly assembly, string scope)
|
||||
{
|
||||
if (scope == null ||
|
||||
!string.Equals(scope, SkipAssemblies.ControllerDiscoveryScope, StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string name = assembly.GetName().Name;
|
||||
|
||||
bool contains = _hash.Contains(name);
|
||||
|
||||
return contains;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public interface IControllerDescriptorProvider
|
||||
{
|
||||
IEnumerable<ControllerDescriptor> GetControllers(string controllerName);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public interface IFinalizeSetup
|
||||
{
|
||||
void FinalizeSetup();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public abstract class SkipAssemblies
|
||||
{
|
||||
public static readonly string ControllerDiscoveryScope = "DCS";
|
||||
|
||||
public abstract bool Skip(Assembly assembly, string scope);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class SkipNoAssemblies : SkipAssemblies
|
||||
{
|
||||
public override bool Skip(Assembly assembly, string scope)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue