Learn ASP.NET MVC, ASP.NET Core, and Design Patterns through our online training programs. Courses conducted by Bipin Joshi on weekends. Read more details here.

Developing 3 tier web sites in ASP.NET 2.0

Introduction

Many months back I wrote a series of articles that explained in plain words N-tier application development. The series can still be found at:

Then came .NET 2.0. Many things changed especially for ASP.NET. So I decided to write a sequel to the earlier series specifically for ASP.NET 2.0. This article will explain how a typical 3-tier architecture can be used in ASP.NET 2.0 with the help of Generics based collections and Data Source Controls. Note that the article aims at showing a simple 3 tier architecture and not at teaching you generics, data bound controls and data source controls.

What are Generic collections?

.NET 1.x provided many collection classes such as ArrayList, NameValueCollection, HashTable, Queue and Stack. All of them were designed to store objects. Have a look at the following code:

Dim arr As New ArrayList
Dim c As New Customer
...
arr.Add(c)

The code creates a new instance of ArrayList and a class called Customer. The Customer instance is then added to the ArrayList. Now the problem is ArrayList stores all the items as of type Object and not of a specific type. That means while retrieving the any element back you need to write:

c = CType(arr(0), Customer)

That means you must type case the element to the actual class type. This also means some performance penalty. If you are thinking it to be reasonable then have a look at the next example.

Dim arr As New ArrayList
Dim c As New Customer
arr.Add(c)
arr.Add("Hello World")
arr.Add(100)

Here the code adds an instance of Customer class, a string and an integer to the ArrayList. You will agree that errors can easily crop up while retrieving the values back.

Because of these reasons Microsoft added a special Generics based collection classes. These classes are available in System.Collections.Generic namespace. Some of the classes from this namespace are List, SortedList, Queue and Stack. You can of course create your own generic collection classes. The same code can be written as shown below:

Dim arr As New List(Of Customer)
Dim c As New Customer
arr.Add(c)
...
c = arr(0)

As you can see the code declares a new instance of List but it is not just List it is List of Customers. That means you can store only Customer instances in the List. Later on when you retrieve the values back you need not perform any typecasting. any attempt to store anything other than Customer objects will result in compile time error. Thus generic collections are better from performance as well as usage point of view.

Architecture of our sample 3 tier application

A three tiered application need not be necessarily distributed. Than means various layers (Data Access Layer, Business Logic Layer and User Interface Layer) can be within a single web site. You can of course separate out individual layers and host them on a dedicated server if you so wish. In such cases you may need to use Remoting or Web Services.

The components in a three tired system are generally implemented in a class library. However in ASP.NET 2.0 there is one more alternative. The special folder App_Code is intended to store the classes used by your web site. You can neatly organize the classes inside the App_Code folder by creating subdirectories.

In ASP.NET 1.x you would have used either DataSet or ArrayList kind of collection to pass data from one layer to the other. In ASP.NET 2.0 you can use DataSet in the same way as in 1.x. However, instead of using ArrayList you can go for generic List class.

In ASP.NET 1.x the user interface layer consists of web forms. You need to assemble various server controls on the form and manually data bind them with the underlying data source. Tasks such as inserting, updating and deleting data were all manual i.e. you need to write code to accomplish them. In ASP.NET 2.0 new data bound controls such as GridView, DetailsView and FormView along with Data Source Controls come handy. They allow you to design the UI with minimal coding.

To summarize the architecture of our sample application will be like this:

  • A class called SqlHelper will act as a Data Access Layer and will reside in a subfolder called DAL inside the App_Code folder. This class will take the data in and out of Customers table of Northwind database
  • A class called Customer will act as a Business Logic Layer and will be placed in a subfolder called BOL inside the App_Code folder
  • A UP will consist of a web form. We will use Object Data Source and DetailsView control for adding, updating and deleting data

Developing the sample application

Let's begin our development. First of all create a new web site using VS.NET 2005. Open the solution explorer, right click on the web site and choose Add ASP.NET Folder option. Select App_Code folder so as to create the App_Code folder. Add two subfolders called DAL and BOL to the App_Code folder. Add a class called SqlHelper in the DAL folder. Similarly add a class called Customer in the BOL folder. Your folder structure should resemble as shown below:

Open the web.config file and add a connection string as shown below:

<connectionStrings>
<add name="connectionstring" connectionString="data source
=.\sqlexpress;initial catalog=northwind;integrated security
=true" providerName="System.Data.SqlClient"/>
</connectionStrings>

The <connectionStrings> section is used to store database connection strings. In our example the connection string points to Northwind database from a SQL Server Express database.

Creating Data Access Layer

The SqlHelper class represents our Data Access Layer. Open SqlHelper.vb and key in following code in it:

Public Class SqlHelper
Private Shared connectionstring As String

Shared Sub New()
connectionstring = ConfigurationManager.ConnectionStrings
("connectionstring").ConnectionString
End Sub

Public Shared Function ExecuteNonQuery(ByVal sql As String, 
ByVal params() As SqlParameter) As Integer
Dim cnn As New SqlConnection(connectionstring)
Dim cmd As New SqlCommand(sql, cnn)
For i As Integer = 0 To params.Length - 1
cmd.Parameters.Add(params(i))
Next
cnn.Open()
Dim retval As Integer = cmd.ExecuteNonQuery()
cnn.Close()
Return retval
End Function

Public Shared Function ExecuteDataSet(ByVal sql As String) 
As DataSet
Dim ds As New DataSet
Dim da As New SqlDataAdapter(sql, connectionstring)
da.Fill(ds)
Return ds
End Function

Public Shared Function ExecuteDataSet(ByVal sql As String, 
ByVal params() As SqlParameter) As DataSet
Dim ds As New DataSet
Dim da As New SqlDataAdapter(sql, connectionstring)
For i As Integer = 0 To params.Length - 1
da.SelectCommand.Parameters.Add(params(i))
Next
da.Fill(ds)
Return ds
End Function
End Class

The SqlHelper class consists of several shared methods. In the shared constructor the class reads the connection string that we stored in the web.config file previously. In order to read the connection string the code uses ConfigurationManager class.

The ExecuteNonQuery() method is designed for action queries such as INSERT, UPDATE and DELETE. The overloads of ExecuteDataSet() method is intended to return a DataSet filled with the required records.

Creating Business Logic Layer

The Customer class represents our Business Logic Layer. Open Customer class and add the following code to it.

Imports System.Data
Imports System.Data.SqlClient
Imports System.Collections.Generic

Public Class Customer
Private strID As String
Private strCompany As String
Private strContact As String
Private strCountry As String

Public Property CustomerID() As String
Get
Return strID
End Get
Set(ByVal value As String)
strID = value
End Set
End Property

Public Property CompanyName() As String
Get
Return strCompany
End Get
Set(ByVal value As String)
strCompany = value
End Set
End Property

Public Property ContactName() As String
Get
Return strContact
End Get
Set(ByVal value As String)
strContact = value
End Set
End Property

Public Property Country() As String
Get
Return strCountry
End Get
Set(ByVal value As String)
strCountry = value
End Set
End Property

Public Shared Function Insert(ByVal c As Customer) As Integer
Dim sql As String
sql = "insert into customers(customerid,companyname,
contactname,country) values(@custid,@company,@contact,@country)"
Dim params(3) As SqlParameter
params(0) = New SqlParameter("@custid", c.CustomerID)
params(1) = New SqlParameter("@company", c.CompanyName)
params(2) = New SqlParameter("@contact", c.ContactName)
params(3) = New SqlParameter("@country", c.Country)
Return SqlHelper.ExecuteNonQuery(sql, params)
End Function

Public Shared Function Update(ByVal c As Customer) 
As Integer
Dim sql As String
sql = "update customers set companyname=@company,contactname
=@contact,country=@country where customerid=@custid"
Dim params(3) As SqlParameter
params(0) = New SqlParameter("@company", c.CompanyName)
params(1) = New SqlParameter("@contact", c.ContactName)
params(2) = New SqlParameter("@country", c.Country)
params(3) = New SqlParameter("@custid", c.CustomerID)
Return SqlHelper.ExecuteNonQuery(sql, params)
End Function

Public Shared Function Delete(ByVal c As Customer) 
As Integer
Dim sql As String
sql = "delete from customers where customerid=@custid"
Dim params(0) As SqlParameter
params(0) = New SqlParameter("@custid", c.CustomerID)
Return SqlHelper.ExecuteNonQuery(sql, params)
End Function

Public Shared Function SelectAll() As List(Of Customer)
Dim ds As DataSet = SqlHelper.ExecuteDataSet
("select customerid,companyname,contactname,
country from customers")
Dim arr As New List(Of Customer)
For Each row As DataRow In ds.Tables(0).Rows
Dim c As New Customer
c.CustomerID = row("customerid")
c.CompanyName = row("companyname")
c.ContactName = row("contactname")
c.Country = row("country")
arr.Add(c)
Next
Return arr
End Function

Public Shared Function SelectSingle(ByVal custid As String) 
As Customer
Dim params(0) As SqlParameter
params(0) = New SqlParameter("@custid", custid)
Dim ds As DataSet = SqlHelper.ExecuteDataSet
("select customerid,companyname,contactname,country from 
customers where customerid=@custid", params)
Dim row As DataRow = ds.Tables(0).Rows(0)
Dim c As New Customer
c.CustomerID = row("customerid")
c.CompanyName = row("companyname")
c.ContactName = row("contactname")
c.Country = row("country")
Return c
End Function
End Class

The Customer class consists of four public properties and five public shared methods. The method names are self-explanatory. The methods essentially manipulate data from Customers table of Northwind database.

Note the code marked in bold. We have imported System.Collections.Generic namespace because we want to return records as collection of Customer objects. Have a look carefully at SelectAll() method. The method returns a List of Customers. Inside we have declared a variable (arr) of type List. The SelectAll() method calls ExecuteDataSet() method of the SqlHelper class and retrieves a DataSet filled with required records. Then the method iterates through all the records from the DataSet and with each iterations add a Customer object to the List.

Creating the User Interface

The UI consists of a single web form. Open the default web form in the VS.NET IDE and design it as shown below:

The DropDownList shows a list of all CustomerIDs from the database. Once you select a perticular customer ID its details are displayed in the DetailsView below. The DetailsView is bound with ObjectDataSource1 where as the DropDownList is bound with ObjectDataSource2.

Open the smart tags of ObjectDataSource1 and choose "Configure Data Source...". Doing so will start a wizard as shown below:

Select Customer class from the list and click Next. On the next Wizard step choose methods for selecting, updating, inserting and deleting data. In our case the methods are SelectSingle(), Update(), Insert() and Delete() respectively.

Once you specify the methods click on Next. The next step will ask you the source of the custid parameter of SelectSingle() method. In our case the source control is DropDownList1.

Click on Finish to close the Wizard.

Similarly configure the ObjectDataSource2 to select all the Customers i.e. specify Select method as SelectAll().

We just finished configuring the object data source controls. Running the wizard sets the following important properties of the object data source controls.

Property Description
TypeName This property specifies the name of the class that is acting as a Business Object. In our case it is the Customer class.
DataObjectTypeName This property indicates the object that is being passed as a parameter to Insert, Update and Delete methods. In our case it is the Customer class.
SelectMethod The method from Business Object that is going to fetch data.
InsertMethod The method from Business Object that is going to insert record in the underlying data store.
UpdateMethod The method from Business Object that is going to update record in the underlying data store.
DeleteMethod The method from Business Object that is going to delete record in the underlying data store.

Now open the smart tag of DetailsView and set its data source to ObjectDataSource1. Also, enable inserting, editing and deleting.

Similarly open smart tag of DropDownList1 and set its data source to ObjectDtaSource2. Specify DataTextField and DataValueField properties as CustomerID.

Finally enable AutoPostBack for the DropDownList.

We want that whenever an item is added or deleted the DropDownList should immediately reflect the changes. In order to achieve this you need to handle ItemInserted and ItemDeleted events of DetailsView. These event handlers are shown below

Protected Sub DetailsView1_ItemInserted
(ByVal sender As Object, ByVal e As DetailsViewInsertedEventArgs) 
Handles DetailsView1.ItemInserted
DropDownList1.DataBind()
End Sub

Protected Sub DetailsView1_ItemDeleted
(ByVal sender As Object, ByVal e As DetailsViewDeletedEventArgs)
Handles DetailsView1.ItemDeleted
DropDownList1.DataBind()
End Sub

The event handlers simply call the DataBind() method of the DropDownList which causes it to repopulate itself.

That's it. Run the web form and test editing and deleting functionality for different customers. The following figure shows the web form in action.




Bipin Joshi is a software consultant, trainer, author and a yogi having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, AngularJS, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Ajapa Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 25 Aug 2006



Tags : ASP.NET Architecture Deployment