Next, I will go to Asp.net MVC source code analysis-In actionresult, viewengineresult result = viewenginecollection. findview (context, viewname, mastername) in viewresult. How did I find the view? Let's take a look at the findview method in viewenginecollection.
Return find (E => E. findview (controllercontext, viewname, mastername, true ),
E => E. findview (controllercontext, viewname, mastername, false ));
But there are a lot of things to do. Call an internal find method,
Private viewengineresult find (func <iviewengine, viewengineresult> cachelocator, func <iviewengine, viewengineresult> Locator ){
// First, look up using the cachelocator and do not track the searched paths in non-matching view Engines
// Then, look up using the normal locator and track the searched paths so that an error view engine can be returned
Return find (cachelocator, tracksearchedpaths: false)
?? Find (locator, tracksearchedpaths: True );
}
Here, cachelocator = E. findview (controllercontext, viewname, mastername, true), locator = E. findview (controllercontext, viewname, mastername, false), it is also calling an internal find method,
Private viewengineresult find (func <iviewengine, viewengineresult> lookup, bool tracksearchedpaths) {// returns // 1st result // or list of searched paths (if tracksearchedpaths = true) // or null viewengineresult result; List <string> searched = NULL; If (tracksearchedpaths) {searched = new list <string> ();} foreach (iviewengine engine in combineditems) {If (engine! = NULL) {result = Lookup (engine); If (result. view! = NULL) {return result;} If (tracksearchedpaths) {searched. addrange (result. searchedlocations) ;}}}if (tracksearchedpaths) {// remove duplicate search paths since multiple view engines cocould have potentially looked at the same path return New viewengineresult (searched. distinct (). tolist ();} else {return NULL ;}}
Tracksearchedpaths indicates whether to record the cable receiving path,FirstCheck whether the view can be found. If the view cannot be found, record the query path here.. By the way, there is a findpartialview method in viewenginecollection that is consistent with the findview method logic..
Combineditems here is actually the engines attribute in viewengines. By default, only the webformviewengine and razorviewengine instances are supported. So it will traverse all iviewengines to find the view. To improve performance,We can remove an iviewengine that we don't need ., For example, when I use razor to develop MVC, I can remove webformviewengine to improve the performance. Add viewengines. Engines. removeat (0) to application_start );
The tracksearchedpaths parameter records the search path. What is the result? If the corresponding view is not found in iviewengine, the search path is recorded.
If (tracksearchedpaths ){
Searched. addrange (result. searchedlocations );
}
If all iviewengine is found, no view is found. If tracksearchedpaths is false, null is directly returned. Otherwise, a viewengineresult with no view is returned.
The results are as follows:
Now let's take a look at the findview method in the virtualpathproviderviewengine class:
Public Virtual viewengineresult findview (controllercontext, string viewname, string mastername, bool usecache) {If (controllercontext = NULL) {Throw new argumentnullexception ("controllercontext");} If. isnullorempty (viewname) {Throw new argumentexception (mvcresources. common_nullorempty, "viewname");} string [] viewlocationssearched; string [] masterlocationssearched; s Tring controllername = controllercontext. routedata. getrequiredstring ("controller"); string viewpath = getpath (controllercontext, viewlocationformats, callback, "viewlocationformats", viewname, controllername, _ cachekeyprefix_view, usecache, out viewlocationssearched ); string masterpath = getpath (controllercontext, masterlocationformats, areamasterlocationformats, "masterlocationform ATS ", mastername, controllername, _ cachekeyprefix_master, usecache, out masterlocationssearched); If (string. isnullorempty (viewpath) | (string. isnullorempty (masterpath )&&! String. Loads (mastername) {return New viewengineresult (viewlocationssearched. Union (masterlocationssearched);} return New viewengineresult (createview (controllercontext, viewpath, masterpath), this );}
A very important string viewpath = getpath (controllercontext, viewlocationformats, areaviewlocationformats, "viewlocationformats", viewname, controllername, _ cachekeyprefix_view, usecache, out viewlocationssearched); getpath is used to find viewpath and masterpath.
Private string getpath (controllercontext, string [] locations, string [] arealocations, string locationspropertyname, string name, string controllername, string cachekeyprefix, bool usecache, out string [] searchedlocations) {searchedlocations = _ emptylocations; If (string. isnullorempty (name) {return string. empty;} string areaname = areahelpers. getareaname (controllercontext. route Data); bool usingareas =! String. isnullorempty (areaname); List <viewlocation> viewlocations = getviewlocations (locations, (usingareas )? Arealocations: NULL); If (viewlocations. count = 0) {Throw new invalidoperationexception (string. format (cultureinfo. currentculture, mvcresources. callback, locationspropertyname);} bool namerepresentspath = isspecificpath (name); string cachekey = createcachekey (cachekeyprefix, name, (namerepresentspath )? String. Empty: controllername, areaname); If (usecache) {return viewlocationcache. getviewlocation (controllercontext. httpcontext, cachekey);} return (namerepresentspath )? Getpathfromspecificname (controllercontext, name, cachekey, ref searchedlocations): getpathfromgeneralname (controllercontext, viewlocations, name, controllername, areaname, cachekey, ref searchedlocations );}
String areaname = areahelpers. getareaname (controllercontext. routedata );
Bool usingareas =! String. isnullorempty (areaname );
Usingareas is false by default. The getviewlocations method returns a set of viewlocations.CodeSimple. For example, reset it in razorviewengine.
Viewlocationformats = new [] {
"~ /Views/{1}/{0}. cshtml ",
"~ /Views/{1}/{0}. vbhtml ",
"~ /Views/shared/{0}. cshtml ",
"~ /Views/shared/{0}. vbhtml"
};
Add the current controller as home and action as index,
Then, the actual search path will be
"~ /Views/home/index. cshtml ",
"~ /Views/home/index. vbhtml ",
"~ /Views/shared/index. cshtml ",
"~ /Views/shared/index. vbhtml"
ActuallyOur general project is either C # Or VB, so viewlocationformats can remove two elements to improve performance..
Bool namerepresentspath = isspecificpath (name) indicates whether our viewname is ~ And.
String cachekey = createcachekey (cachekeyprefix, name, (namerepresentspath )? String. Empty: controllername, areaname); Create a cache key.
If (usecache ){
Return viewlocationcache. getviewlocation (controllercontext. httpcontext, cachekey );
}
Viewpath is returned from the cache. By default, viewlocationcache = new defaultviewlocationcache ()
The main method of defaultviewlocationcache is as follows:
Public String getviewlocation (httpcontextbase httpcontext, string key ){
If (httpcontext = NULL ){
Throw new argumentnullexception ("httpcontext ");
}
Return (string) httpcontext. cache [Key];
}
Public void insertviewlocation (httpcontextbase httpcontext, string key, string virtualpath ){
If (httpcontext = NULL ){
Throw new argumentnullexception ("httpcontext ");
}
Httpcontext. cache. insert (Key, virtualpath, null/* dependencies */, cache. noabsoluteexpiration, timespan );
}
Very simple.
Now let's go back to the getpath method. If you don't need to cache, there will be only the last sentence.
Return (namerepresentspath )?
Getpathfromspecificname (controllercontext, name, cachekey, ref searchedlocations ):
Getpathfromgeneralname (controllercontext, viewlocations, name, controllername, areaname, cachekey, ref searchedlocations );
The two methods are implemented as follows:
Private string getpathfromgeneralname (controllercontext, list <viewlocation> locations, string name, string controllername, string areaname, string cachekey, ref string [] searchedlocations) {string result = string. empty; searchedlocations = new string [locations. count]; for (INT I = 0; I <locations. count; I ++) {viewlocation location = locations [I]; string virtualpath = location. f Ormat (name, controllername, areaname); If (fileexists (controllercontext, virtualpath) {searchedlocations = _ emptylocations; Result = virtualpath; viewlocationcache. insertviewlocation (controllercontext. httpcontext, cachekey, result); break;} searchedlocations [I] = virtualpath;} return result;} private string getpathfromspecificname (controllercontext, string name, String cache Key, ref string [] searchedlocations) {string result = Name; If (! (Filepathissupported (name) & fileexists (controllercontext, name) {result = string. empty; searchedlocations = new [] {name};} viewlocationcache. insertviewlocation (controllercontext. httpcontext, cachekey, result); return result ;}
Both methods call a common method fileexists. fileexists is an inline function that directly calls virtualpathprovider. fileexists (virtualpath). By default, virtualpathprovider = hostingenvironment. virtualpathprovider. But what is actually called isBuildmanager. getobjectfactory (virtualpath, false )! = NULLWe will ignore it here for how it checks the path. Call the filepathissupported method in the getpathfromspecificname method to check the viewname extension.
Here we can complete the search for viewpath.
Now let's go back to findview,
If (string. isnullorempty (viewpath) | (string. isnullorempty (masterpath )&&! String. isnullorempty (mastername ))){
Return new viewengineresult (viewlocationssearched. Union (masterlocationssearched ));
}
This sentence is simple. Return new viewengineresult ( createview (controllercontext, viewpath, masterpath), this); this sentence returns a viewengineresult. The viewengineresult constructor is also very simple. Now let's take a look at the createview method. The specific implementation of razorviewengine and webformviewengine is as follows:
Protected override iview createview (controllercontext, string viewpath, string masterpath ){
VaR view = new razorview (controllercontext, viewpath,
Layoutpath: masterpath, runviewstartpages: True, viewstartfileextensions: fileextensions, viewpageactivator: viewpageactivator );
Return view;
}
Protected override iview createview (controllercontext, string viewpath, string masterpath ){
Return new webformview (controllercontext, viewpath, masterpath, viewpageactivator );
}
Now we have successfully found the view.