asp.net MVC controller activation system detailed: default implementation

Source: Internet
Author: User
Tags reflection

The controller activation system eventually creates the corresponding Conroller object through the registered Controllerfactory, If an controllerfactory type or type is not explicitly registered (by invoking the Setcontrollerfactory method of the current controllerbuilder), By default, a Defaultcontrollerfactory object is used, and we will now discuss the default controller activation mechanism implemented in the Defaultcontrollerfactory type.

Analysis of the type of controller

The precondition of activating target controller object is that the corresponding controller type can be parsed correctly. For Defaultcontrollerfactory to come, The information used to resolve the target controller type includes the routedata generated by the routed object that matches the current request (which contains the controller name and namespace) and the namespace contained in the current controllerbuilder. The first thing readers can think of is to get the corresponding type by controller name, and to make up the full name of the controller type through the namespace, and finally traverse all the assemblies with this name to load the appropriate type.

This looks like a good solution, but in fact it is completely unworkable. Don't forget that the controller name that is part of the URL of the request is case-insensitive, and the type name is case-sensitive, either the namespace specified when the route is registered or the default namespace of the current Controllerbuilder, possibly including the wildcard character (*). Since we cannot get the true type name of controller by the given controller name and namespace, it is impossible to parse the type of controller by name.

asp.net MVC's controller activation system does the opposite. It first traverses the assembly used by the Getreferencedassemblies method of compiling the Web application through BuildManager static method, and gets all the types that implement the interface IController by reflection. Finally, the type of the target controller is obtained from the given controller's name and namespace as a matching criterion in the list of predefined types.

Example Demo: Create a custom Controllerfactory impersonation controller default activation mechanism

In order to give readers a deep understanding of the default controller activation mechanism, especially the controller type of parsing mechanism, we simulate the implementation through a custom controllerfactory. Because we use reflection to create controller objects, we name the custom controllerfactory as reflelctioncontrollerfactory. [Source code download from here]

1:public class Reflelctioncontrollerfactory:icontrollerfactory
2: {
3://other Members
4:private static list<type> controllertypes;
5:static Reflelctioncontrollerfactory ()
6: {
7:controllertypes = new list<type> ();
8:foreach (Assembly Assembly in Buildmanager.getreferencedassemblies ())
9: {
10:controllertypes.addrange (assembly. GetTypes (). Where (Type => typeof (IController). IsAssignableFrom (type)));
11:}
12:}
13:
14:public IController Createcontroller (RequestContext requestcontext, String controllername)
15: {
16:type Controllertype = this. Getcontrollertype (Requestcontext.routedata, controllername);
17:if (Null = = Controllertype)
18: {
19:throw new HttpException (404, "No controller Found");
20:}
21:return (IController) activator.createinstance (Controllertype);
22:}
23:
24:private static bool Isnamespacematch (string requestednamespace, String targetnamespace)
25: {
26:if (!requestednamespace.endswith (". *", StringComparison.OrdinalIgnoreCase))
27: {
28:return string. Equals (Requestednamespace, targetnamespace, stringcomparison.ordinalignorecase);
29:}
30:requestednamespace = requestednamespace.substring (0, Requestednamespace.length-". *". Length);
31:if (!targetnamespace.startswith (Requestednamespace, StringComparison.OrdinalIgnoreCase))
32: {
33:return false;
34:}
35:return (requestednamespace.length = = targetnamespace.length) | | (Targetnamespace[requestednamespace.length] = = = '. '));
36:}
37:
38:private Type Getcontrollertype (ienumerable<string> namespaces, type[] controllertypes)
39: {
40:var types = (from type in controllertypes
41:where namespaces. Any (NS => isnamespacematch (ns, type. Namespace))
42:select type). ToArray ();
43:switch (types. Length)
44: {
45:case 0:return null;
46:case 1:return Types[0];
47:default:throw New InvalidOperationException ("Multiple types were found that match the requested R name. ");
48:}
49:}
50:
51:protected virtual Type getcontrollertype (routedata routedata, String controllername)
52: {
53://Omit implementation
54:}
55:}

As shown in the code snippet above, Reflelctioncontrollerfactory has a static controllertypes field due to the type of all controller saved. In the static constructor, we call the BuildManager Getreferencedassemblies method to get all the assemblies that are used to compile the Web application and derive all the types that implement the IController interface. These types are all added to the list of types represented by static field controllertypes.

The parsing implementation of the controller type is in the protected Getcontrollertype method, in the Createcontroller method for the final activation of the Controller object, We get the controller type that matches the specified RequestContext and controller names by calling this method. Finally, the corresponding controller object is created by calling the Activator static method CreateInstance based on the type. If a matching controller type cannot be found (the Getcontrollertype method returns null), a httpexception with an HTTP state of 404 is thrown.

In Reflelctioncontrollerfactory, two helper methods are defined, Isnamespacematch to determine whether the true namespace of the controller type matches the specified namespace (which may contain the wildcard character). The case is ignored during character comparisons. The private method getcontrollertype an exact matching controller type based on an array of types that match the specified namespace list and type name. If more than one matching type is obtained, the invalidoperation exception is thrown directly, with a hint of multiple matching controller types, or null if no matching type is found.

In the Getcontrollertype method that is used to parse the controller type as shown below, we filter out the type names that match the incoming controller names from all of the controller types listed in advance. We first further filter through the namespace of the routed object, and if we can find a unique type, return it directly as a controller type. In order to determine whether a fallback namespace is used to parse the controller type, we obtain an element "Usenamespacefallback" from the Datatokens of the Routedata object as a parameter parameter, If the element exists and the value is False, NULL is returned directly.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.