前一篇介紹Asp.net MVC
可透過DependencyResolver.SetResolver
替換成IOC
容器注入控制器物件.
要建立客製化的解析器可以實現IDependencyResolver
介面並使用DependencyResolver.SetResolver
替換DefaultDependencyResolver
預設解析器
DependencyResolver
,Controller
和ControllerFactory
的關係如下圖
本篇介紹DependencyResolver
在Asp.net MVC
中有哪些實際的應用.
我有做一個可以針對於Asp.net MVC Debugger的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
在DefaultControllerFactory
建構子建立DefaultControllerActivator
,而DefaultControllerActivator
有一個Create
方法使用他來建立Controller
物件.
internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
{
if (controllerActivator != null)
{
_controllerActivator = controllerActivator;
}
else
{
_activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(
() => null,
new DefaultControllerActivator(dependencyResolver),
"DefaultControllerFactory constructor");
}
}
如果我們沒透過DependencyResolver.SetResolver
方法設定其他解析器,預設使用DefaultControllerActivator
類別幫助我們建立Controller
物件透過Create
方法.
private class DefaultControllerActivator : IControllerActivator
{
private Func<IDependencyResolver> _resolverThunk;
public DefaultControllerActivator()
: this(null)
{
}
public DefaultControllerActivator(IDependencyResolver resolver)
{
if (resolver == null)
{
_resolverThunk = () => DependencyResolver.Current;
}
else
{
_resolverThunk = () => resolver;
}
}
public IController Create(RequestContext requestContext, Type controllerType)
{
try
{
return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
}
catch (Exception ex)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
}
因為DependencyResolver.Current
建構子傳入參數IDependencyResolver resolver
一般是NULL
,所以會使用DependencyResolver.Current
解析器.
Create
方法預設利用DefaultDependencyResolver.GetService
創建物件(使用Activator.CreateInstance()
)
BuildManagerViewEngine
類別的詳細介紹會在之後的View
如何產生有更細節的資訊.
這邊是提一下哪邊有用到IDependencyResolver
解析器.
internal class DefaultViewPageActivator : IViewPageActivator
{
private Func<IDependencyResolver> _resolverThunk;
public DefaultViewPageActivator()
: this(null)
{
}
public DefaultViewPageActivator(IDependencyResolver resolver)
{
if (resolver == null)
{
_resolverThunk = () => DependencyResolver.Current;
}
else
{
_resolverThunk = () => resolver;
}
}
public object Create(ControllerContext controllerContext, Type type)
{
try
{
return _resolverThunk().GetService(type) ?? Activator.CreateInstance(type);
}
catch (MissingMethodException exception)
{
// Ensure thrown exception contains the type name. Might be down a few levels.
MissingMethodException replacementException =
TypeHelpers.EnsureDebuggableException(exception, type.FullName);
if (replacementException != null)
{
throw replacementException;
}
throw;
}
}
}
一樣可以看到有一個Create
方法.透過跟DefaultControllerActivator
一樣的操作來使用IDependencyResolver
解析器
預設使用DependencyResolver.Current
可以透過IOC
容器注入客製化ProvideFilter
使用行為.
預設ProvideFilter
有三個(詳細資訊會在之後分享)
GlobalFilterCollection
(在Global擴充)ControllerInstanceFilterProvider
(Controller
自行Override
)FilterAttributeFilterProvider
(提供Attribute
註冊最常用)MVC Filters With Dependency Injection文章有介紹如何使用
internal IFilterProvider[] CombinedItems
{
get
{
IFilterProvider[] combinedItems = _combinedItems;
if (combinedItems == null)
{
combinedItems = MultiServiceResolver.GetCombined<IFilterProvider>(Items, _dependencyResolver);
_combinedItems = combinedItems;
}
return combinedItems;
}
}
上面有說如果要改變MVC使用解析器可以透過DependencyResolver.SetResolver
方法傳入一個IDependencyResolver
物件,Autofac對於使的是AutofacDependencyResolver 原始碼.
替換完成後MVC就會使用AutofacDependencyResolver.GetService
取得物件.
這裡就不多敘述Autofac內部完成細節.
public class AutofacDependencyResolver : IDependencyResolver
{
private static Func<AutofacDependencyResolver> _resolverAccessor = DefaultResolverAccessor;
private readonly Action<ContainerBuilder> _configurationAction;
private readonly ILifetimeScope _container;
private ILifetimeScopeProvider _lifetimeScopeProvider;
public AutofacDependencyResolver(ILifetimeScope container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
this._container = container;
}
public AutofacDependencyResolver(ILifetimeScope container, Action<ContainerBuilder> configurationAction)
: this(container)
{
if (configurationAction == null)
{
throw new ArgumentNullException(nameof(configurationAction));
}
this._configurationAction = configurationAction;
}
public AutofacDependencyResolver(ILifetimeScope container, ILifetimeScopeProvider lifetimeScopeProvider) :
this(container)
{
if (lifetimeScopeProvider == null)
{
throw new ArgumentNullException(nameof(lifetimeScopeProvider));
}
this._lifetimeScopeProvider = lifetimeScopeProvider;
}
public AutofacDependencyResolver(ILifetimeScope container, ILifetimeScopeProvider lifetimeScopeProvider, Action<ContainerBuilder> configurationAction)
: this(container, lifetimeScopeProvider)
{
if (configurationAction == null)
{
throw new ArgumentNullException(nameof(configurationAction));
}
this._configurationAction = configurationAction;
}
/// <summary>
/// Gets the Autofac implementation of the dependency resolver.
/// </summary>
public static AutofacDependencyResolver Current
{
get
{
return _resolverAccessor();
}
}
public ILifetimeScope ApplicationContainer
{
get { return this._container; }
}
public ILifetimeScope RequestLifetimeScope
{
get
{
if (this._lifetimeScopeProvider == null)
{
this._lifetimeScopeProvider = new RequestLifetimeScopeProvider(this._container);
}
return this._lifetimeScopeProvider.GetLifetimeScope(this._configurationAction);
}
}
public static void SetAutofacDependencyResolverAccessor(Func<AutofacDependencyResolver> accessor)
{
if (accessor == null)
{
_resolverAccessor = DefaultResolverAccessor;
}
else
{
_resolverAccessor = accessor;
}
}
public virtual object GetService(Type serviceType)
{
return this.RequestLifetimeScope.ResolveOptional(serviceType);
}
public virtual IEnumerable<object> GetServices(Type serviceType)
{
var enumerableServiceType = typeof(IEnumerable<>).MakeGenericType(serviceType);
var instance = this.RequestLifetimeScope.Resolve(enumerableServiceType);
return (IEnumerable<object>)instance;
}
private static AutofacDependencyResolver DefaultResolverAccessor()
{
var currentResolver = DependencyResolver.Current;
var autofacResolver = currentResolver as AutofacDependencyResolver;
if (autofacResolver != null)
{
return autofacResolver;
}
var targetType = currentResolver.GetType().GetField("__target");
if (targetType != null && targetType.FieldType == typeof(AutofacDependencyResolver))
{
return (AutofacDependencyResolver)targetType.GetValue(currentResolver);
}
throw new InvalidOperationException(string.Format(
CultureInfo.CurrentCulture,
AutofacDependencyResolverResources.AutofacDependencyResolverNotFound,
currentResolver.GetType().FullName, typeof(AutofacDependencyResolver).FullName));
}
}
本篇挑了幾個有使用到DependencyResolver
的使用點
在DefaultControllerActivator
建立Controller會利用當前使用的解析器來幫我們達成(預設DefaultDependencyResolver
)
如果我們不想要使用預設解析器也可自行替換自己的解析器(像Autofac第三方容器)來控制我們自己如何產生物件.
能看到Asp.net MVC
在設計上運用許多小巧思可讓系統可以更好的擴充且不用到到原本的程式碼
這些設計技巧很值得我們還學習效法.