Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

An innovative architecture to develop web forms - comparison with ASP.NET and MVC

4.60/5 (11 votes)
11 Mar 2010GPL313 min read 2  
The article introduces an innovative approach to develop web forms in enterprise software rather than either ASP.NET or MVC through step by step comparison on development complexity, reusability, performance and maintainability. The approach is implemented as an important UI component of RapidWebDev

Introduction

The article introduces an innovative architecture to develop web forms in enterprise software rather than either ASP.NET or MVC through step by step comparison on development complexity, reusability, performance and maintainability. The architecture is implemented as an important UI component of RapidWebDev which is an open source enterprise software development infrastructure.   

RapidWebDev Uri: http://www.rapidwebdev.org

Catalog of The Article

1. Development Complexity Comparison  

Problems of ASP.NET and MVC development  

The complexity of ASP.NET and MVC development is that we have to write new ASP.NET templates for different web forms although we can summarize the main functionality of web forms are query, list, detail and any other custom operations. But in most case, we don't have the same requirement on query filters, grid columns, detail panel or any other operation panel surface between web forms. So master page doesn't work for such scenarios. So with more code written for different web forms,

1.1. we're hard to consolidate the style of web forms in a system especially in teamwork, e.g. indent, label style, operation notificaton, user interaction behavior etc.

1.2. we have to handle each postback or submit manually. e.g.
  • 1.2.1 in a submit of query, we have to go through each query filter one by one and assemble it with query expression if a query filter is not empty like the code snippet below. The hardwired query expression is not agile for changes. We have to re-compile the solution and deploy it even if we have a small change on a query filter.
C#
// for Linq2SQL or ADO.NET EF case
if (!string.IsNullOrEmpty(this.TextBoxQueryByName.Text.Trim()))
    q += q.Where(user => user.Name.Contains(this.TextBoxQueryByName.Text.Trim()));
    
// for nature T-SQL case
if (!string.IsNullOrEmpty(this.TextBoxQueryByName.Text.Trim()))
{
	string nameParameter = this.TextBoxQueryByName.Text.Trim().Replace("'", "''");
    q += "AND Name LIKE '%" + nameParameter + "%'";
}
  • 1.2.2 when we need to support paging and sorting in grid, we have the similar issue as query. We have to write different handlers for an user submits an either query, paging or sorting. And in the handlers, we also need to assemble query expression.
1.3. we're hard to handle and consolidate UI interaction bebaviors manually like the following simple code snippet. The more complex scenario is when an user selects multiple records in grid and click bulk delete button, a modal confirmation panel should be displayed. In the case, we have to implement the modal panel by JS code. The such code increases the complexity of UI obviously especially in teamwork that each member may implement behaviors in different ways.
C#
// when an user clicks query button, the panel to display detail information for a record should be hidden
protected void OnQueryButtonClick(sender object, EventArgs e)
{
    // TODO: execute query
    Grid.Visible = true;
    DetailPanel.Visible = false;
    ApprovePanel.Visible = false;
}

C#
// when an user clicks a edit button in a row of grid, 
// the panel to display detail information for a record should be displayed
protected void GridView_RowCommand(object sender, GridViewCommandEventArgs e)
{
    if (string.Equals("Edit", e.CommandName, StringComparison.OrdinalIgnoreCase))
    {
        DetailPanel.Visible = true;
        ApprovePanel.Visible = false;
        // TODO: Load an entity by e.CommandArgument to controls inner of DetailPanel.
    }
}

1.4. UI code becomes more complex when we integrate actions with permission in a web form, like readonly, create, update, delete and approve permission etc. We have to hard code somewhere in the webform to redirect or hide some actions based on authorization of current user. We may abstract the redirect from a HttpModule or base page class, but that permission only works for page level. We have no way to authorize on behavior level besides a sample dummy code snippet as following.
C#
protected void Page_Load(object sender, EventArgs e)
{
    if (user is not authorized)
        redirect to Unauthorized page;

    if (user has no readonly permission)
        redirect to Unauthorized page;
        
    if (user has permission on create)
        display create button;
    
    if (user has permission on update/delete)
        display update/delete button in each row of grid;
        
    if (user has permission on approve)
        display approve button somewhere;
        
    if (user has permission on OperationX)
        display OperationX button somewhere;
}


1.5. nearly impossible to write unit tests for ASP.NET pages and controls that we're hard to feel free for any coming changes in UI. This is not the issue for MVC.

Solutions of RapidWebDev UI Framework 

RapidWebDev UI framework is in a different thought to ASP.NET or MVC development. The framework predefines general behaviors of web forms include both UI panel visibility control and user actions which can be summerized as following,

  • when an user clicks button Query, the data returned asynchronously from server is rendered to grid.
  • there are three buttons configurable for each row of grid, View, Edit and Delete. When an user clicks button View or Edit, the record is rendered in a modal detail panel. When the user saves changes of the record, the modal detail panel is closed and the grid is refreshed automatically. When an user clicks button Delete, the record is deleted after the user confirms deletion in a popup dialog.
  • when an user clicks button Add in Button Panel, a blank modal detail panel is popup for input. When the user saves changes of the record, the modal detail panel is closed and the grid is refreshed automatically.
  • when an user clicks a custom button in Button Panel, the button related Aggregate Panel is popup in a modal dialog. The user can confirm the custom operations in the aggregate panel against the records selected in grid. After the user confirms the custom operation against selected records, the modal aggregate panel is closed and the grid is refreshed automatically.

When an user does an operation in UI, the framework invokes the operation related callback method of configured implementation of interface IDynamicPage, IDetailPanelPage or IAggregatePanelPage. The architecture as following,

So let's see how RapidWebDev UI framework improves the facing problems of ASP.NET and MVC,

1.1. the UI of web forms are rendered by the framework through xml configuration and template. So UI style of web forms in a software such as label, indent, layout are easy to be uniform that the framework renders UI from xml by . And the user interaction is predefined and managed by the framework so that user interaction behaviors of web forms are consolidate out of question.

1.2. query filters are configured by xml. When an user clicks Query button, the framework callbacks method Query with QueryParameter collected from click submit automatically. QueryParameter includes query filters, sorting and paging information which can be converted to query expression intelligently. So we don't need to assemble query expression manually as the following code snippet. With this design, we're very agile for changes of query filters that we don't need to change any CLR code but only modify the xml configuration.

C#
/// <summary>
/// Query products by parameters.
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public override QueryResults Query(QueryParameter parameter)
{
    using (ProductManagementDataContext ctx = 
        DataContextFactory.Create<ProductManagementDataContext>())
    {
        // Initialize a query from LINQ data context
        IQueryable<Product> q = from p in ctx.Products 
                                where p.ApplicationId == authenticationContext.ApplicationId 
                                select p;

        // QueryParameter is converted to LINQ lambda Where expression 
        // by a convertion strategy configured in Spring.NET IoC
        LinqPredicate predicate = parameter.Expressions.Compile();
        if (predicate != null && !string.IsNullOrEmpty(predicate.Expression))
            q = q.Where(predicate.Expression, predicate.Parameters);

        // QueryParameter is converted to LINQ lambda OrderBy expression 
        // by a convertion strategy configured in Spring.NET IoC
        if (parameter.SortExpression != null)
            q = q.OrderBy(parameter.SortExpression.Compile());

        int recordCount = q.Count();
        var results = q.Skip(parameter.PageIndex * parameter.PageSize)
            .Take(parameter.PageSize).ToList();
        return new QueryResults(recordCount, );
    }
}

1.3. we're just writing business logics in callback for predefined or custom operation but not take care of how and when the callback is invoked by the framework. So we don't have the troubles on control visibility of panels manually as ASP.NET or MVC. Take an exampe, we implement a callback for Create as following sample code snippet. We don't need to take care of how to switch the panels after the record been created. Generally, the sight on developing web forms is limited so that the complexity is decreased. Finally the developlment velocity and product quality is increased.
C#
/// <summary>
/// Create a new concrete data from detail panel and return its id.
/// The method needs to create a new entity and set control values to its properties then persist it.
/// </summary>
/// <returns>returns the id of new created concrete data.</returns>
public override string Create()
{
    ConcreteDataObject concreteDataObject = new ConcreteDataObject();
    concreteDataObject.Name = this.TextBoxName.Text;
    concreteDataObject.Value = this.TextBoxValue.Text;
    concreteDataObject.Description = this.TextBoxDescription.Text;
    concreteDataApi.Save(concreteDataObject);
    
    return concreteDataObject.Id.ToString();
}

1.4. Authorization is integrated in the framework through interface IPermissionBridge which is configured in Spring.NET IoC. Take an example, ProductManagement is configured for a web form. ProductManagement.Add is the permission for adding a new product. ProductManagement.Update is the permission for editing an existed product. ProductManagement.[CommandArgument] is the permission for special button and aggregate panel with that command argument. If an user don't have permission ProductManagement.Add, the button Add is invisible for the user and the related method Create in the implementation of interface IDetailPanelPage is protected. So we don't need to write explicit permission checking code any more.

1.5. Webforms developed by RapidWebDev UI framework is unit testable. Let's see the test case for the method Create above.
C#
[Test, Description("Test the Create Method in ConcreteDataDetailPanel")]
public void TestCreate()
{
    ConcreteDataDetailPanel page = new ConcreteDataDetailPanel();
    DetailPanelPageProxy proxy = new DetailPanelPageProxy(page);
    using (var httpEnv = new HttpEnvironment())
    {
        httpEnv.SetRequestUrl(@"/ConcreteDataDetailPanel/DynamicPage.svc?ConcreteDataType=Department");
        
        #region Set binding controls of DetailPanel

        TextBox TextBoxName = new TextBox();
        TextBoxName.Text = "name";
        proxy.Set("TextBoxName", TextBoxName);

        TextBox TextBoxValue = new TextBox();
        TextBoxValue.Text = "value";
        proxy.Set("TextBoxValue", TextBoxValue);

        TextBox TextBoxDescription = new TextBox();
        TextBoxDescription.Text = "description";
        proxy.Set("TextBoxDescription", TextBoxDescription);

        #endregion

        string entityId = proxy.Create();
        
        // Verify the added entity
        IConcreteDataApi concreteDataApi = SpringContext.Current.GetObject<IConcreteDataApi>();
        ConcreteDataObject obj = concreteDataApi.GetById(new Guid(entityId));
        Assert.AreEqual(obj.Name, "name");
    }
}

2. Reusability Comparison  

Problems of ASP.NET and MVC development

The ASP.NET developed web pages are hard reusable. We cannot inherit a web page from another although there may be a lot resuable stuff. MVC decouples controller and view but the panels inner of the view are mixed together. Once we developed a web form with query, list, edit, view or any other panels for custom operations, even we're hard to reuse this form to display a single record only. Take an example, we have an user management page. If we want to see the profile of an user in system, we cannot reuse the page to display the profile of user only in other places because there are query panel, grid etc.

Solution of RapidWebDev UI Framework

With RapidWebDev UI framework, we implement interface IDynamicPage and write xml configuration for query and list; implement interface IDetailPanelPage and write an ascx template for detail panel; implement interface IAggregatePanelPage and write ascx template for aggregate panel. The development of a web form is broken down into multiple units. This enables us possible to reuse small units of a web form in other place. "Reusability" in RapidWebDev UI framework can be summerized as,

2.1. reuse detail panel for a single record view
If we developed a product management page at Uri: ~/ProductManagement/DynamicPage.svc,

We can reuse the page for detail information view by Uri: ~/ProductManagement/DetailPanel.svc?entityid=792db4cc-3a89-4f9d-94e8-a9293cd27c56&rendermode=View. This actually reuses the detail panel of a whole web form to view a single record directly.
 

2.2. reuse implementations of interface IDynamicPage, IDetailPanelPage or IAggregatePanelPage in other web forms. If there are two web forms having the similar functionality requirement but different layout, we can only configure different xml or ascx template but using the same implementations of interface IDynamicPage, IDetailPanelPage or IAggregatePanelPage.

2.3. extend implementations of interface IDynamicPage, IDetailPanelPage or IAggregatePanelPage by inheritence. This is quite useful in an enterprise software that a business workflow is usually composed of a series of processes to an entity. Like in a shiping order workflow, we may have the processes as create, approve, distribute, ship and feedback. But all of these are around an order. With RapidWebDev UI framework, you may only need to implement a order management page for create, edit and view. And only extend the implementations with a slight changes for other business processes.

Take an example, we have an web page to manage products as above screenshot. Now we need to develop another web page to audit products. The audit detail panel includes approval decision and comment for a single product besides all information readonly of the product. In the case, we can reuse IDynamicPage implementation of product management for query and create a new implementation to IDetailPanelPage which inherits from the implementation of product management. Then we can reuse Load method to load the product detail information.

C#
/// <summary>
/// Detail panel page to audit products.
/// When we configure an implementation of IDetailPanelPage or IAggregatePanelPage to a web form, 
/// the members with Binding attribute of its base class are resolved from template as well.
/// </summary>
public class ProductAuditDetailPanel : ProductDetailPanel
{
    #region 2 new controls only used in audit detail panel
    [Binding]
    protected DropDownList DropDownListAuditDecision;
    [Binding]
    protected TextBox TextBoxAuditComment;
    #endregion

    /// <summary>
    /// Load a product into detail panel for audit.
    /// </summary>
    /// <param name="entityId"></param>
    public override void LoadWritableEntity(string entityId)
    {
        // load the existed audit decision and comment
        Guid productId = new Guid(entityId);
        using (ProductManagementDataContext ctx = DataContextFactory.Create<ProductManagementDataContext>())
        {
            Product p = ctx.Products.FirstOrDefault(product => product.Id == productId);
            if (p == null)
                throw new ValidationException("The product id doesn't exist.");
        }

        this.DropDownListAuditDecision.SelectedValue = p.AuditDecision;
        this.TextBoxAuditComment.Text = p.AuditComment;

        // Reuse base LoadReadOnlyEntity to load readonly detail of the product
        base.LoadReadOnlyEntity(entityId);
    }

    /// <summary>
    /// Audit a product.
    /// </summary>
    /// <param name="entityId"></param>
    public override void Update(string entityId)
    {
        Guid productId = new Guid(entityId);
        using (ProductManagementDataContext ctx = DataContextFactory.Create<ProductManagementDataContext>())
        {
            Product p = ctx.Products.FirstOrDefault(product => product.Id == productId);
            if (p == null)
                throw new ValidationException("The product id doesn't exist.");

            p.AuditDecision = this.DropDownListAuditDecision.SelectedValue;
            p.AuditComment = this.TextBoxAuditComment.Text;
            ctx.SubmitChanges();
        }
    }
}

3. Performance Comparison 

Problems of ASP.NET and MVC development

If you have experience on both ASP.NET and MVC web pages, you shall know that MVC web pages are much faster than a same page written by ASP.NET. The performance of ASP.NET web form is limited by view state, ASP.NET JS library and postback. Microsoft provides a dummy AJAX UpdatePanel for ASP.NET, but it's just develop AJAX-like web forms and doesn't solve the performance issues really. UpdatePanel posts all form elements to server asynchrounously by JS and replace the part inner of the UpdatePanel with returned HTML content. The limitation of ASP.NET is still there.

Web pages developed by MVC are pretty fast that there is no large view state and slow ASP.NET JS library. But postback of MVC follows the original HTTP submit, GET or POST which is not efficient enough in frequent user interactions. So it's perfect solution to develop website but not for enterprise business software.

Ideally, the communication between web forms and server should be JSON data driven in enterprise software. When an user does any operations in a web form, the web form only sends JSON data to server asynchronously and render returned JSON data to the web form by JS. Surely, there are a ton of JS library for such work, like jquery, ExtJS, YUI and Microsoft Atlas. But we have to write a lot of JS code in ASP.NET web forms/MVC view templates which is hard maintainable and poor productivity. When a software company is under pushing by a contract, the preferred solution is to sacrifice user experience to improve productivity.

Solution of RapidWebDev UI framework

RapidWebDev UI framework creates an balanced approach to develop high performance AJAX web forms without taking care of JS library by developers. The user interactions in a web form is abstracted as query, pagination, sort, create, update, delete and any other custom operations in the framework. The paragraph below introduces how AJAX works in the developed web forms by each user interaction.

query
When an user clicks button Query, the web form only sends the query filters to server asynchronously and render returned JSON data into grid. The paging and sorting in grid doesn't the same as query. The web server response size for the interaction is very small by the size of JSON data only seeing the screenshot below.
 

And what developers do is to implement a query method and return a collection of records as following code snippet. The AJAX request and rendering is all controlled by the framework.

C#
/// <summary>
/// Query products by parameters.
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public override QueryResults Query(QueryParameter parameter)
{
    // TODO: get the total record count and results
    int recordCount = q.Count();
    var results = q.Skip(parameter.PageIndex * parameter.PageSize)
        .Take(parameter.PageSize).ToList();
    return new QueryResults(recordCount, );
}

delete
After the user confirms deletion on a record, the web forms sends a GET request asynchronously to web server and refreshed grid after get sucessful notification. As the following code snippet, developers only need to delete the record by the passing entity id. All the other works is done by the framework, like request, grid refresh etc.
C#
/// <summary>
/// Delete a concrete data by id.
/// </summary>
/// <param name="entityId"></param>
public override void Delete(string entityId)
{
	ConcreteDataObject concreteDataObject = concreteDataApi.GetById(new Guid(entityId));
	if (concreteDataObject != null)
	{
		concreteDataObject.DeleteStatus = DeleteStatus.Deleted;
		concreteDataApi.Save(concreteDataObject);
	}
}


create, update, view
When an user is creating, updating or viewing a record in detail panel as following screenshot, the detail panel acsx template is actually rendered in a dynamic generated standalone web page but integrated with the main page by iframework. The detail panel acsx template is wrapped in an ASP.NET UpdatePanel automatically. When the user "saves", the detail panel wrapping page postback in dummy AJAX. But the page is only composed of an ascx template for detail information which is very small, so the performance is acceptable as well. After the detail panel saved, the popup modal dialog is closed and the grid is refreshed automatically.
 

any custom operations
Aggregate panel is used for custom operations which works in the same way as detail panel. The following screenshot is when an user clicks button bulk delete, the aggregate ascx template is rendered in a dynamic generated web page in the modal dialog for confirmation.

So, RapidWebDev UI framework provide a innovative approach to develop high performance web forms productively.

4. Maintainability Comparison

With less development complexity and high reusability talked above, the developed web forms by RapidWebDev UI framework is easy maintainable. Especially we can easily find the code block to modify because user behaviors are abstracted and categorized by the architecture of framework. And if we prefer to developing high efficient AJAX web forms in ASP.NET or MVC, we have to write a lot of JS code which is very hard maintainable out of question.  

What's RapidWebDev 

RapidWebDev Uri: http://www.rapidwebdev.org  

RapidWebDev is an infrastructure helps engineers to develop enterprise software solutions in Microsoft .NET easily and productively. It consists of an extendable and maintainable web system architecture with a suite of generic business model, APIs and services as fundamental functionalities needed in development for almost all business solutions. So when engineers develop solutions in RapidWebDev, they can have a lot of reusable and ready things then they can more focus on business logics implementation. In practice, we can save more than 50% time on developing a high quality and performance business solution than traditional ASP.NET development.  

Related Topics

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)