概述
默认情况下,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