The MVC Application Execution Process (C#)

The module and handler are the entry points to the ASP.NET MVC framework. They perform the following actions:

·         Select the appropriate controller in an MVC Web application.

·         Obtain a specific controller instance.

·         Call the controller s Execute method.

 

Requests to an ASP.NET MVC-based Web application first pass through the UrlRoutingModule object, (which is an HTTP module)

This module parses the request and performs route selection.

The UrlRoutingModule object selects the first route object that matches the current request.

(A route object is a class that implementsRouteBase, and is typically an instance of the Route class.)

If no routes match, the UrlRoutingModule object does nothing and lets the request fall back to the regular ASP.NET or IIS request processing.

From the selected Route object, the UrlRoutingModule object obtains the IRouteHandler object that is associated with the Route object.

Typically, in an MVC application, this will be an instance ofMvcRouteHandler. The IRouteHandler instance creates an IHttpHandler object and passes it theIHttpContext object. By default, the IHttpHandler instance for MVC is the MvcHandler object. TheMvcHandler 

object then selects the controller that will ultimately handle the request.


Note: When an ASP.NET MVC Web application runs in IIS 7.0, no file name extension is required for MVC projects. However, in IIS 6.0, the handler requires that you map the .mvc file name extension to the ASP.NET ISAPI DLL.


Stage

Details

Receive first request for the application

In the Global.asax file, Route objects are added to the RouteTable object.

Perform routing

The UrlRoutingModule module uses the first matching Route object in the RouteTablecollection to create the RouteData object, which it then uses to create a RequestContext(IHttpContext) object.

Create MVC request handler

The MvcRouteHandler object creates an instance of the MvcHandler class and passes it the RequestContext instance.

Create controller

The MvcHandler object uses the RequestContext instance to identify theIControllerFactory object (typically an instance of the DefaultControllerFactory class) to create the controller instance with.

Execute controller

The MvcHandler instance calls the controller s Execute method.

Invoke action

Most controllers inherit from the Controller base class. For controllers that do so, theControllerActionInvoker object that is associated with the controller determines which action method of the controller class to call, and then calls that method.

Execute result

A typical action method might receive user input, prepare the appropriate response data, and then execute the result by returning a result type. The built-in result types that can be executed include the following: ViewResult (which renders a view and is the most-often used result type), RedirectToRouteResultRedirectResultContentResultJsonResult, and EmptyResult.

MVC Basics


MVC

The purpose of many computer systems is to retrieve data from a data store and display it for the user. After the user changes the data, the system stores the updates in the data. One problem is that the user interface tends to change much more frequently than the data storage system. Another problem with coupling the data and user interface pieces is that business applications tend to incorporate business logic that goes far beyond data transmission.

 

ASP.NET Routing

A browser request gets mapped to a controller action through a feature of the ASP.NET framework called ASP.NET Routing. ASP.NET Routing is used by the ASP.NET MVC framework to route incoming requests to controller actions.

ASP.NET Routing uses a route table to handle incoming requests. This route table is created when your web application first starts. The route table is setup in the Global.asax file.

When an ASP.NET application first starts, the Application_Start() method is called. this method calls the RegisterRoutes() method and the RegisterRoutes() method creates the default route table.

The default route table consists of one route. This default route breaks all incoming requests into three segments (a URL segment is anything between forward slashes). The first segment is mapped to a controller name, the second segment is mapped to an action name, and the final segment is mapped to a parameter passed to the action named Id.

For example, consider the following URL:

/Product/Details/3

This URL is parsed into three parameters like this:

Controller = Product

Action = Details

Id = 3

 

The Default route defined in the Global.asax file includes default values for all three parameters. The default Controller is Home, the default Action is Index, and the default Id is an empty string.

The URL is parsed like this:

Controller = Home

Action = Index

Id = ��

The request is routed to the Index() action on the HomeController class.

 

 

Controllers

A controller is responsible for controlling the way that a user interacts with an MVC application. A controller contains the flow control logic for an ASP.NET MVC application. A controller determines what response to send back to a user when a user makes a browser request.

A controller is just a class (for example, a Visual Basic or C# class).

HomeController has two methods named Index() and About(). These two methods correspond to the two actions exposed by the controller. 

The URL /Home/Index invokes the HomeController.Index() method and the URL /Home/About invokes the HomeController.About() method.

Any public method in a controller is exposed as a controller action. You need to be careful about this. This means that any public method contained in a controller can be invoked by anyone with access to the Internet by entering the right URL into a browser.

Views

The two controller actions exposed by the HomeController class, Index() and About(), both return a view. A view contains the HTML markup and content that is sent to the browser. A view is the equivalent of a page when working with an ASP.NET MVC application.

You must create your views in the right location. The HomeController.Index() action returns a view located at the following path:

\Views\Home\Index.aspx

The HomeController.About() action returns a view located at the following path:

\Views\Home\About.aspx

 

In general, if you want to return a view for a controller action, then you need to create a subfolder in the Views folder with the same name as your controller. Within the subfolder, you must create an .aspx file with the same name as the controller action.

A view is very similar to a page in Active Server Pages or ASP.NET Web Forms. A view can contain HTML content and scripts. You can write the scripts in your favorite .NET programming language (for example, C# or Visual Basic .NET). You use scripts to display dynamic content such as database data.

Models

An MVC model contains all of your application logic that is not contained in a view or a controller. The model should contain all of your application business logic, validation logic, and database access logic. For example, if you are using the Microsoft Entity Framework to access your database, then you would create your Entity Framework classes (your .edmx file) in the Models folder.

A view should contain only logic related to generating the user interface. A controller should only contain the bare minimum of logic required to return the right view or redirect the user to another action (flow control). Everything else should be contained in the model.

In general, you should strive for fat models and skinny controllers. Your controller methods should contain only a few lines of code. If a controller action gets too fat, then you should consider moving the logic out to a new class in the Models folder.

Web services steps

Web services give developers the ability to utilize four open Web standards:

  1. HTTP - Hypertext Transfer Protocol
    The standard protocol used over Port 80, which traverses firewalls, and is responsible for requesting and transmitting data over the Internet.

  2. SOAP - Simple Object Access Protocol 
    An XML-inherent protocol that encloses a set of rules for data description and process. As a standard, this is the center-piece that complements the other three standards mentioned here.

  3. XML - Extensible Markup Language
    The most common markup language in which all this information is written.
        4. WSDL - Web Services Description Language
        An XML-based method used to identify Web Services and their access at runtime. .NET provides a tool called WSDL.exe, which essentially makes it 
        quite easy to    generate an XML Web service as an XML file. This contains all the methods and instructions the Web Service has, and typically uses 
        SOAP as its default.

This article will see you create and consume a data-driven .NET XML Web service in 5 quick and easy steps!

I'll assume that you have a decent grasp of common .NET data access, Web server controls, such a datagrid, and some object-oriented programming concepts. If not, don't worry too much. If you complete the examples, and view the results, you should have no difficulty in keeping up and observing the causes and effects of what this tutorial entails.

Prior to .NET, there were other alternatives that could be used to access a Web service, such as Microsoft's MSXML component, which enabled you to communicate with the given Web Service over HTTP POST. However, this process, while acceptable, is just not .NET.

Ok, let's begin!

Step 1 - Create the Web Service

First we'll create the Web service function or method that'll you'll call (or "expose") over the Internet as you would any object-oriented class. The difference is that we'll have to incorporate and import all the necessary Web Services namespaces, syntax and attributes, as well as our data namespaces, in this case. As this article uses C#, any important differences regarding VB will be shown as well.

So go ahead and copy the code below to a file called suppliers.asmx. Save it to your Inetpub/wwwroot folder, and then run it in your browser using http://localhost/suppliers.asmx. What you'll see is a list of the Web Service Descriptions, including the Web service name and the exposed method.

By clicking the exposed method link, you'll be presented with the three main protocols that are available for your use. Just so you know, the file with the .asmx extension is the actual Web Service file that enables the ASP.NET runtime to return all pertinent exposed Web service methods and information.

<%@ WebService Language="C#" Class="GetInfo" %> 

using System; 
using System.Data; 
using System.Data.SqlClient; 
using System.Web.Services; 

[WebService(Description="My Suppliers List Web Service")] 

public class GetInfo : WebService  


 [WebMethod(BufferResponse=true)] 

 public DataSet ShowSuppliers (string str)  
 { 

   SqlConnection dbConnection = new SqlConnection("server=(local); 
                                uid=sa;pwd=;database=Northwind;"); 

   SqlDataAdapter objCommand = new SqlDataAdapter("select  
              ContactName, CompanyName, City, Phone from Suppliers  
              where Country = '" + str + "' order by ContactName  
              asc", dbConnection); 

   DataSet DS = new DataSet(); 

   objCommand.Fill(DS); 

   return DS; 

   dbConnection.Close(); 
   dbConnection = null; 

 } 

}

The <%@ WebService Language="C#" Class="GetInfo" %> directive sets up the file as a Web Service, gives it a name and specifies the language it uses.

Aside from your typical data namespace import, you also add the Web Services namespace:

using System.Web.Services

Step 2 - Consume the Web Service Source File

Next, append ?WSDL to the Web services URI (Uniform Resource Identifier) like so:

http://localhost/suppliers.asmx?WSDL

"What's this?" you ask. This is the WSDL document that the client will use to access this service.

Even so, you don't have to know much about this unreadable code to produce results, which is where the WSDL.exe command-line tool comes into play. Due to the open protocol nature of Web services, this tool enables you to consume non-.NET Web Services as well.

You can bypass WSDL and test the XML results instantly through HTTP GET protocol, by typing the following into your browser:

http://localhost/suppliers.asmx/ShowSuppliers?str=USA

This passes USA as a parameter to the ShowSuppliers class method. Note that if you're using .NET SDK Beta 1.1 (v.1.1.4322), it seems to prefer HTTP POST protocol, so invoke the Web Service through its exposed method link. The XML results? A little crazy, huh?

Nevertheless, scroll down a little and you'll see the query results of our class call. Later in Step 4, all this gobbledygook will make much more readable sense.

So, to create our proxy class sourcefile, make a batch file named makeWS.bat and type in:

[C#] 
wsdl.exe /l:CS /n:WService /out:bin/GetSuppliers.cs   
 http://localhost/suppliers.asmx?WSDL


This tells our WSDL tool that /l is the language you're using.

/n: creates the Wservice namespace so you can reference it from your .aspx page, and finally send the C#/VB source file to the bin folder from which we'll create the dll assembly that we'll use in Step 3. Also add pause, so after this is all said and done, you'll be prompted to press any key to continue, and more importantly, you'll know for sure that it all went according to plan.

Now locate your new batch file, and double-click on it to run it. Once you've done this, you will have created, rather consumed, the proxy class or source file GetSuppliers.cs right from the .asmx file. Have a look in your bin folder.

Note that you could run these commands via the command-line prompt, but for the sake of convenience, I prefer the good old' batch file. You may be wondering where the phrase "consuming" came from? In this regard, it simply refers to the presentation of the Web Service to the "consumer".

Step 3 - Build our Object

OK, now let's invoke the .NET C# compiler (csc.exe) or VB compiler (vbc.exe) to convert your source file into an assembly or DLL. Create a new .bat file named makelib.bat and type in:

[C#]

csc /t:library /out:bin\GetSuppliers.dll bin\GetSuppliers.cs      
 /reference:System.dll,System.Data.dll,System.Web.dll,   
 System.Web.Services.dll,System.XML.dll /optimize


All this does is compile the source file from the bin folder to a DLL of set name, to the bin folder.

The /t:library instructs the compiler to create a dll (dynamic link library), rather than an exe (executable) file, with /reference:importing the necessary dll's libraries that will be used in our Web service. Finally, we /optimize our dll to produce smaller, faster, and more efficient output.

Step 4 - Put it All Together

Now copy and paste the code below to an .aspx .NET page and name it. This code is for the page that will access the assembly/DLL in your bin folder, and with all the Web server controls, pass the appropriate parameters to the Web Service. Go ahead and run it (http://localhost/Websrvce.aspx).

<%@ Page Language="C#" Explicit="true" Strict="true" Buffer="true"%>   
<%@ Import Namespace="System.Data" %>   
<%@ Import Namespace="System.Data.SqlClient" %>   
<%@ Import Namespace="WService" %>   
  
<html>   
<script language="C#" runat="server">   
  
void Page_Load(Object sender, EventArgs e)    
{    
 Response.Flush();   
}   
  
void SubmitBtn_Click (object src, EventArgs e)   
{   
  
 int RcdCount;   
 string Catg = DropDown1.SelectedItem.Text;   
  
  
 //Instantiate DLL   
 GetInfo supInfo = new GetInfo();   
  
  
 //Pass parameter into DLL function   
 DataSet MyData = supInfo.ShowSuppliers(Catg);   
  
  
 MyDataGrid.DataSource = MyData.Tables[0].DefaultView;   
 MyDataGrid.DataBind();   
    
 RcdCount = MyData.Tables[0].Rows.Count;   
 if (RcdCount <= 0)    
 {   
  
 Message.InnerHtml = "<b>No results were found for <FONT    
                    Color=Red><i>"+ Catg +"</i></font>";   
 MyDataGrid.Visible = false; //Hide Results until needed   
  
 }   
 else   
 {   
  
   Message.InnerHtml = "<b><FONT Color=Red><i>" + Catg +   
    "</i></font> has " + RcdCount + " local suppliers</b>";   
 MyDataGrid.Visible = true;   
 }   
}   
  
</script>   
<body style="font: 10pt verdana">   
  
<h4>Accessing Data with Web Services</h4>   
  
<form runat="server">   
  
<asp:DropDownList id=DropDown1 runat="server">   
<asp:ListItem>Australia</asp:ListItem>   
<asp:ListItem>Brazil</asp:ListItem>   
<asp:ListItem>Canada</asp:ListItem>   
<asp:ListItem>Denmark</asp:ListItem>   
<asp:ListItem>Finland</asp:ListItem>   
<asp:ListItem>France</asp:ListItem>   
<asp:ListItem>Germany</asp:ListItem>   
<asp:ListItem>Italy</asp:ListItem>   
<asp:ListItem>Japan</asp:ListItem>   
<asp:ListItem>Netherlands</asp:ListItem>   
<asp:ListItem>Norway</asp:ListItem>   
<asp:ListItem>Singapore</asp:ListItem>   
<asp:ListItem>Spain</asp:ListItem>   
<asp:ListItem>Sweden</asp:ListItem>   
<asp:ListItem>UK</asp:ListItem>   
<asp:ListItem>USA</asp:ListItem>   
</asp:DropDownList>   
<asp:button text="Submit" OnClick="SubmitBtn_Click" runat=server/>   
<p>   
<span id="Message" runat="server"/>   
  
<p>   
  
  
<ASP:DataGrid id="MyDataGrid" runat="server"   
AutoGenerateColumns="True"   
Width="100%"   
BackColor="White"   
Border="1"   
BorderWidth="1"    
CellPadding="1"   
CellSpacing="1"   
Font-Size="10pt"   
HeaderStyle-BackColor="White"   
HeaderStyle-ForeColor="Blue"   
AlternatingItemStyle-BackColor="White"   
AlternatingItemStyle-ForeColor="Black"   
ShowFooter="false"   
/>   
  
</form>   
  
</body>   
</html>

How It Works

Pretty cool, huh? Now, aside from the common data accessing and Web server control usage, I'll outline some specifics. By virtue of creating our proxy class with a namespace in Step 2, we need to import it to the dll we created in Step 3, like this:

<%@ Import Namespace="WService" %>

We then instantiate it or reference our object:
[C#]

GetInfo supInfo = new GetInfo();

This is our Web Service class. We then retrieve our dropdown list parameter, and pass it to our ShowSuppliers constructor method like so:

[C#]
string Catg = DropDown1.SelectedItem.Text;   
  
DataSet MyData = supInfo.ShowSuppliers(Catg);

Step 5 - Show it Off

XML Web Service discovery, called DISCO for short, is Microsoft's Web Services Discovery tool. It "discovers" the URL for a Web Service and saves that information in a file on your local server. Here's how:

disco http://localhost/suppliers.asmx?DISCO

This creates a static discovery file containing a WSDL file with all the important information pertaining to your Web Service. Opposite this static discovery file is a dynamic discovery .vsdisco file, which notes all the available Web Services located within that URL.

Remember from Step 2 the WSDL command for creating the source file with "?WSDL"? Now, for an already "discovered" file, you can use:

wsdl.exe /l:CS /n:WService /out:bin/GetSuppliers.cs     
 http://localhost/suppliers.wsdl

to create your source file and, in turn, your DLL for Web Service access.

When you examine the created .disco file you'll notice it includes the following information:

<contractRef ref="http://localhost/suppliers.asmx?wsdl"     
 docRef="http://localhost/suppliers.asmx"     
 xmlns="http://schemas.xmlsoap.org/disco/scl/" />

This contains the link to your Web Service so others can discover it, alongside XSD schemas, and service descriptions. Using this information others can parse the WSDL document, build a proxy source file, and implement this Web Service locally!

If you do have something you'd like to share, run over to the Microsoft UDDI (Universal Description, Discovery and Integration) Business Registry (UBR) node at http://uddi.microsoft.com, which is currently supported by Microsoft, IBM and Ariba. Here you can find other available Web Services and their details, register your Web Service, and discover more about this whole business.

A little "Asynchronousity"

Before, I conclude this article I need to mention an additional method of calling Web Services from your application. Web Services by default form are called "synchronously". As a result, both the application and its inherent Web Service will have to finish simultaneously. As such, a quicker method within the application will have to wait for any slower one to finish, before the application can fully display its results, potentially creating an unnecessary delay for the client. Likewise, since Web Service calls make use of Port 80 for communications, they can at times cause this type of delay. How would you be able to remedy such a situation? With a little asynchronousity, of course!

There are four common ways to make asynchronous method calls. Here we will demonstrate waiting for our asynchronous call utilizing the WaitHandle method, which determines when the service call is complete. The other three possible methods for asynchronous calls include:

  1. The IsCompleted property of the IasyncResult returned by BeginInvoke, within ourBeginSuppliers Method

  2. Executing a callback method when the asynchronous call finishes

  3. Finally, parallel any processing or code until the call completes prior to calling EndInvoke

Given that asynchronous calls can perform a given task without affecting other functions around them, let's implement our chosen technique. Going back to Step 2, you'll remember how we created our proxy class file for our .asmx Web Service file.

If you took the time to inspect it, you will have noticed that, aside from our main ShowSuppliers Dataset function, two additional functions were listed: BeginShowSuppliers and EndShowSuppliers. These are the asynchronous functions that will enable us to produce asynchronous Web Service calls implementing the WaitHandle Class'sWaitOne() Method, and here's how.

We do all this from the .aspx page we created in Step 4. Here's the additional code:

//Instantiate DLL    
GetInfo supInfo = new GetInfo();    
   
// Begin the Asynchronous call to ShowSuppliers    
IAsyncResult AsyncWS = supInfo.BeginShowSuppliers(Catg, null, null);    
   
//We wait for the callback    
AsyncWS.AsyncWaitHandle.WaitOne();    
   
//We return the data bypassing the initial ShowSuppliers Method    
DataSet MyData = supInfo.EndShowSuppliers(AsyncWS);

Notice that just a few more lines of code were added. The only thing that changed was that we added our asynchronous methods right after we instantiated our dll. After this, we create an instance of the IasyncResultobject that will let us know when the Web Service process has finished.

IAsyncResult AsyncWS = supInfo.BeginShowSuppliers(Catg, null, null);

Here, we called the asynchronous BeginShowSuppliers method that accepts our initial dropdown list parameter and two other mandatory arguments that, in this example, were not included, and were substituted with null. The second argument would typically call the AsyncCallBack object responsible for calling another method once theBeginShowSuppliers completes, contrary to our example. The third argument would contain information about the asynchronous operation.

We then next implement the AsyncWaitHandle that allows us to incorporate and handle different kinds of synchronization techniques. There are a few AsyncWaitHandles available to us. With the WaitOne method included below, we wait for the WaitHandle to receive a callback signal. Further available methods include: WaitAll(), which waits for all elements to receive a callback signal, and WaitAny(), which waits for any one of the elements to receive a callback signal. Both utilize specified arrays as one of the overloaded element arguments, alongside an Integer for specific time interval waiting, and/or Timespan as well. In any event, all WaitHandles have available to them multiple overload forms that can be further viewed in the .NET SDK documentation.

//We wait for the default callback    
AsyncWS.AsyncWaitHandle.WaitOne();

A quick digression regarding the line above: although our WaitOne method as shown in default form will work fine, it can be overloaded. To illustrate, our WaitOne Handler, once overridden in a derived class, will block the current thread until the current WaitHandle receives a signal. The two arguments it allows, paralleling what the other two Waitmethods also accept, are:

  1. waiting for a set number of milliseconds to pass, and 
  2. a Boolean value, true or false, specifying whether to exit the synchronization domain before the wait.

Once the wait is over, we return our results via the EndShowSuppliers method that got the OK from theWaitHandle.

The AsyncWaitHandle method being a resource releaser, OK'd our supInfo.EndShowSuppliers method below, and in turn obtained our data, without letting the rest of our application grow impatient.

//We return the data    
DataSet MyData = supInfo.EndShowSuppliers(AsyncWS);

Therefore, in coping with the possible speed limitations resulting from HTTP network traffic, we can implement our Web Service without any concern that the rest of our application will wait impatiently until it's all completed. With some asynchronous"ity," our application will run its entire course, in conjunction with our possibly leisurely Web Service, simply jumping in when it's finished.

In summing up, our BeginShowSuppliers method, once initialized, returns instantly (though not in this case, as we used the WaitHandler) to notify your applicable callback function that the process is done, and it's OK to call theEndShowSuppliers method and return the results.