あなたをオープンソースに連れて行きます-ASP.NET_MVC(八)
前編ではG e t C o r l e TypeWithinNamespacesメソッドについて述べたが,その定義に入るとControllerTypeCacheが呼び出されたことを追跡した.GetControllerTypeメソッドは、ControllerType Cacheという名前から、あるキャッシュからコントローラタイプを検索していると推測できます.これを追跡して、コードセグメント1に入ります.
コードセグメント1
コードセグメント1では、文[_cache.TryGetValue(controllerName,out namespaceLookup)]を参照してください.Cacheは辞書オブジェクトであり、その操作についてはコードセグメント2を参照してください.
コードセグメント2
コードセグメント2でGetFilteredTypesFromAssembliesメソッドを見つけ、その定義(コードセグメント3)に追跡します.中には、「//first,try reading from the cache on disk」、「//if reading from the cache failed,enumerate over every assemblylooking for a matching type」、「//finally,save thecache back to disk」の3つの目立つ注釈があります.マイクロソフトのオープンソースプロジェクトチームは「三」という数字が好きそうです.ほほほ!まず、ディスクキャッシュを読み込んでみます.キャッシュの読み取りに失敗した場合、すべてのプログラムセットを列挙して、一致するタイプを探します.最後に、キャッシュをディスクに保存します.これにより、MVCフレームワークがキャッシュからコントローラタイプを取得したという推測が検証される.
コードセグメント3
では、コントローラのタイプを入手しました.DefaultControllerFactoryのCreateControllerメソッドに戻り、「IController controller=GetControllerInstance(requestContext,controller Type);」という文を見つけます.この文の役割はIControllerインタフェースをインスタンス化することです.GetControllerInstanceの定義をトレースします.コードセグメント4を参照してください.
コードセグメント4
コードセグメント4の最後の文【return ControllerActivatvator.Create(requestContext,controllerType);】この文がIControllerインスタンスを作成しました.
OK、コントローラのインスタンス化が完了しました.
次に、ControllerまたはActionを実行する前にフィルタ処理を行う「フィルタ」、すなわちFilterについて説明します.その原理を見てみましょう.コードセグメント5を参照してください.これはControllerActionInvokerクラスの1つの方法であり、もちろん、ControllerActionInvokerはIActionInvokerインタフェースを実現している.
コードセグメント5
コードセグメント5からは、まずGetFilters法によってすべてのFilterが得られる.次に、AuthenticationFiltersが存在するかどうかをチェックし、存在する場合は実行し、そうでない場合はAuthorizationFiltersが存在するかどうかをチェックし、存在する場合は実行し、そうでない場合はActionおよびActionResultを実行します.つまり、コントローラは、Actionを実行する前に、まずAuthenticationとAuthorizationの2つのフィルタをチェックし、存在する場合に実行します.
続きを待つ....
public ICollection<Type> GetControllerTypes(string controllerName,HashSet<string> namespaces)
{
HashSet<Type> matchingTypes = new HashSet<Type>();
ILookup<string, Type> namespaceLookup;
if (_cache.TryGetValue(controllerName, out namespaceLookup))
{
// this friendly name was located in the cache, now cycle throughnamespaces
if (namespaces != null)
{
foreach (stringrequestedNamespace in namespaces)
{
foreach (vartargetNamespaceGrouping in namespaceLookup)
{
if(IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key))
{
matchingTypes.UnionWith(targetNamespaceGrouping);
}
}
}
}
else
{
// if the namespacesparameter is null, search *every* namespace
foreach (var namespaceGroup innamespaceLookup)
{
matchingTypes.UnionWith(namespaceGroup);
}
}
}
return matchingTypes;
}
コードセグメント1
コードセグメント1では、文[_cache.TryGetValue(controllerName,out namespaceLookup)]を参照してください.Cacheは辞書オブジェクトであり、その操作についてはコードセグメント2を参照してください.
public void EnsureInitialized(IBuildManager buildManager)
{
if (_cache == null)
{
lock (_lockObj)
{
if (_cache == null)
{
List<Type>controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(TypeCacheName,IsControllerType, buildManager);
var groupedByName =controllerTypes.GroupBy(
t =>t.Name.Substring(0, t.Name.Length - "Controller".Length),
StringComparer.OrdinalIgnoreCase);
_cache =groupedByName.ToDictionary(
g => g.Key,
g =>g.ToLookup(t => t.Namespace ?? String.Empty,StringComparer.OrdinalIgnoreCase),
StringComparer.OrdinalIgnoreCase);
}
}
}
}
コードセグメント2
コードセグメント2でGetFilteredTypesFromAssembliesメソッドを見つけ、その定義(コードセグメント3)に追跡します.中には、「//first,try reading from the cache on disk」、「//if reading from the cache failed,enumerate over every assemblylooking for a matching type」、「//finally,save thecache back to disk」の3つの目立つ注釈があります.マイクロソフトのオープンソースプロジェクトチームは「三」という数字が好きそうです.ほほほ!まず、ディスクキャッシュを読み込んでみます.キャッシュの読み取りに失敗した場合、すべてのプログラムセットを列挙して、一致するタイプを探します.最後に、キャッシュをディスクに保存します.これにより、MVCフレームワークがキャッシュからコントローラタイプを取得したという推測が検証される.
public static List<Type> GetFilteredTypesFromAssemblies(stringcacheName, Predicate<Type> predicate, IBuildManager buildManager)
{
TypeCacheSerializer serializer = new TypeCacheSerializer();
// first, try reading from the cache on disk
List<Type> matchingTypes = ReadTypesFromCache(cacheName,predicate, buildManager, serializer);
if (matchingTypes != null)
{
return matchingTypes;
}
// if reading from the cache failed, enumerate over every assemblylooking for a matching type
matchingTypes = FilterTypesInAssemblies(buildManager,predicate).ToList();
// finally, save the cache back to disk
SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer);
return matchingTypes;
}
コードセグメント3
では、コントローラのタイプを入手しました.DefaultControllerFactoryのCreateControllerメソッドに戻り、「IController controller=GetControllerInstance(requestContext,controller Type);」という文を見つけます.この文の役割はIControllerインタフェースをインスタンス化することです.GetControllerInstanceの定義をトレースします.コードセグメント4を参照してください.
protected internal virtual IControllerGetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404,
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_NoControllerFound,
requestContext.HttpContext.Request.Path));
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
controllerType),
"controllerType");
}
return ControllerActivator.Create(requestContext, controllerType);
}
コードセグメント4
コードセグメント4の最後の文【return ControllerActivatvator.Create(requestContext,controllerType);】この文がIControllerインスタンスを作成しました.
OK、コントローラのインスタンス化が完了しました.
次に、ControllerまたはActionを実行する前にフィルタ処理を行う「フィルタ」、すなわちFilterについて説明します.その原理を見てみましょう.コードセグメント5を参照してください.これはControllerActionInvokerクラスの1つの方法であり、もちろん、ControllerActionInvokerはIActionInvokerインタフェースを実現している.
public virtual boolInvokeAction(ControllerContext controllerContext, string actionName)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
Contract.Assert(controllerContext.RouteData != null);
if (String.IsNullOrEmpty(actionName) &&!controllerContext.RouteData.HasDirectRouteMatch())
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty,"actionName");
}
ControllerDescriptor controllerDescriptor =GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext,controllerDescriptor, actionName);
if (actionDescriptor != null)
{
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
try
{
AuthenticationContextauthenticationContext = InvokeAuthenticationFilters(controllerContext,filterInfo.AuthenticationFilters, actionDescriptor);
if(authenticationContext.Result != null)
{
// An authenticationfilter signaled that we should short-circuit the request. Let all
// authenticationfilters contribute to an action result (to combine authentication
// challenges). Then,run this action result.
AuthenticationChallengeContext challengeContext =InvokeAuthenticationFiltersChallenge(
controllerContext,filterInfo.AuthenticationFilters, actionDescriptor,
authenticationContext.Result);
InvokeActionResult(controllerContext,challengeContext.Result ?? authenticationContext.Result);
}
else
{
AuthorizationContextauthorizationContext = InvokeAuthorizationFilters(controllerContext,filterInfo.AuthorizationFilters, actionDescriptor);
if(authorizationContext.Result != null)
{
// An authorizationfilter signaled that we should short-circuit the request. Let all
// authenticationfilters contribute to an action result (to combine authentication
// challenges).Then, run this action result.
AuthenticationChallengeContext challengeContext =InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authorizationContext.Result);
InvokeActionResult(controllerContext,challengeContext.Result ?? authorizationContext.Result);
}
else
{
if(controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
}
IDictionary<string, object> parameters =GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContextpostActionContext = InvokeActionMethodWithFilters(controllerContext,filterInfo.ActionFilters, actionDescriptor, parameters);
// The actionsucceeded. Let all authentication filters contribute to an action result (to
// combineauthentication challenges; some authentication filters need to do negotiation
// even on asuccessful result). Then, run this action result.
AuthenticationChallengeContextchallengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
postActionContext.Result);
InvokeActionResultWithFilters(controllerContext,filterInfo.ResultFilters,
challengeContext.Result ?? postActionContext.Result);
}
}
}
catch (ThreadAbortException)
{
// This type of exceptionoccurs as a result of Response.Redirect(), but we special-case so that
// the filters don't seethis as an error.
throw;
}
catch (Exception ex)
{
// something blew up, soexecute the exception filters
ExceptionContextexceptionContext = InvokeExceptionFilters(controllerContext,filterInfo.ExceptionFilters, ex);
if(!exceptionContext.ExceptionHandled)
{
throw;
}
InvokeActionResult(controllerContext, exceptionContext.Result);
}
return true;
}
// notify controller that no method matched
return false;
}
コードセグメント5
コードセグメント5からは、まずGetFilters法によってすべてのFilterが得られる.次に、AuthenticationFiltersが存在するかどうかをチェックし、存在する場合は実行し、そうでない場合はAuthorizationFiltersが存在するかどうかをチェックし、存在する場合は実行し、そうでない場合はActionおよびActionResultを実行します.つまり、コントローラは、Actionを実行する前に、まずAuthenticationとAuthorizationの2つのフィルタをチェックし、存在する場合に実行します.
続きを待つ....