Merge pull request #3621 from umbraco/temp8-controllers

Refactor/fix registering controllers
This commit is contained in:
Warren Buckley
2018-11-27 10:18:28 +00:00
committed by GitHub
4 changed files with 122 additions and 79 deletions

View File

@@ -210,53 +210,55 @@ namespace Umbraco.Core.Composing
/// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match
/// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll"
/// </remarks>
internal static readonly string[] KnownAssemblyExclusionFilter = new[]
{
"mscorlib,",
"System.",
"Antlr3.",
"Autofac.",
"Autofac,",
"Castle.",
"ClientDependency.",
"DataAnnotationsExtensions.",
"DataAnnotationsExtensions,",
"Dynamic,",
"HtmlDiff,",
"Iesi.Collections,",
"Microsoft.",
"Newtonsoft.",
"NHibernate.",
"NHibernate,",
"NuGet.",
"RouteDebugger,",
"SqlCE4Umbraco,",
"umbraco.datalayer,",
"umbraco.interfaces,",
//"umbraco.providers,",
//"Umbraco.Web.UI,",
"umbraco.webservices",
"Lucene.",
"Examine,",
"Examine.",
"ServiceStack.",
"MySql.",
"HtmlAgilityPack.",
"TidyNet.",
"ICSharpCode.",
"CookComputing.",
"AutoMapper,",
"AutoMapper.",
"AzureDirectory,",
"itextsharp,",
"UrlRewritingNet.",
"HtmlAgilityPack,",
"MiniProfiler,",
"Moq,",
"nunit.framework,",
"TidyNet,",
"WebDriver,"
};
internal static readonly string[] KnownAssemblyExclusionFilter = {
"Antlr3.",
"AutoMapper,",
"AutoMapper.",
"Autofac,", // DI
"Autofac.",
"AzureDirectory,",
"Castle.", // DI, tests
"ClientDependency.",
"CookComputing.",
"CSharpTest.", // BTree for NuCache
"DataAnnotationsExtensions,",
"DataAnnotationsExtensions.",
"Dynamic,",
"Examine,",
"Examine.",
"HtmlAgilityPack,",
"HtmlAgilityPack.",
"HtmlDiff,",
"ICSharpCode.",
"Iesi.Collections,", // used by NHibernate
"LightInject.", // DI
"LightInject,",
"Lucene.",
"Markdown,",
"Microsoft.",
"MiniProfiler,",
"Moq,",
"MySql.",
"NHibernate,",
"NHibernate.",
"Newtonsoft.",
"NPoco,",
"NuGet.",
"RouteDebugger,",
"Semver.",
"Serilog.",
"Serilog,",
"ServiceStack.",
"SqlCE4Umbraco,",
"Superpower,", // used by Serilog
"System.",
"TidyNet,",
"TidyNet.",
"WebDriver,",
"itextsharp,",
"mscorlib,",
"nunit.framework,",
};
/// <summary>
/// Finds any classes derived from the type T that contain the attribute TAttribute

View File

@@ -520,6 +520,8 @@ namespace Umbraco.Core.Composing
// if not caching, or not IDiscoverable, directly get types
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
{
_logger.Logger.Debug<TypeLoader>("Running a full, non-cached, scan for type {TypeName} (slow).", typeof(T).FullName);
return GetTypesInternal(
typeof (T), null,
() => TypeFinder.FindClassesOfType<T>(specificAssemblies ?? AssembliesToScan),
@@ -559,6 +561,8 @@ namespace Umbraco.Core.Composing
// if not caching, or not IDiscoverable, directly get types
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
{
_logger.Logger.Debug<TypeLoader>("Running a full, non-cached, scan for type {TypeName} / attribute {AttributeName} (slow).", typeof(T).FullName, typeof(TAttribute).FullName);
return GetTypesInternal(
typeof (T), typeof (TAttribute),
() => TypeFinder.FindClassesOfTypeWithAttribute<T, TAttribute>(specificAssemblies ?? AssembliesToScan),
@@ -595,6 +599,11 @@ namespace Umbraco.Core.Composing
// do not cache anything from specific assemblies
cache &= specificAssemblies == null;
if (cache == false)
{
_logger.Logger.Debug<TypeLoader>("Running a full, non-cached, scan for types / attribute {AttributeName} (slow).", typeof(TAttribute).FullName);
}
return GetTypesInternal(
typeof (object), typeof (TAttribute),
() => TypeFinder.FindClassesWithAttribute<TAttribute>(specificAssemblies ?? AssembliesToScan),

View File

@@ -1,49 +1,82 @@
using System.Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Mvc;
using LightInject;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
namespace Umbraco.Web
{
internal static class LightInjectExtensions
{
/// <summary>
/// Registers all IControllers using the TypeLoader for scanning and caching found instances for the calling assembly
/// Registers Umbraco controllers.
/// </summary>
/// <param name="container"></param>
/// <param name="typeLoader"></param>
/// <param name="assembly"></param>
public static void RegisterMvcControllers(this IServiceRegistry container, TypeLoader typeLoader, Assembly assembly)
public static void RegisterUmbracoControllers(this IServiceRegistry container, TypeLoader typeLoader, Assembly umbracoWebAssembly)
{
//TODO: We've already scanned for UmbracoApiControllers and SurfaceControllers - should we scan again
// for all controllers? Seems like we should just do this once and then filter. That said here we are
// only scanning our own single assembly. Hrm.
// notes
//
// We scan and auto-registers:
// - every IController and IHttpController that *we* have in Umbraco.Web
// - PluginController and UmbracoApiController in every assembly
//
// We do NOT scan:
// - any IController or IHttpController (anything not PluginController nor UmbracoApiController), outside of Umbraco.Web
// which means that users HAVE to explicitly register their own non-Umbraco controllers
//
// This is because we try to achieve a balance between "simple" and "fast. Scanning for PluginController or
// UmbracoApiController is fast-ish because they both are IDiscoverable. Scanning for IController or IHttpController
// is a full, non-cached scan = expensive, we do it only for 1 assembly.
//
// TODO
// find a way to scan for IController *and* IHttpController in one single pass
// or, actually register them manually so don't require a full scan for these
// 5 are IController but not PluginController
// Umbraco.Web.Mvc.RenderMvcController
// Umbraco.Web.Install.Controllers.InstallController
// Umbraco.Web.Macros.PartialViewMacroController
// Umbraco.Web.Editors.PreviewController
// Umbraco.Web.Editors.BackOfficeController
// 9 are IHttpController but not UmbracoApiController
// Umbraco.Web.Controllers.UmbProfileController
// Umbraco.Web.Controllers.UmbLoginStatusController
// Umbraco.Web.Controllers.UmbRegisterController
// Umbraco.Web.Controllers.UmbLoginController
// Umbraco.Web.Mvc.RenderMvcController
// Umbraco.Web.Install.Controllers.InstallController
// Umbraco.Web.Macros.PartialViewMacroController
// Umbraco.Web.Editors.PreviewController
// Umbraco.Web.Editors.BackOfficeController
container.RegisterControllers<IController>(typeLoader, assembly);
// scan and register every IController in Umbraco.Web
var umbracoWebControllers = typeLoader.GetTypes<IController>(specificAssemblies: new[] { umbracoWebAssembly });
//foreach (var controller in umbracoWebControllers.Where(x => !typeof(PluginController).IsAssignableFrom(x)))
// Current.Logger.Debug(typeof(LightInjectExtensions), "IController NOT PluginController: " + controller.FullName);
container.RegisterControllers(umbracoWebControllers);
// scan and register every PluginController in everything (PluginController is IDiscoverable and IController)
var nonUmbracoWebPluginController = typeLoader.GetTypes<PluginController>().Where(x => x.Assembly != umbracoWebAssembly);
container.RegisterControllers(nonUmbracoWebPluginController);
// scan and register every IHttpController in Umbraco.Web
var umbracoWebHttpControllers = typeLoader.GetTypes<IHttpController>(specificAssemblies: new[] { umbracoWebAssembly });
//foreach (var controller in umbracoWebControllers.Where(x => !typeof(UmbracoApiController).IsAssignableFrom(x)))
// Current.Logger.Debug(typeof(LightInjectExtensions), "IHttpController NOT UmbracoApiController: " + controller.FullName);
container.RegisterControllers(umbracoWebHttpControllers);
// scan and register every UmbracoApiController in everything (UmbracoApiController is IDiscoverable and IHttpController)
var nonUmbracoWebApiControllers = typeLoader.GetTypes<UmbracoApiController>().Where(x => x.Assembly != umbracoWebAssembly);
container.RegisterControllers(nonUmbracoWebApiControllers);
}
/// <summary>
/// Registers all IHttpController using the TypeLoader for scanning and caching found instances for the calling assembly
/// </summary>
/// <param name="container"></param>
/// <param name="typeLoader"></param>
/// <param name="assembly"></param>
public static void RegisterApiControllers(this IServiceRegistry container, TypeLoader typeLoader, Assembly assembly)
private static void RegisterControllers(this IServiceRegistry container, IEnumerable<Type> controllerTypes)
{
//TODO: We've already scanned for UmbracoApiControllers and SurfaceControllers - should we scan again
// for all controllers? Seems like we should just do this once and then filter. That said here we are
// only scanning our own single assembly. Hrm.
container.RegisterControllers<IHttpController>(typeLoader, assembly);
}
private static void RegisterControllers<TController>(this IServiceRegistry container, TypeLoader typeLoader, Assembly assembly)
{
var types = typeLoader.GetTypes<TController>(specificAssemblies: new[] { assembly });
foreach (var type in types)
container.Register(type, new PerRequestLifeTime());
foreach (var controllerType in controllerTypes)
container.Register(controllerType, new PerRequestLifeTime());
}
}
}

View File

@@ -122,9 +122,8 @@ namespace Umbraco.Web.Runtime
composition.Container.EnableMvc(); // does container.EnablePerWebRequestScope()
composition.Container.ScopeManagerProvider = smp; // reverts - we will do it last (in WebRuntime)
composition.Container.RegisterMvcControllers(typeLoader, GetType().Assembly);
composition.Container.RegisterUmbracoControllers(typeLoader, GetType().Assembly);
composition.Container.EnableWebApi(GlobalConfiguration.Configuration);
composition.Container.RegisterApiControllers(typeLoader, GetType().Assembly);
composition.Container.RegisterCollectionBuilder<SearchableTreeCollectionBuilder>()
.Add(() => typeLoader.GetTypes<ISearchableTree>()); // fixme which searchable trees?!