Introduction
Picking up from our last discussion from part 2,
I hope by now we have got an idea on the Model. Now we can move to our next topic, which is
Controllers and my personal favorite, Routing. I like to cover Controllers and Routing
in this same post as I believe it will be more convenient to readers. As usual I
like to skip the how to, and rather to focus on the why.
Why Controllers
The controller's job is to avoid request chaos. Without the controller things get messy, and this is true
not just for MVC, but in real life also. Now think about a real life example. In an airport there
are hundreds of flights using runways, but does the airport accommodate individual runways for individual flights? No, flights share the same runway.
Now think if there is no control over which flight will use runways and when,
and things will be just like in this illustration:
To avoid such a mess, airports requires ATC (Air Traffic Control). As Wiki says:
The primary purpose of ATC is to prevent collisions, organize and expedite the flow of traffic, and provide information and other support for pilots.
Similar to ATC, we have a Controller in MVC. The Controller controls the application logic and acts as the coordinator between the view and the model.
This process is pretty simple. When the request from the user lands in the
MVC framework, it first goes to the controller. The Controller's responsibility at this point is to determine
which model should be called, and then call the appropriate function within that model. After calling the function, it will catch the result.
Now the question could be is this new concept introduced only in MVC? The answer is no.
If you read the first
part, our very known web forms uses
a similar concept. Web forms
use the Page Controller pattern that handles all HTTP requests directed at a specific
web page. This is where MVC tops traditional web forms by structuring the control
flow into controllers to make the application logic simple and testable.
When we are talking about controllers handling user requests, this is where Routing kicks in. Routing is pre-configured in
an MVC application that invokes the action methods
defined in the controller.
Routing and Controller
You have probably seen a highway shield or route marker used by travelers for identifying, navigating, and organizing routes within a county/state.
Just like a highway shield which directs travelers, ASP.NET routing directs incoming requests to
the appropriate controller actions.
As the name implies, routing is a handler which is in reality a class that processes the requests, such as a controller in an MVC application. The ASP.NET Routing module
is responsible for mapping incoming browser requests to a particular MVC controller actions.
Routing uses URL patterns to identify proper controllers as well as actions.
The URL pattern for routes in MVC applications typically is domain/{controller}/{action}/{id}.
When a request is received, it is routed to UrlRoutingModule-->MvcHandler. The MvcHandler HTTP handler determines which {controller} to invoke.
The action value in the URL determines which {action} method to call, with which
ID parameter if {id}
is specified.
How to Choose Controllers
In general, the ASP.NET MVC control action blocks the ASP.NET thread pool while the action is
being completed. This is called synchronous programming model. But in some scenarios
it is desired that the action will not block a thread in the thread pool. For these scenario we can use async controllers that
are being introduced in .NET Framework 4.5.
These are the benefits of using async controllers:
- For I/O, network-bound, or long-running and parallelizable requests, making async method will increase the responsiveness of
the application
- It can be more easily canceled by the user than a synchronous request
Now you might think what the fresh hell is this? When to use sync controllers and when to use async controllers?
Fear not, you do not have to choose blindly whether to use asyn or sync. Scenarios that fit with synchronous actions:
- Simple and short-running operations
- Cases where simplicity is more important than efficiency
- CPU-intensive operations
- When an action must perform several independent operations
Scenarios that fit with asynchronous actions:
- Long-running operations that are causing a bottleneck in performance
- Network- or I/O-intensive operations
- When the application requires the ability for users to cancel a long-running operation
- When IIS is required to service more requests by using asynchronous methods
Note to remember, if the controller action is CPU-bound, making the call asynchronous won't provide any benefit and might add
an overhead for such operations. Since an asynchronous
controller does not change the amount of time the request will take, it just frees up the thread executing the request so it can be allocated back into the ASP.NET thread pool.
How to Define Controllers
Physically a controller is just another class that must be post-fixed with
the "Controller" word that can expose controller actions. When a user enters a URL into the browser,
the MVC application uses routing rules to parse the URL and to determine the path of the controller. Any method that is defined
as public is considered an action method.
There are some additional requirements that must be satisfied by a controller action.
- A method used as a controller action cannot be overloaded.
- Controller action cannot be a static method.
How to Define Custom Routing
Any class derived from the ControllerBase
class and having a name that ends with "Controller" does not require any custom routing declaration.
But you can define your own route as well. A custom route constraint enables you to prevent a route from being matched unless some custom condition is matched.
If you want to add custom routes in an MVC application, you use the MapRoute(RouteCollection, String, String)
method in
Global.ascx.
When you define a route, you can assign a default routing value for a parameter. The default
is utilized if the value for that parameter is not present in the URL.
Testing Controllers
If we do not test our controller's behavior, the motivation of using MVC will not satisfied. ASP.NET MVC controllers are nothing but
simple classes, and controller actions are nothing but simple methods. So this approach makes the test case writing a lot easier.
We can conduct two types of testing on the respective controller:
- whether or not the correct action result is returned from the controller action.
- whether or not the correct view is returned for a view result.
For example purposes, let's write a unit test for the home controller. Here is our very own default HomeController:
In order to test the HomeController controller actions, all we need to do is create a new instance using the default constructor and call the action as shown below.
The above test demonstrated may not satisfy or cover every aspect of a unit
test for a controller, but it's good to start with.
Advanced readers can check the references given in the next section. I hope to cover a full post describing
different test cases for controllers.
References and Good Reads