概述
默認狀況下,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的實例如何被解析出來。
1、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);
}
2、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);
}
}
3、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