Building a 3-Tier Application Using ASP.Net

This article explains 3-Tier Architecture and the implementation in an ASP.NET example.

Abstract

This article has two major parts. In the first part we will explain 3-Tier Architecture and in the second part we will implement an ASP.NET example to practice the 3-Tier design. You need Visual Studio, IIS and a Microsoft SQL Server to follow this article.

Build3tierApp1.jpg
Figure 0: TimeEntry.aspx

Contents 

  1. 3-Tier Architecture
    1. Definition and Motivation
    2. Data Tier
    3. Logical Tier
      1. Business Tier
      2. Data Access Tier
    4. Presentation Tier
  2. Creating a 3-Tier ASP.NET application
    1. Installing the web application TimeManagement
    2. Implementing of Data Tier
      1. Table Person
      2. Table ProjectI
      3. Table ProjectInvolvement
    3. Implementing Logical Tier
      1. Implementing Data Access Tier
      2. Implementing  Business Tier
    4. Implementing Presentation Tier
    5. Conclusion
    6. Reference

1. 3-Tier Architecture


1.0  Definition and motivation

A 3-tier application is organized into three major disjunctive (independent) tiers. These tiers are:
  • Presentation Tier (Front end)
  • Logical Tier (Middleware)
  • Data Tier (Back end).

Each layer can be deployed in separate computers in a network. Some architects divide the Logic Tier into two sub-tiers, Business and Data Access Tiers, to increase scalability and transparency. The tiers can be deployed on physically separated machines. The characteristics of the tier communication is that the tiers will communicate only with their adjacent neighbors. For an example, the Presentation Tier will interact directly with the Business Tier and not directly with Data Access or Data Tiers.

 Build3tierApp4.jpg 
Figure 1  (A typical 3-Tier Architecture)

The Figure 1 shows a typical 3-Tier Architecture scenario. I think we should look back at the history of computing to understand the advantages of 3-Tier Architecture.

Mainframes ruled the IT landscape until the mid 1980s. The main characteristic of a Host Architecture is that the application and databases reside on the same host computer and the user interacted with the host using an unfriendly and dumb terminal. This monolith architecture does not support distributed computing (the host applications are not able to connect with a database of a strategically allied partner). Some mangers found that developing a host application took too long and was too expensive. Consequently these disadvantages led to a Client-Server (C/S) architecture.

In fact, a Client Server (C/S) architecture is a 2-Tier architecture because the client does not distinguish between the Presentation Tier and the Logic Tier. That is why we call this type of client a Fat Client. The increasing demands on GUI controls caused difficulty in managing the mixture of source code from GUI and Business Logic (Spaghetti Code). Further, C\S Architecture does not support enough Change Management. Let us suppose that the government increases the consumer tax rate from 14% to 16%, then in the C\S case, you need to send an update to each client and they must update synchronously on a specific time otherwise you may store corrupt information. The C/S Architecture is also a burden to network traffic and resources. Let us assume that about five hundred clients are working on a data server, then we will have five hundred ODBC connections and several ruffian record sets, which must be transported from the server to the clients (because the Business Logic Tier is situated in the client side). The fact that C/S does not have any caching facilities like in ASP.NET, caused additional traffic in the network. In the late 1990s, designers have shifted the Business Logic from the client to server to elude the handicaps from C/S Architecture. Normally, a server has a better hardware than client therefore it is able to compute algorithms faster than a client, so this fact is also an additional pro argument for the 3-Tier Architecture.   

Now let us return to our 3-Tier Architecture and start to explore the tiers. 

1.1  Data Tier

The Data Tier is responsible for retrieving, storing and updating information, therefore this tier is ideally represented by a commercial database. We consider Stored Procedures as a part of tte Data Tier. Usage of Stored Procedures increases the performance and code transparency of an application.

1.2  Logical Tier

The Logic Tier is the brain of the 3-Tier application. Some architects do not make any distinction between Business Tier and Data Access Tier. Their main argumentation is that additional tiers will degrade down performance. I think that it is more advantageous to separate the Logical Tier into a Business Tier and a Data Access Tier. Some of these advantages are:

  • Increases code transparency
  • Supports changes in Data Layer. You can change or alter a database without touching the Business Layer and this would be a very minimumal touch up.  

1.2.1  Business Tier

This sub-tier consists of classes to calculate aggregated values, such as total revenue, cash flow and ebit and this tier doesn't know about any GUI controls and how to access databases. The classes of a Data Access Tier will supply the needy information from the databases to this sub-tier.

1.2.2  Data Access Tier

The Data Access Tier acts as an interface to the Data Tier. This tier knows how to (from which database) retrieve and store information.

1.3  Presentation Tier

The Presentation Tier Tier is responsible for communication with the users and web service consumers and it will use objects from the Business Layer to respond to GUI raised events.

After this brief theory, I think we should move now to the practical part. Our purpose is to develop a work diary for employees, in which they can record daily project activities.

2. Creating a  3-Tier ASP.NET application


You need SQL Server, IIS and Microsoft .NET CLR to run the example application. Use the following procedure to create the sample ASP.NET application. 

2.1  Installing the web application Timemanagement

Use the following procedure to install the web application TimeManagement on your machine.

  1. Create a new SQL Server database with the name TimeManagement and execute the file TimeManagement.sql (included in the Zip file) using the tool SQL Query Analyzer to create the  necessary tables and Stored Procedures for this application.

  2. Create an ASP.Net Appliaction "TimeManagement" and replace it with the file TimeManagement that you find in the .zip file.

  3. Adjust the  XML Element <appsettings> in the Web.config file to establish a SQL connection. (Modify the value from Sqlconnection.)
    1. <appSettings>  
    2. <addkeyaddkey="SqlConnect" value="server=F5;database=TimeManagement;uid=sa;pwd=moses;" />  
    3. </appSettings>
  4. Set the Page LogIn.aspx as the start page.  

I hope now that you can run the web application.

2.2  Implementing of Data Tier

This tier is represented by the SQL Server database TimeManagement and it has 3 tables. Figure 2 shows the ERD diagram of  the database TimeManagement. I will now describe the tables briefly.

Build3tierApp5.jpg

Figure 2

2.2.1  Table Person

This table stores information about employees. The attribute PersID is the primary key of this table and the database will increment this value automatically during insertion of a new data row. The values of the attribute Email correspond with the values of the attribute PersID. In order to obtain this relationship, the application must keep the values of the attribute Email unique. We have implemented this rule in the Stored Procedure InsertPerson (see Listing 1) that inserts a new record. 

  1. CREATE PROCEDURE InsertPerson  
  2. (  
  3. @Name  char(50),  
  4. @CName char(50),  
  5. @WeekHour int,  
  6. @Password  
  7. char(50),                                                                                                                      
  8. @EMail  char(50),  
  9. @AlreadyIn int out  
  10. )  
  11.  AS  
  12. SELECT @AlreadyIn=COUNT(*) FROM Person WHERE EMail=@EMail   
  13.   
  14. IF @AlreadyIn=0  
  15. INSERT INTO Person   
  16. (Name ,CName ,WeekHour ,Password ,EMail )  
  17. VALUES  
  18. (@Name ,@CName ,@WeekHour ,@Password ,@EMail  )  
  19. GO  
Listing 3

2.2.2  Table Project

This table stores information about the projects of a firm. The attribute ProjID is the key of this table and it will be automatically incremented by the database during the insertion of a new row. The attribute Leader is a foreign key of the table Person. 

2.2.3  Table  ProjectInvolvement

This table contains information to answer questions, such as: how many hours have been spent by employee X in the project P on a specific day? The key attributes of this table are EntryDate, ProjID and PersID. The attribute ProjID is a foreign key of the Table Project and the attribute is PersID is a foreign key of the table Person.

Build3tierApp6.gif

Figure 4 ( partial class diagram of the application TimeManagement)

2.3 Implementing Logical Tier 

2.3.1 Implementing Data Access Tier

All classes of the Data Access Tier are derived from the super class DABasis (see Figure 4), that is responsible for establishing a database connection.

  1. <appSettings>  
  2. <addkeyaddkey="SqlConnect" value="server=F5;database=TimeManagement;uid=sa;pwd=moses;" />  
  3. </appSettings>   
Listing 5 (partial source code from Web.config) 
  1. /// <summary>  
  2. /// This is the super class for Data Access Classes  
  3. /// </summary>  
  4. class DABasis  
  5. {  
  6.     protected static string strConnect;  
  7.     public DABasis()  
  8.     {  
  9.     }  
  10.     /// <summary>  
  11.     /// Please see the web.config file  
  12.     /// </summary>  
  13.     static DABasis()  
  14.     {  
  15.         strConnect = ConfigurationSettings.AppSettings["SqlConnect"];  
  16.     }  
  17.     /// <summary>  
  18.     /// Gets a SqlConnection to the local sqlserver  
  19.     /// </summary>  
  20.     /// <returns>SqlConnection</returns>   
  21.     protected SqlConnection GetConnection()  
  22.     {  
  23.         SqlConnection oConnection = new SqlConnection(strConnect);  
  24.         return oConnection;  
  25.     }  
  26. }  
Listing 6  (class DABasis)

We have stored the global application attributes, such as the string SqlConnect in the configuration file Web.config and you can retrieve this value using the sealed class ConfigurationSettings (see Listing 6: static DABasis()).

Now to show an exemplary typical data access method of a DataAccess class to retrieve a Dataset or insert or update some data rows. In our implementation we distinguish two types of Data Access methods, they are:

  • Query Data Access Method: used typically to retrieve data structures like DataSet or DataTable from tables.

  • Non Query Data Access Method: used typically to update a table or insert a data row in to a table. 

At first, we will look at a Query Data Access Method. The class DAPInvolvement wraps a bundle of data access methods that deal with the matter project involvement. The method void Dataset DAPInvolvement.GetDayRecord(int nPersID,DateTime dtEntry) (see Figure 8) will return a dataset containing all project activities of a person with the ID PersID on a specific day dtEntry This method uses the Stored Procedure GetDayRecord (see Figure 7) to retrieve essential data from the tables ProjectInvolvement and Project.

  1. CREATE PROCEDURE   GetDayRecord    
  2. (  
  3. @PersID int,  
  4. @EntryDate datetime  
  5. )  
  6. AS  
  7. SELECT  P.Name, P.ProjID, PI.Duration  
  8. FROM ProjectInvolvement PI , Project P  
  9. WHERE PI.PersID= @PersID and PI.ProjID=P.ProjID and PI.EntryDate=@EntryDate   
Listing 7  (Store Procedure  GetDayRecord)
  1. /// <sumary>  
  2. /// gives the list of activities of the (person)ID for the particular EntryDate  
  3. /// </summary>  
  4. /// <param name="nPersID">PersID attribute of ProjectInvolvement</param>  
  5. /// <param name="dtEntry">EntryDate attribute of ProjectInvolvement</param>  
  6. /// <returns>DataSet and the table name is "dtDayRecord" </returns>   
  7. public DataSet GetDayRecord(int nPersID, DateTime dtEntry)  
  8. {  
  9.     SqlConnection oConnection = GetConnection();  
  10.     // build the command  
  11.     SqlCommand oCommand = new SqlCommand("GetDayRecord", oConnection);  
  12.     oCommand.CommandType = CommandType.StoredProcedure; // Parametrs  
  13.     SqlParameter paraPersID = new SqlParameter("@PersID", SqlDbType.Int, 4);  
  14.     paraPersID.Value = nPersID;  
  15.     oCommand.Parameters.Add(paraPersID);  
  16.     SqlParameter paraEntryDate =  
  17.     new SqlParameter("@EntryDate", SqlDbType.DateTime);  
  18.     paraEntryDate.Value = dtEntry;  
  19.     oCommand.Parameters.Add(paraEntryDate);  
  20.     // Adapter and DataSet  
  21.     SqlDataAdapter oAdapter = new SqlDataAdapter();  
  22.     oAdapter.SelectCommand = oCommand;  
  23.     DataSet oDataSet = new DataSet();  
  24.     try  
  25.     {  
  26.         oConnection.Open();  
  27.         oAdapter.Fill(oDataSet, "dtDayRecord"); return oDataSet;  
  28.     }  
  29.     catch (Exception oException)  
  30.     {  
  31.   
  32.         throw oException;  
  33.     }  
  34.     finally  
  35.     {  
  36.         oConnection.Close();  
  37.     }  
  38. }  

Listing 8   (The method DAPInvolvement.GetDayRecord)

A typical Query Data Access method might be abstractly described as:

  • Establishes a SqlConnection.
  • Creates a SqlCommand and necessary SqlParameters to the command.
  • Creates a DataSet and a SqlDataAdapter.
  • Opens the connection and fills the DataSet using the SqlDataAdapter.
  • Closes the SqlConnection. 

Some of you may ask the question, why are we using a DataSet instead of a SqlDataReader. Indeed, you can retrieve data rows faster using a SqlDataReader than a Dataset, but if you want to use a WebService then you ought to use a DataSet because it is not possible to transmit a SqlDataReader using SOAP protocol. You can transmit via SOAP all the objects that belong to the types:

  • DataSet (ADO.NET)
  • Complex Arrays
  • XML nodes

I want to now show a typical Non-Query Data Access method. The DataAccess method:

  1. public void DAProject.Insert(string strName,string strDescription,int nLeader,out int nAlreadyIn)   
(see Figure  10) inserts a new project into the database and it uses the Stored Procedure InsertProject.

(see Figure 9). The out parameter of this method out int nAlreadyIn serves as a flag to the classes of the Business Logic Tier, whether the record is inserted by this method or not.
  1. CREATE PROCEDURE InsertProject  
  2. (  
  3. @Name  char(50),  
  4. @Description char(150),  
  5. @Leader int,  
  6. @AlreadyIn int output  
  7. )  
  8. AS   
  9. SELECT @AlreadyIn = Count(*)  From Project WHERE Name=@Name  
  10. IF  @AlreadyIn =0  
  11. INSERT INTO Project  
  12. (Name,Description,Leader)   
  13. VALUES  
  14. (@Name,@Description,@Leader)  
  15. GO    
Listing 9 (store procedure InsertProject)
  1. /// <summary>  
  2. /// inserts a new data row into the table "project"  
  3. /// </summary>  
  4. /// <param name="Name"></param>  
  5. /// <param name="Description"></param>/// <param name="Leader">a foreign key   
  6. from Person</param>  
  7. /// <param name="AlreadyIn">number of records which fulfill the term "Name=strName"   
  8. efore the Insertation</param>  
  9. public void Insert(string strName, string strDescription, int nLeader,  
  10. out int nAlreadyIn)  
  11. {  
  12.     // Establish Connection  
  13.     SqlConnection oConnection = GetConnection();  
  14.     // build the command  
  15.     SqlCommand oCommand = new SqlCommand("InsertProject", oConnection);  
  16.     oCommand.CommandType = CommandType.StoredProcedure;  
  17.     // Parameters  
  18.     SqlParameter paraName = new SqlParameter("@Name", SqlDbType.Char, 50);  
  19.     paraName.Value = strName;  
  20.     oCommand.Parameters.Add(paraName);  
  21.     SqlParameter paraDescription = new SqlParameter("@Description", SqlDbType.Char, 150);  
  22.     paraDescription.Value = strDescription; oCommand.Parameters.Add(paraDescription);  
  23.     SqlParameter paraLeader = new SqlParameter("@Leader", SqlDbType.Int); paraLeader.Value = nLeader;  
  24.     oCommand.Parameters.Add(paraLeader);  
  25.     SqlParameter paraAlreadyIn = newSqlParameter("@AlreadyIn", SqlDbType.Int);  
  26.     paraAlreadyIn.Direction = ParameterDirection.Output;  
  27.     oCommand.Parameters.Add(paraAlreadyIn);  
  28.     try  
  29.     {  
  30.         oConnection.Open();  
  31.         oCommand.ExecuteNonQuery();  
  32.         nAlreadyIn = (int)paraAlreadyIn.Value;  
  33.     }  
  34.     catch (Exception oException)  
  35.     {  
  36.   
  37.         throw oException;  
  38.     }  
  39.     finally  
  40.     {  
  41.         oConnection.Close();  
  42.     }  
  43. }  

Listing 10 (Method DAProject.Insert)

A typical Non-Query Data Access method might be described abstractly as:

(see Figure 10)  

  • Establishes a SqlConnection.
  • Creates a SqlCommand and the SqlParameters to the command.
  • Opens the connection and executes the query.
  • Retrieves the values from all output parameters.
  • Closes the SqlConnection.

  1. public class BLBasis  
  2. {  
  3.     // Current HttpContext  
  4.     protected HttpContext oCurrentContext;  
  5.     public BLBasis()  
  6.     {  
  7.         oCurrentContext = HttpContext.Current;  
  8.     }  
  9.     /// <summary>  
  10.     /// returns true, if the web client authorized or not  
  11.     /// </summary>   
  12.     public bool IsAuthenticated  
  13.     {  
  14.         get  
  15.         {  
  16.             return oCurrentContext.User.Identity.IsAuthenticated;  
  17.         }  
  18.     }  
  19.     /// <summary>  
  20.     /// returns the UserID,if the user already authorized  
  21.     /// </summary>  
  22.     public int UserId  
  23.     {  
  24.         get  
  25.         {  
  26.             if(IsAuthenticated)  
  27.             {  
  28.                 string strHelp = oCurrentContext.User.Identity.Name;  
  29.                 return Int32.Parse(strHelp);  
  30.             }  
  31.             else  
  32.             {  
  33.                 return -1;  
  34.             }  
  35.         }  
  36.     }  
  37. }  
Listing 11 (class BLBasis)

2.3.2  Implementing Business Tier

All classes of the Business Tier have the super class BLBasis (Figure 11) and it will supply its derived classes session relevant information, such as UserID. The web application uses the attribute UserID  to identify the current user. We use the method public static void FormsAuthentication.Redirect-FromLoginPage(string userName, bool createPersistentCookie) to assign the user identity into the current instance of the HttpContext class.

Let us now analyze a class of this tier in order to understand the pattern. The class BLPInvolvement is a Business Logic class and gathers all interrelated methods, that deal with the topic project involvement. The method public void BLPInvolvement.GetDayRecord(DateTime dtEntry,out double dTotal out DataSet dsDayRecord) (see Figure 12) is responsible for passing a Dataset and a numeric value to the Presentation Layer.  

  1. /// <summary>  
  2. /// get all records   
  3. /// </summary>  
  4. /// <param name="dtEntry"></param>  
  5. /// <param name="dTotal"></param>  
  6. /// <param name="dsDayRecord"></param>  
  7. public void GetDayRecord(DateTime dtEntry, out double dTotal, out DataSet dsDayRecord)  
  8. {  
  9.     dTotal = 0;  
  10.     dsDayRecord = null;  
  11.     try  
  12.     {  
  13.         DAPInvolvement oDAPInvolvement = new DAPInvolvement();  
  14.         dsDayRecord = oDAPInvolvement.GetDayRecord(this.UserId, dtEntry);  
  15.   
  16.         // calculate the total duration  
  17.         DataTable dtDayRecord = dsDayRecord.Tables["dtDayRecord"];  
  18.         if (dtDayRecord.Rows.Count > 0)  
  19.         {  
  20.             dTotal = (double)dtDayRecord.Compute("Sum(Duration)""Duration>-0.0");  
  21.         }  
  22.     }  
  23.     catch (Exception oException)  
  24.     {  
  25.         throw oException;  
  26.     }  
  27.   
  28. }  
Listing 12 ( class BLPInvolvement)

A typical Business Logic method might be described abstractly like this:

  • Instantiates a Data Access object
  • Retrieves the crude data
  • Calculates business values from the crude data

2.4  Implementing Presentation Tier

We have used ASP.NET to implement the Presentation Layer. Now to show you an exemple, how the Presentation Layer communicates with the Data Access Layer. Figure 0 shows the web side  TimeEntry.aspx, where an employee can record his project activities for a certain day. The method  private void TimeEntry.btnEnter_Click(object sender, System.EventArgs e) is a callback method that will be activated if the user pushes the Enter button.

  1. /// <summary>  
  2. /// this method populates datagrid dgSummary  
  3. /// </summary>   
  4. void PopulateDataGrid(DateTime dtEntry)  
  5. {  
  6.     try  
  7.     {  
  8.         // retrive DataSet and bind to the datagrid  
  9.         BLPInvolvement oBLPInvolvement = new BLPInvolvement();  
  10.         DataSet oDataSet;  
  11.         double dTotalDuration;  
  12.         oBLPInvolvement.GetDayRecord(dtEntry, out dTotalDuration, out oDataSet);  
  13.         DataTable dtDayRecord = oDataSet.Tables["dtDayRecord"];  
  14.         if (dtDayRecord.Rows.Count > 0)  
  15.         {  
  16.             dgSummary.DataSource = dtDayRecord;  
  17.             dgSummary.DataBind();  
  18.             lbDGTitel.Text = "Date: " + dtEntry.ToShortDateString()  
  19.             + " Sum: " + dTotalDuration.ToString();  
  20.         }  
  21.         else  
  22.         {  
  23.             dgSummary.DataSource = null; dgSummary.DataBind();  
  24.   
  25.             lbDGTitel.Text = "No Records found";  
  26.         }  
  27.     }  
  28.     catch (Exception oException)  
  29.     {  
  30.         this.HelpException(oException);  
  31.     }  
  32. }  
  33. /// <summary>/// It is used publish exception text  
  34. /// </summary>  
  35. /// <param name="oException"></param>   
  36. private void HelpException(Exception oException)  
  37. {  
  38.     if (lbMessage.Text != "")  
  39.     {  
  40.         lbMessage.Text += oException.Message;  
  41.     }  
  42.     else  
  43.         lbMessage.Text = oException.Message;  
  44. }  
Listing 13  (Extract from the class TimeEntry)

Listing 13 shows a partial source code, that is responsible for inserting a new project involvement record. The method takes the following steps to accomplish the task:

  • Draw off the values from GUI controls.
  • Instantiate an object from the Class BLPInvolvement and inserts it into the database.
  • Update the other involved GUI controls.
  • Publishes the error message if an error occurred in the Logic Tier or in the Data Tier. 

2.5  Conclusion

If we look back at implementation phase, we can say that it is quite simple to build a 3-Tier Architecture using Microsoft.NET. I think the following tips are useful to increase transparency and stability of the system:

  • Follow the adjacent rule  (Don't jump over a neighbor tier because it makes us easy to follow systematically from the button click to the database access).

  • Use the Web.config file to define global values.

  • Use try, catch and finally control structures in every tier to track bugs.  

2.6  Reference

Heide Balzert :Objektorientierung in 7 Tagen , Spektrum Akademischer Verlag Heidelberg.Berlin 2000
MSDN.