概述
默认情况下,ASP.NET MVC内置的DefaultControllerFactory负责Controller实例的创建。Orchard定义了一个继承自DefaultControllerFactory类的Orchard.Mvc.OrchardControllerFactory类并在OrchardStarter类中进行注册:
// 以下代码来在Orchard.Environment.OrchardStarter类
ControllerBuilder.Current.SetControllerFactory(new OrchardControllerFactory());
OrchardControllerFactory作用是能从Shell生命周期的Autofac"子容器"中解析(Resolver)Controller类型,再根据类型解析Controller实例。这里就引出这些问题:Controller类型在什么时候被注册到Autofac容器中,Controller的类型如何被解析出来,Controller的实例如何被解析出来。
一、Controller注册
在激活Shell之前,会创建Shell的上下文对象ShellContext,在这个过程中会将Shell需要用到的Controller注册到Shell作用域的Autofac容器中:
// 以下代码来在Orchard.Environment.ShellBuilders.ShellContainerFactory类的CreateContainer方法
foreach (var item in blueprint.Controllers) {
var serviceKeyName = (item.AreaName + "/" + item.ControllerName).ToLowerInvariant();
var serviceKeyType = item.Type;
RegisterType(builder, item)
.EnableDynamicProxy(dynamicProxyContext)
.Keyed< IController>(serviceKeyName)
.Keyed< IController>(serviceKeyType)
.WithMetadata( "ControllerType", item.Type)
.InstancePerDependency()
.OnActivating(e =>s {
// necessary to inject custom filters dynamically
// see FilterResolvingActionInvoker
var controller = e.Instance as Controller;
if (controller != null )
controller.ActionInvoker = ( IActionInvoker)e.Context.ResolveService(new TypedService(typeof (IActionInvoker)));
});
}
foreach (var item in blueprint.HttpControllers) {
var serviceKeyName = (item.AreaName + "/" + item.ControllerName).ToLowerInvariant();
var serviceKeyType = item.Type;
RegisterType(builder, item)
.EnableDynamicProxy(dynamicProxyContext)
.Keyed< IHttpController>(serviceKeyName)
.Keyed< IHttpController>(serviceKeyType)
.WithMetadata( "ControllerType", item.Type)
.InstancePerDependency();
}
// 以下代码来在Orchard.Environment.ShellBuilders.ShellContainerFactory类
private IRegistrationBuilder<object , ConcreteReflectionActivatorData, SingleRegistrationStyle> RegisterType(ContainerBuilder builder, ShellBlueprintItem item) {
return builder.RegisterType(item.Type)
.WithProperty( "Feature", item.Feature)
.WithMetadata( "Feature", item.Feature);
}
二、ControllerFactory
既然Controller已经被注册到了Autofac容器中,则很容易提取出来:
// 以下代码来在Orchard.Mvc.ControllerFactory类
/// <summary>
/// Overrides the default controller factory to resolve controllers using LoC, based their areas and names.
/// </summary>
public class OrchardControllerFactory : DefaultControllerFactory {
/// <summary>
/// Tries to resolve an instance for the controller associated with a given service key for the work context scope.
/// </summary>
/// <typeparam name="T"> The type of the controller.</typeparam>
/// <param name="workContext"> The work context.</param>
/// <param name="serviceKey"> The service key for the controller. </param>
/// <param name="instance"> The controller instance.</param>
/// <returns> True if the controller was resolved; false otherwise. </returns>
protected bool TryResolve<T>(WorkContext workContext, object serviceKey, out T instance) {
if (workContext != null && serviceKey != null) {
var key = new KeyedService(serviceKey, typeof (T));
object value;
if (workContext.Resolve<ILifetimeScope >().TryResolveService(key, out value)) {
instance = (T) value;
return true ;
}
}
instance = default(T);
return false ;
}
/// <summary>
/// Returns the controller type based on the name of both the controller and area.
/// </summary>
/// <param name="requestContext"> The request context from where to fetch the route data containing the area.</param>
/// <param name="controllerName"> The controller name.</param>
/// <returns> The controller type.</returns>
/// <example> ControllerName: Item, Area: Containers would return the type for the ItemController class.</example>
protected override Type GetControllerType( RequestContext requestContext, string controllerName) {
var routeData = requestContext.RouteData;
// Determine the area name for the request, and fall back to stock orchard controllers
var areaName = routeData.GetAreaName();
// Service name pattern matches the identification strategy
var serviceKey = (areaName + "/" + controllerName).ToLowerInvariant();
// Now that the request container is known - try to resolve the controller information
Meta<Lazy <IController>> info;
var workContext = requestContext.GetWorkContext();
if (TryResolve(workContext, serviceKey, out info)) {
return (Type ) info.Metadata["ControllerType"];
}
return null ;
}
/// <summary>
/// Returns an instance of the controller.
/// </summary>
/// <param name="requestContext"> The request context from where to fetch the route data containing the area.</param>
/// <param name="controllerType"> The controller type.</param>
/// <returns> An instance of the controller if it's type is registered; null if otherwise. </returns>
protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType) {
IController controller;
var workContext = requestContext.GetWorkContext();
if (TryResolve(workContext, controllerType, out controller)) {
return controller;
}
// fail as appropriate for MVC's expectations
return base .GetControllerInstance(requestContext, controllerType);
}
}
三、ActionInvoker
在成功创建Controller实例后,会将其ActionInvoker设置成Orchard.Mvc.FilterResolvingActionInvoker。
我们知道,ASP.NET MVC中,我们可以将Filter以Attribute的形式定义在Controller或Action上。Orchard提供了一个Filter Provider机制,即可以将一些Filter定义在其他地方,在使用Filter之前再提取出来。这方面比较简单,直接看FilterResolvingActionInvoker的定义:
// 以下代码来在Orchard.Mvc.Filters.FilterResolvingActionInvoker类
public class FilterResolvingActionInvoker : ControllerActionInvoker {
private readonly IEnumerable< IFilterProvider> _filterProviders;
public FilterResolvingActionInvoker(IEnumerable <IFilterProvider> filterProviders) {
_filterProviders = filterProviders;
}
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
var filters= base .GetFilters(controllerContext, actionDescriptor);
foreach(var provider in _filterProviders) {
provider.AddFilters(filters);
}
return filters;
}
}
只要一个Filter实现了Orchard.Mvc.Filters.FilterProvider:FilterProvider抽象类,就能非常方便的将Filter应用到所有Action之上。另外,高级的应用可以直接实现IFilterProvider接口,不过一般实现FilterProvider抽象类就行了。
相关类型:
Orchard.Mvc.OrchardControllerFactory:System.Web.Mvc.DefaultActionInvoker
Orchard.Mvc.Filters.FilterResolvingActionInvoker:System.Web.Mvc.ControllerActionInvoker
Orchard.Mvc.Filters.IFilterProvider
Orchard.Environment.ShellBuilders.ShellContainerFactory
Orchard.Environment.AutofacUtil.DynamicProxy2.DynamicProxyContext
- Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)
概述 Host 是应用程序域级的单例,代表了Orchard应用程序.其处理应用程序生命周期中的初始化.BeginRequest事件.EndRequest事件等. 可以简单理解为HttpApplicat ...
- 消息队列的一些场景及源码分析,RocketMQ使用相关问题及性能优化
前文目录链接参考: 消息队列的一些场景及源码分析,RocketMQ使用相关问题及性能优化 https://www.cnblogs.com/yizhiamumu/p/16694126.html 消息队列 ...
- 《k8s 源码分析》- Custom Controller 之 Informer
Custom Controller 之 Informer 概述 架构概览 reflector - List & Watch API Server Reflector 对象 ListAndWat ...
- Orchard源码分析(1):Orchard架构
本文主要参考官方文档"How Orchard works"以及Orchardch上的翻译. 源码分析应该做到庖丁解牛,而不是以管窥豹或瞎子摸象.所以先对Orchard架构有 ...
- Orchard源码分析(7.1):Routing(路由)相关
概述 关于ASP.NET MVC中路由有两个基本核心作用,一是通过Http请求中的Url参数等信息获取路由数据(RouteData),路由数据包含了area.controller.action的名称等 ...
- Orchard源码分析(6):Shell相关
概述在Orchard中,提出子站点(Tenant)的概念,目的是为了增加站点密度,即一个应用程序域可以有多个子站点. Shell是子站点(Tenant)级的单例,换句话说Shell代表了子站点.对比来 ...
- Orchard源码分析(7):ASP.NET MVC相关
概述 Orchard归根结底是一个ASP.NET MVC(以后都简称为MVC)应用,但在前面的分析中,与MVC相关内容的涉及得很少.MVC提供了非常多的扩展点,本文主要关注Orchard所做的扩展.主 ...
- Orchard源码分析(4.4):Orchard.Caching.CacheModule类
概述 CacheModule也是一个Autofac模块. 一.CacheModule类 CacheModule将DefaultCacheManager注册为ICacheManager: ...
- Orchard源码分析(5.1):Host初始化(DefaultOrchardHost.Initialize方法)
概述 Orchard作为一个可扩展的CMS系统,是由一系列的模块(Modules)或主题(Themes)组成,这些模块或主题统称为扩展(Extensions).在初始化或运行时需要对扩展进行安装:De ...
- Orchard源码分析(4.3):Orchard.Events.EventsModule类(Event Bus)
概述 采用Event Bus模式(事件总线),可以使观察者模式中的观察者和被观察者实现解耦. 在.Net 中使用观察者模式,可以使用事件(委托)和接口(类).Orchard Event Bus使用的 ...
随机推荐
- win7 系统无法复制粘贴解决方案
用远程桌面登陆服务器不能在本机和远程服务器之间粘贴文本了,即不能从本机复制文本粘贴到服务器,也不能从服务器复制文本粘贴到本机. 以下是解决方法之一,试了几次都很管用户:在服务器上打开任务管理器,查看进 ...
- jQuery调用WebService实现增删改查的实现
第一篇博客,发下我自己写的jQuery调用WebService实现增删改查的实现. 1 <!DOCTYPE html> 2 3 <html xmlns="http://ww ...
- Java产生随机数
前言: 每一门程序设计语言基本都具有一个随机函数,而Java当中产生随机数的方式不拘一格.而且其中的Random工具类还有着更深入的应用,但本文仅对比3种产生随机数的方式,就不深入扩展分析其内部工具类 ...
- 【matlab】设定C++编译器
在用matlab2016a,运行的代码涉及到C++混编,要用mex把C++代码编译出来. 记得几年前的matlab版本,通过: mex -setup 就可以设定了,选择要使用的编译器(文本的超链接), ...
- HTTP Status 500 - Servlet.init() for servlet springmvc threw exception
自己的当前环境是ubuntu14.04 64位,tomtcat7和tomcat8,eclipse luna,然后跑了一直spring的demo项目爆了上述的错误,由于这个项目用的是spring3.2在 ...
- wpf配置菜单栏
WPF 内建了两种菜单——Menu 和ContextMenu(上下文菜单). 1. Menu Menu 的项可以是任何东西,但是你应该使用MenuItem 以及Separator 对象. <Me ...
- hdu5047 找规律+欧拉公式
题意:在一个正方形内画n个M,求最多能分成多少个平面 sol:这种求划分成多少个平面的题第一反应肯定是欧拉公式: 二维平面上的欧拉公式:V+F-E=1 (V:Vertices,F:Faces,E:Ed ...
- 使用 BASH 作为 CGI 进行 HTTP 文件上传
憋半天憋出这么点代码来,暂时凑合可以用...需要手动删除文件末尾的分隔符,还有一个windows 换行: #!/bin/bash newline="" while true; do ...
- nand以及yaffs2
用的是tiny210+1312的SDK,友善之臂给的文件系统居然有300多兆,烧写系统可是要人命了,那个等啊...所以,想着把系统给做小一点.和其他一样,我的知识仅限于理论阶段,嘿嘿.今天实践一下. ...
- 帮助理解委托、匿名委托、Lambda表达式还有事件
写了一个小程序,能够很好的认识到这三个的用法 namespace Lambda { /// <summary> /// 实现根据指定运算形式 输出结果 /// </summary&g ...