In the previous articleArticleAsp.net MVC source code analysis-how to find ihttphandler in the routing article and finally mentioned mvchandler. Let's take a look at its definition.
Public classMvchandler: ihttpasynchandler, ihttphandler, irequiressessionstate
It has several important attributes:
Internal controllerbuilder, controllerbuilder class is mainly responsible for creating icontrollerfactory
Public requestcontext, which is the packaging of the httpcontext (httpcontext class) and routedata of this request
The beginprocessrequest method has several important statements.Code:
Icontrollerfactory factory;
Processrequestinit (httpcontext, out controller, out factory );
Iasynccontroller asynccontroller = controller as iasynccontroller;
If (asynccontroller! = NULL ){
}
Else
{
Action action = delegate {
Try {
Controller. Execute (requestcontext );
}
Finally {
Factory. releasecontroller (Controller );
}
Return asyncresultwrapper. beginsynchronous (callback, state, action, _ processrequesttag );
}
In this article, we are mainly concerned aboutProcessrequestinit (httpcontext, out controller, out factory );How to Create a controller
The processrequestinit method is roughly as follows:
Private void processrequestinit (httpcontextbase httpcontext, out icontroller controller, out icontrollerfactory ){
String controllername = requestcontext. routedata. getrequiredstring ("controller ");
Factory = controllerbuilder. getcontrollerfactory ();
Controller = factory. createcontroller (requestcontext, controllername );
}
String controllername = requestcontext. routedata. getrequiredstring ("controller ");This sentence is very easy to get the name of the controller. routedata is the current route data. The key in it includes the registered URL format "{controller}/{action}/{ID }", and default ults = new {controller = "home", Action = "Index", id = urlparameter. optional}
Now you know why {controller}/{action} is required in the previous article.
Now let's take a look at the main code of controllerbuilder:
Public controllerbuilder (): This (null) {} internal controllerbuilder (iresolver <icontrollerfactory> serviceresolver) {_ serviceresolver = serviceresolver ?? New singleserviceresolver <icontrollerfactory> () => _ factorythunk (), new defaultcontrollerfactory {controllerbuilder = this}, "controllerbuilder. getcontrollerfactory ");} public static controllerbuilder current {get {return _ instance;} public icontrollerfactory getcontrollerfactory () {return _ serviceresolver. current;} public void setcontrollerfactory (icontrollerfactory controllerfactory) {If (controllerfactory = NULL) {Throw new argumentnullexception ("controllerfactory");} _ factorythunk = () => controllerfactory;} public void setcontrollerfactory (type controllerfactorytype) {If (controllerfactorytype = NULL) {Throw new argumentnullexception ("controllerfactorytype");} If (! Typeof (icontrollerfactory ). isassignablefrom (controllerfactorytype) {Throw new argumentexception (string. format (cultureinfo. currentculture, mvcresources. controllerbuilder_missingicontrollerfactory, controllerfactorytype), "controllerfactorytype");} _ factorythunk = delegate () {try {return (icontrollerfactory) activator. createinstance (controllerfactorytype);} catch (exception ex) {Throw new invalidoperationexception (string. format (cultureinfo. currentculture, mvcresources. controllerbuilder_errorcreatingcontrollerfactory, controllerfactorytype), Ex );}};}
From the above code, we can know that if the setcontrollerfactory method of controllerbuilder is not called, we will use the default defaultcontrollerfactory. If it is set, we can use our ownControllerfactory. If the project needs to implement its own controllerfactory, we canThe protected void application_start () method of Global. asax. CS calls controllerbuilder. Current. setcontrollerfactory (XXX );
Let's take a look at how defacontrocontrollerfactory was created:
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 contstructor"
);
}
}
Here we involve an icontrolleractivator interface. By default, the defaultcontrolleractivator class is used,
Now we have obtained controllerfactory, and then we can see how it creates controller. To put it bluntly, it is the createcontroller method of defaultcontrollerfactory.
Public Virtual icontroller createcontroller (requestcontext, string controllername ){
..
Type controllertype = getcontrollertype (requestcontext, controllername );
Icontroller controller = getcontrollerinstance (requestcontext, controllertype );
Return controller;
}
by default, we do not have namespaces, while getcontrollertype (requestcontext, controllername) mainly calls getcontrollertypewithinnamespaces (requestcontext. routedata. route, controllername, null/* namespaces */)
private type getcontrollertypewithinnamespaces (routebase route, string controllername, hashset
namespaces) {// once the master list of controllers has been created we can quickly index into it controllertypecache. ensureinitialized (buildmanager); icollection
matchingtypes = controllertypecache. getcontrollertypes (controllername, namespaces); Switch (matchingtypes. count) {Case 0: // no matching types return NULL; Case 1: // single matching type return matchingtypes. first (); default: // Multiple matching types throw createambiguouscontrollerexception (route, controllername, matchingtypes); }}
This mainly involves a controllertypecache, which caches the currentProgramAll controllers.
Let's take a look at the main methods of controllertypecache.
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 = grou Pedbyname. todictionary (G => G. Key, G => G. tolookup (t => T. namespace ?? String. Empty, stringcomparer. ordinalignorecase), stringcomparer. ordinalignorecase) ;}}} internal static bool iscontrollertype (type T) {return t! = NULL & T. ispublic & T. Name. endswith ("controller", stringcomparison. ordinalignorecase )&&! T. isabstract & typeof (icontroller ). isassignablefrom (t);} public icollection <type> getcontrollertypes (string controllername, hashset <string> namespaces) {hashset <type> matchingtypes = new hashset <type> (); ILookup <string, type> NSLookup; If (_ cache. trygetvalue (controllername, out NSLookup) {// This friendly name was located in the cache, now cycle through namespaces if (namespaces! = NULL) {foreach (string requestednamespace in namespaces) {foreach (VAR targetnamespacegrouping in NSLookup) {If (isnamespacematch (requestednamespace, targetnamespacegrouping. key) {matchingtypes. unionwith (targetnamespacegrouping) ;}}} else {// if the namespaces parameter is null, search * Every * namespace foreach (VAR nsgroup in NSLookup) {matchingtypes. unionwith (nsgroup) ;}}return matchingtypes ;}
List controllertypes = typecacheutil. getfilteredtypesfromassemblies (_ typecachename, iscontrollertype, buildmanager); the code is used to query the class of the current program. This class must be verified by the iscontrollertype method (the class is a class that inherits non-Abstract from icontroller and ends with the class name Controller) , and the _ cache is a dictionary type data.
now we have obtained the controllertype. Let's take a look at icontroller controller = getcontrollerinstance (requestcontext, controllertype ); here is the main sentence
controlleractivator. create (requestcontext, controllertype); While controlleractivator is the previous defaultcontrolleractivator, The defaultcontrolleractivator code is as follows:
Private Class Identifier: icontrolleractivator {func <strong> _ resolverthunk; Public defaultcontrolleractivator (): This (null) {} public defadefacontrolleractivator (writable resolver) {If (Resolver = NULL) {_ resolverthunk = () => dependencyresolver. current;} else {_ resolverthunk = () => resolver;} public icontroller create (requestcontext requestconte XT, type controllertype) {try {return (icontroller) (_ resolverthunk (). getservice (controllertype )?? Activator. createinstance (controllertype);} catch (exception ex) {Throw new invalidoperationexception (string. Format (cultureinfo. currentculture, mvcresources. Sources, controllertype), Ex );}}}
Here _ resolverthunk () comes from dependencyresolver. Current,
Private Static dependencyresolver _ instance = new dependencyresolver ();
Public static idependencyresolver current {
Get {
Return _ instance. innercurrent;
}
}
Private idependencyresolver _ current = new defaultdependencyresolver ();
Public idependencyresolver innercurrent {
Get {
Return _ current;
}
}
SoCreating a controller is Getultdependencyresolver's getservice Method, Core codeReturn activator. createinstance (servicetype);
This document is far from being pulled. It can be seen how troublesome it is to create a controller. ReadSource codeIt mainly helps us understand its logic and also let us learn its development ideas.
This article talks about so much, The most commonly used project is controllerbuilder. Current. setcontrollerfactory (XXX) ;You only need to understand it.