Implementing the MVC Design Pattern in ASP.NET
 
Published: 31 Jan 2008
Abstract
In this article, Joydip explains the basics of the MVC design pattern, the advantages and the disadvantages of it. He also examines how effectively we can decouple the business logic layer of an application from the UI layer. The article provides source code to illustrate the concepts in C# with relevant explanation.
by Joydip Kanjilal
Feedback
Average Rating: 
Views (Total / Last 10 Days): 93285/ 199

Introduction

Design patterns can help solve complex design problems if they are properly used. The main advantage of using the Model-View-Control (MVC) pattern is decoupling the business and the presentation layers.

MVC Architectural Components

The MVC design pattern is comprised of three major components.

1.    The Model (The Data Layer)

2.    The View (The User Interface Layer)

3.    The Controller (The Business Logic Layer)

Let us now take a look at what each of these components relate to. The Model represents the application's data; the View displays this data to the user and the Controller represents the application's business logic. The Model is that component that maintains the state of the entities. The View is the graphical representation of the data; it displays the current state of the Model in the user interface. Note that you can have more than one view of the same data. The Controller has a major role to play. It handles the user interaction in the presentation layer, manipulates the model and renders the model's data into one or more views. It also applies business rules as and when necessary.

Here is a figure (from an article at C# Corner) that illustrates how each of these components work.

Figure 1: The MVC Architectural Components

Advantages of MVC Pattern

The main objective of the MVC design pattern is separation of concerns. It provides an isolation of the application’s presentation layer that displays the data in the user interface, from the way the data is actually processed. In other words, it isolates the application’s data from how the data is actually processed by the application’s business logic layer. The biggest advantage of the MVC design pattern is that you have a nice isolation of these components/layers and you can change any one of them without the rest being affected. Here is the list of the major advantages of this pattern.

·         It provides a clean separation of concerns.

·         It is easier to test code that implements this pattern.

·         It promotes better code organization, extensibility, scalability and code re-use.

·         It facilitates de-coupling the application's layers.

Disadvantages of MVC Pattern

Even if this pattern comes with a lot of advantages, there are distinct disadvantages too. Firstly, it is too complex to implement and is not suitable for smaller application; rather, implementing this pattern in such applications will have adverse effects in the application's performance and design. In this regard, the MSDN states, "You are building a Web application in Microsoft ASP.NET, and, based on the complexity of your application, you need to separate different aspects of the program to reduce code duplication and to limit the propagation of change." This is where this design pattern fits in.

Well, let us now dig into some code. In the section that follows we will learn how we can implement this pattern in ASP.NET applications.

Implementing a Sample Application

In this section we will design and implement a sample application that makes use of the MVC Design Pattern. Before we delve further, let us take a quick look at the class diagram to understand the classes and the interfaces in the design and how they are related to each other.

Figure 2: The Class Diagram

Here is the list of the interfaces that we will be using in our application.

·         IDataModel: This is the interface that will be implemented by all our data model classes.

·         IView: This is the interface that will be implemented by all views that we design using this architecture.

·         IController: This is the interface which will be implemented by the ControllerBase class.

·         IBusinessEntity: This is the base interface for all business entities.

Listing 1: The IBusinessEntity interface

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
 
public interface IBusinessEntity
{
    Object Key
    {
        get;
        set;
    }
}

Note that the IBusinessEntity interface contains only one property, Key, that contains a unique value that identifies each business entity.

Listing 2: The IDataModel interface

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
 
public interface IDataModel
{
    void Create();
    void Update();
    ArrayList Read();
    void Delete();
    ArrayList Data { get; }
 
    int RecordCount
    {
        get;
    }
}

As you can see, the IDataModel contains the CRUD (Create, Read, Update and Delete) method definitions and two properties that relate to the data that the model holds and the count of the records in the model.

Listing 3: The IView interface

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
 
public interface IView
{
  void Notify(String strMessage);
  void InvalidateView();
}

The IView interface shown above contains two method definitions, the Notify(String strMessage) and the InvalidateView() method. While the former is used to notify the view of any event, the later is used to refresh the contents of the view with the latest data from the model.

Listing 4: The IController interface

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
public interface IController
{
    void Create();
    void Update();
    void Delete(Object objID);
    void Read();
    IDataModel DataModel
    { get; set; }
    IView View
    { get; set; }
}

The IController interface contains the CRUD method definitions and it holds the references to the view and the model. The ControllerBase class, shown next, implements this interface.

Listing 5: The ControllerBase class

using System;
using System.Collections;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;

 

public class ControllerBase : IController
{
    public IView iView;
    public IDataModel iDataModel;
 
    public ControllerBase(IView iView)
    {
        this.iView = iView;
    }
 
    void IController.Create()
    {
        this.iDataModel.Create();
    }
 
    void IController.Update()
    {     
    }
 
    void IController.Read()
    {
        this.iDataModel.Read();
        iView.InvalidateView();
        iView.Notify("Total number of records: " + 
            this.iDataModel.RecordCount.ToString()); 
    }
 
    void IController.Delete(Object objID)
    {     
    }
    public IView View
    {
        get
        {
            return iView;
        }
 
        set
        {
            iView = value;
        }
    }
 
    public IDataModel DataModel 
    { 
        get 
        {
            return this.iDataModel;
        }
 
        set 
        {
            this.iDataModel = value; 
        } 
    }
}

Next, we will take a look at the BaseDataModel class that implements the IDataModel interface shown earlier.

Listing 6: The BaseDataModel class

using System.Collections;
using System.Collections.Generic;
 
public class BaseDataModel : IDataModel
{
    protected ArrayList dataList = new ArrayList();
 
    ArrayList IDataModel.Read()
    {
        return dataList;
    }
 
    ArrayList IDataModel.Data
    {
        get
        {
            return dataList;
        }
    }
 
    void IDataModel.Create()
    {
 
    }
 
    void IDataModel.Update()
    {
 
    }
 
    void IDataModel.Delete()
    {
 
    }
 
    public void AddData(IBusinessEntity obj)
    {
        dataList.Add(obj);
    }
 
    public void RemoveData(IBusinessEntity obj)
    {
        dataList.Remove(obj);
    }
 
    public void Refresh()
    {
        dataList.Clear();
    }
 
    int IDataModel.RecordCount
    {
        get
        {
            return this.dataList.Count;
        }
    }
}

The EmployeeDataModel class extends the above class and implements the same interface; it extends that BaseDataModel class and implements the IDataModel interface.

Listing 7: The EmployeeDataModel class

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
 
public class EmployeeDataModel : BaseDataModel, IDataModel
{
    void IDataModel.Create()
    {
        IBusinessEntity employee = new Employee();
        ((Employee)(IBusinessEntity)employee).Key = "E2008-0001";
        ((Employee)(IBusinessEntity)employee).Name = "Joydip Kanjilal";
        this.AddData(employee);
        employee = (IBusinessEntity)new Employee();
        ((Employee)(IBusinessEntity)employee).Key = "E2008-0002";
        ((Employee)(IBusinessEntity)employee).Name = "Anand Narayaswamy";
        this.AddData(employee);
        employee = (IBusinessEntity)new Employee();
        ((Employee)(IBusinessEntity)employee).Key = "E2008-0003";
        ((Employee)(IBusinessEntity)employee).Name = "Steve Smith";
        this.AddData(employee);
        employee = (IBusinessEntity)new Employee();
        ((Employee)(IBusinessEntity)employee).Key = "E2008-0004";
        ((Employee)(IBusinessEntity)employee).Name = "Michelle Smith";
        this.AddData(employee);
    }
}

And then we have our employee business entity class called EmployeeDO. It implements the IBusinessEntity class shown earlier. Here is the code.

Listing 8: The EmployeeDO class

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
 
public class Employee : IBusinessEntity
{
    private String employeeName;
    private Object key;
 
    public Object Key
    {
        get
        {
            return this.key;
        }
 
        set
        {
            this.key = value;
        }
    }
 
    public String Name
    {
        get
        {
            return this.employeeName;
        }
 
        set
        {
            this.employeeName = value;
        }
    }
}

Note that we will make use of an Object Factory class to instantiate classes. I will show you how we will use this class later; let us take a quick look at the code.

Listing 9: The Generic Object Factory

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
 
public static class ObjectFactory
{
    public static T CreateObject<T>()
    {
        return Activator.CreateInstance<T>();
    }
}

This class is based on generics and contains one static method called CreateObject that accepts a generic type, instantiates the type and the returns the instance to the caller. Here is how we will make use of this class in our application.

Listing 10: Using the Generic Object Factory

IController iController.DataModel = (EmployeeDataModel)
ObjectFactory.CreateObject<EmployeeDataModel>();

Note that you require the necessary cast to assign the returned instance to the appropriate type. This is because the return type of the CreateObject() method is a generic type. Lastly, we will take a look at the presentation layer, i.e., the view that invokes the controller which in turn invokes the other layers to display data in the presentation layer.

We will take a simple view that will contain a repeater control that we will use to display the data (from the model) returned by the controller to it. Here is the markup code.

Listing 11: The Markup code of the View

<form id="form1" runat="server">
    <div>
        <asp:Repeater ID="Repeater1" runat="server">
    <HeaderTemplate>
        <table border="1">
            
           <th>
               <asp:Label id="lblPatientCode" Text="Employee Code" runat="server" />
            </th>
            <th>
               <asp:Label id="lblPatientName" Text="Employee Name" runat="server" />
            </th>
             
    </HeaderTemplate>
    <ItemTemplate>
    <tr>
            <td>
                <%# DataBinder.Eval(Container.DataItem, "Key")%>
            </td>            
 
            <td>
                <%# DataBinder.Eval(Container.DataItem, "Name")%>
            </td>            
        </tr>
    </ItemTemplate>
</asp:Repeater>
    </div>
    <div>
    <asp:Label id="lblMessage" Text="" runat="server" />
    </div>
    </form>

Note that the Label control called lblMessage will be used to display any notification message in the view. Here is the complete code behind source code for our view.

 

Listing 12: The complete source of the view

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
 
public partial class _Default : System.Web.UI.Page,IView 
{
    private IController iController;
 
    protected void Page_Load(object sender, EventArgs e)
    {
        iController = new ControllerBase(this);
        iController.DataModel = (EmployeeDataModel)
            ObjectFactory.CreateObject<EmployeeDataModel>();
        iController.Create();
        iController.Read();
    }
 
    void IView.InvalidateView()
    {        
        Repeater1.DataSource = iController.DataModel.Data;
        Repeater1.DataBind();
    }
 
    void IView.Notify(String message)
    {
        lblMessage.Text = message;
    }
}

When we execute the application, the output is similar to what is shown in the figure below.

Figure 3

Note that the employee records are displayed using the repeater control. The message "Total number of records: 4" is displayed as a notification message. We will now understand how the program works.

In the presentation layer, we instantiate the ControllerBase class and pass the reference of the view to it using the constructor. Then the generic Object factory is used to create an instance of the data model and assign this reference to the DataModel property of the ControllerBase instance just created. In our example, the data model is the EmployeeDataModel class. Next, the Create() method is called on the ControllerBase instance we created earlier. This method in turn calls the Create() method of the EmployeeDataModel that creates a set of records of the Employee business entity and stores these records in an ArrayList instance. Now, we make a call to the Read() method on the ControllerBase instance which in turn calls the Read() method of the data model to retrieve the records we just created. The Read() method of the ControllerBase class then invalidates the view which binds the data retrieved to a repeater control. We are done!

References

Conclusion

In this article we have learned how to implement MVC Design Pattern in our ASP.NET applications. Though the MVC pattern has a lot of benefits, there are certain pitfalls too. It should be noted that choosing the right design pattern when designing and architecting solutions goes a long way in designing high performance, scalable, reusable architectures. Happy reading!



User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2019 ASPAlliance.com  |  Page Processed at 2019-05-26 11:23:19 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search