Designing and Developing Web Applications Using Microsoft .NET Framework 4: Designing the Application Architecture

  • 10/17/2011
Tony Northrup walks you through how to design your application architecture in this chapter from MCPD 70-519 Exam Ref: Designing and Developing Web Applications Using Microsoft .NET Framework 4.

The highest level aspect of the design process is also the most exciting: designing the application architecture. In this stage, the application begins to come to life, and you are not getting bogged down in technical details. You create a logical design for your application and then map the logical layers to physical servers. After you determine the physical layout, you can choose interapplication communication mechanisms and plan for cross-cutting concerns, such as systems administration.

Further into the design process, you choose how presentation logic will be divided between the client and server. For client-side components, you will need to decide between basic JavaScript, jQuery, Microsoft AJAX, and Microsoft Silverlight. For server-side components, you will need to choose between HTML controls, server controls, user controls, and Web Parts.

Finally, you will need to decide how to implement various state-management tasks. The Microsoft .NET Framework provides a wide variety of technologies, including application state, session state, view state, cookies, and caching.

Objectives in this chapter:

  • Objective 1.1: Plan the Division of Application Logic

  • Objective 1.2: Analyze Requirements and Recommend a System Topology

  • Objective 1.3: Choose Appropriate Client-Side Technologies

  • Objective 1.4: Choose Appropriate Server-Side Technologies

  • Objective 1.5: Design State Management

Objective 1.1: Plan the Division of Application Logic

In the early days of the web, browsers did little more than render HTML and display images. Today, thanks to technologies such as JavaScript, Flash, and Silverlight, the browser can interact with the user, validate data, and communicate with servers without loading new webpages. Use these client-side capabilities properly, and you can make your web application feel faster, reduce bandwidth, and reduce user input errors.

Server-side processing still has its place, however. First, server-side code is much easier to develop, test, and maintain. Second, anything but the most trivial data validation must be performed on the server, because it is possible for malicious attackers to bypass client-side validation. Third, some clients do not support JavaScript, Flash, or Silverlight, requiring you to duplicate any mandatory client-side functionality on the server.

This objective covers how to:

Choosing Between the Client Side and Server Side

Many tasks can be performed at either the client or the server. For example, if you ask the user to enter his address in a web form, you can provide a DropDownList named CountryDropDownList that contains every country/region in the world. When the user selects a country, you can populate the StateDropDownList with a list of states or provinces in his country.

You can do this on either the server or the client:

  • Server. In ASP.NET, set CountryDropDownList.AutoPostBack to True. In the DropDownList.SelectedIndexChanged event handler, populate StateDropDownList.

  • Client. Create a JavaScript function that handles the CountryDropDownList.OnChange JavaScript event and populates the StateDropDownList on the client.

Neither approach is clearly superior, but they each have advantages. By populating the list on the server side, you keep more code in ASP.NET, which is generally easier to write, troubleshoot, and maintain than JavaScript. Additionally, server-side processing works when the client does not support JavaScript.

By populating the list on the client side, you improve performance for both the user and the server. Client-side processing avoids a browser postback to the server when the user selects her country. This eliminates a delay in data entry that could last several seconds. Additionally, by reducing the number of requests sent to the web server, it reduces the performance impact on the server, thus improving scalability.

Table 1-1 compares common tasks that can be performed at either the client or server, and how you write code to accomplish them. When validating user input, you typically validate it on the client (for immediate responsiveness) and again at the server (for security and for browsers that do not support JavaScript).

Table 1-1. Performing Different Tasks at the Client-side and Server-side

Task

Client-side feature

Server-side feature

Respond to a button click

JavaScript’s onClick event

ASP.NET’s Button.Click event

Access a SOAP web service

JavaScript SOAP clients or the XMLHttpRequest object

Import the definition, and access the methods directly

Update part of a page with data from the server

ASP.NET UpdatePanel control

Any server control

Validate user input

RequiredFieldValidator, RangeValidator, RegularExpressionValidator, and CustomValidator (with the ClientValidationFunction property)

RequiredFieldValidator, RangeValidator, RegularExpressionValidator, and CustomValidator (with the OnServerValidate property)

Many tasks should always be done on the server, while other tasks should be performed on the client (when the client supports JavaScript). Table 1-2 lists tasks that can be done on the client, and situations that require you to perform the task on the server, instead.

Table 1-2. Client-side and Server-side Tasks

Client-side Tasks

Server-side Tasks

For convenience, notify users if they enter data in an invalid format. For example, if they enter too few numbers for a credit card.

For security and data integrity, verify that user data falls within specified bounds.

Dynamically add items to a menu, based on what the user does within a single webpage.

Add items to a menu for restricted pages that only authorized users can access.

Perform tasks that require access to the client computer, such as saving files using JavaScript or accessing the graphical processing unit (GPU) with Silverlight.

Perform tasks that require access to resources on the internal network that the server can access but are not exposed to the client.

Perform tasks that consume a great deal of bandwidth when communicating between the client and server.

Perform tasks that cannot be performed on the client, especially when the client lacks JavaScript, Flash, or Silverlight.

Process business logic that the end user is allowed to examine (because the user can access the source code).

Process business logic that should not be exposed to the end user.

Perform user-interface interactions, such as expanding menus and displaying slide shows.

Perform security-oriented tasks, such as processing credit cards and authenticating users.

If a task can be performed on either the client or the server, you should perform the task on the server because server-side programming is more efficient, the code is easier to debug, and the application is easier to maintain. Table 1-3 describes the key differences between client-side and server-side programming.

Table 1-3. Comparison of Client-side and Server-side Programming

Client-side Programming

Server-side Programming

Code is written in Microsoft Visual Studio 2010 with limited support for auto-complete.

Code is written in Visual Studio 2010 with full support for auto-complete, descriptions of all parameters, and integrated documentation.

Weak typing and run-time detection of errors.

Strong typing with compile-time detection of many errors

Must test in every supported operating system, browser, and browser version (which can be more than a dozen different environments).

Only need to test in a single-web-server environment.

Somewhat imprecise debugging provided by Microsoft Internet Explorer and Visual Studio 2010. Other browsers require browser-specific debugging tools.

Precise debugging provided by Microsoft Internet Information Services (IIS) and the Visual Studio 2010 ASP.NET runtime environment.

Code might never run if the client does not support JavaScript.

Code always runs regardless of client capabilities.

End users can view, manipulate, or bypass code.

Code is never exposed to the end user.

Partitioning According to Separation of Concerns

Separation of Concerns (SoC) is a software architecture concept for dividing code used for different purposes. For example, if you were designing a web application with SoC in mind, you might create different application layers for the user interface, the business logic, the data access, and the database itself.

Microsoft’s early web development languages provided little opportunity for implementing SoC. However, the importance of SoC is reflected in each new web development model that Microsoft has released, as the following sections describe.

Classic ASP

In 1998, Microsoft released Active Server Pages (ASP), now known as Classic ASP. Classic ASP mixed the HTML user interface and all back-end code into a single file. To write output to part of a webpage, you had to write code at the appropriate spot in the HTML:

<p>First name:
<%
    Dim firstName
    firstName = "Kim" (Akers)
    Response.Write firstName
%>
</p>
<p>Last name:
<%
    Dim lastName
    lastName = "Akers"
    Response.Write lastName
%>
</p>

Because all the code was mixed together, a web designer who wanted to modify the user interface might accidentally change code that performed business logic or accessed the database. Similarly, if a database designer changed the layout of a table in the database, it might affect the user interface of the application. Performing quality assurance (QA) was difficult because you could not easily test individual components. Instead, developers had to simulate user input and then examine the resulting HTML output for an expected result. Different developers could not easily work on the same page at the same time.

ASP.NET

In 2002, Microsoft released ASP.NET, which allowed developers to use code-behind files to separate the HTML and the placement of server controls from the back-end code. This was a definite improvement for implementing SoC, but developers still created a single class for displaying output and responding to user input. This approach makes testing difficult because testing an individual page requires creating an instance of the page class, its child controls, and all dependent classes.

ASP.NET MVC

In 2009, Microsoft released ASP.NET MVC, which is named for the Model-View-Controller software architecture and provides three different layers of SoC:

  • Model. The data and behavior of the application

  • View. The user interface, which displays data provided by the model

  • Controller. Accepts user input, and calls the model and view to generate a response

Figure 1-1 shows the MVC design pattern and the communications between the layers.

Figure 1-1

Figure 1-1. The MVC design pattern

By providing SoC, MVC provides several benefits. Support for test-driven development allows QA personnel to query the model directly to verify that it provides an expected output when given a specific input. Developers can modify views to update the user interface without any potential impact on the business logic or data access layers. Controllers completely abstract requests from the models and views responding to the request, allowing web architects to specify a structure for the user interface without defining the application architecture.

Implementing SoC can increase development time for smaller applications, albeit by a small margin. However, SoC can dramatically reduce debugging, QA, and maintenance time. SoC also simplifies dividing development tasks between multiple developers. Therefore, the larger the development effort, the more important SoC becomes.

Planning for Long-Running Processes

Web users are impatient and will cancel a request or give up on a website entirely if pages do not load quickly. As a result, webpages typically need to be rendered in less than a second. That is enough time to query a database or a web service, but performing a longer-running task requires multiple requests.

Consider a travel agency web application that provides flight information from multiple airlines. If a user requests information about all flights between Boston and Chicago on a specific day, the web application might need to send web service requests to a dozen different airlines and wait for the responses before displaying the results to the user. One airline might respond in half a second, but another airline might take 10 seconds to respond.

If the web application queried each airline synchronously (in sequence, one after another), the response time would be delayed by the sum total of all the airline web services. It is more efficient to submit the web service queries asynchronously (in parallel, all at the same time). Then the response time is delayed only by the time required by the slowest web service.

When you create a method, such as a Button.Click event handler, the code in the method runs synchronously by default. In other words, the common language runtime (CLR) runs one line of code, waits for the results, and then moves on to the next line. This linear flow is easy for developers to understand, and it is efficient for short-running processes.

If you have long-running processes, such as waiting for a web service to respond, you can use asynchronous processing to allow the .NET Framework to perform other tasks instead of waiting for the response. When the asynchronous response is complete, you can retrieve the results and update the response to the user.

Designing a Webpage for a Long-Running Process

Figure 1-2 shows the typical flow of a synchronous webpage. With this model, however, the user gets no feedback until the server finishes rendering the response. If it takes the server more than a few seconds, the user is likely to cancel the request.

Figure 1-2

Figure 1-2. The flow of a typical synchronous webpage

Figure 1-3 shows the typical flow of an asynchronous webpage. With this model, the server informs the user that the response will take a few moments. A client-side script regularly checks the server to determine whether the results are ready. When the long-running, asynchronous process has completed, the final results are displayed to the user.

Figure 1-3

Figure 1-3 The flow of a typical asynchronous webpage

You can run a long-running process while remaining responsive to users. In the travel agency example, developers might take one of these two approaches:

  • Display a loading page with a progress bar or other animation that shows the application is currently processing the request. This page uses JavaScript to communicate with the server. When the server reports that the results are ready, JavaScript loads the results page.

  • Immediately display a formatted results page. Instead of showing the results, a progress bar indicates that the results are loading. In the background, the page runs JavaScript to connect to the server and wait for results. As the server returns results (either partially or all at once), JavaScript adds the results to the page.

For processes that might take more than a minute or two to complete, gather the user’s email address and send her a notification with a link to retrieve the results.

Designing a Web Service for a Long-Running Process

Whereas web applications must render the HTML that the browser displays as the user interface, web services return raw data that the client application processes. Because the web service client creates the user interface, the web service developer does not need to decide how to communicate the delay to the user.

The web service developer does, however, need to design the web service to accommodate long-running asynchronous processes. If both the client and server are based on the .NET Framework, and the client is not protected by a firewall or Network Address Translation (NAT) device, you can use WSDualHttpBinding, netTcpBinding, NetNamedPipeBinding, NetPeerTcpBinding, or NetTcpContextBinding to create a callback contract on the client and then use that callback to notify the client that the process is complete.

If the binding type does not support duplex communications, or you must communicate through a firewall that prevents incoming connections to the client, you should handle long-running web service requests by immediately providing a token the client can use to later retrieve the results. To provide better feedback to the end user, you can also provide an estimated wait time that the client can use to display the progress to the user. Then have the client regularly poll the server to determine if the process is complete.

Use polling to retrieve the results of a long-running query that is using a web service by following this process:

  1. The client sends the request to the web service. This might be, for example, “List all flights between Boston and Chicago on May 1.”

  2. The web service provides a unique token to the client and an approximate wait time. The token should be large and cryptographically random, such as a 20-byte value generated by RngCryptoServiceProvider. The wait time should be based on the actual wait time for similar requests.

  3. The web service client displays a progress bar to the user to let him know the request is continuing and the application is responsive. The web service asynchronously calls a method to process the request and store the results in a database record associated with the token.

  4. After an interval (for example, one-quarter of the estimated wait time), the web service client queries the web service for the results, providing the unique token. If the results are ready, the web service client formats and displays the data; otherwise, it repeats this step.

Objective Summary

  • Use client-side scripting to provide users with a rich, responsive interface. However, you must write server-side code when security is important. Additionally, server-side code is more efficient to write, test, troubleshoot, and maintain.

  • SoC simplifies development, testing, and updating of large-scale web applications. Strive to design applications with SoC dividing the functional layers of an application.

  • To perform a long-running request while appearing responsive to the user, divide a request into multiple steps. In the first step, launch the long-running process asynchronously and display a wait page to the user. Embed JavaScript in the wait page that queries the server for the status of the long-running process and retrieves the final results page when processing is complete.

Objective Review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

  1. You are designing an ASP.NET web application that allows members of the Administrators role to edit content by using a HyperLink control on each page named EditHyperLink. You do not want nonadministrators to discover the location of the administration pages. Which approach should you recommend?

    1. In the ASPX page, set the EditHyperLink.Visible property to True. In the JavaScript window.onload event handler, set the link’s style.display property to none if the user is not a member of the Administrators role.

    2. In the ASPX page, set the EditHyperLink.Visible property to False. In the JavaScript window.onload event handler, set the link’s style.display property to block if the user is a member of the Administrators role.

    3. In the ASPX page, set the EditHyperLink.Visible property to False. In the Page.Load event handler, set the HyperLink.Visible property to True if the user is a member of the Administrators role.

    4. In the ASPX page, set the EditHyperLink.Visible property to False. In the EditHyperLink.Click event handler, set the HyperLink.Visible property to False if the user is not a member of the Administrators role.

  2. You are designing an ASP.NET web application that provisions virtual machines for a testing environment. Users can provision from 1 to 10 virtual machines at a time, and each virtual machine provision might take up to 60 seconds. For security reasons, the server hosting the virtual machines allows provisioning requests only from the web server. You need to design the application to keep users notified of the provisioning progress. Which approach should you recommend?

    1. On the server, start asynchronous processes to provision each virtual machine. On the client, use JavaScript to query the server every five seconds for a status update.

    2. On the server, synchronously provision each virtual machine. When complete, return a status update to the user.

    3. On the server, calculate the approximate total provisioning time. On the client, use JavaScript to connect to the server hosting the virtual machines and initiate the provisioning.

    4. On the client, use JavaScript to launch a separate asynchronous process for each virtual machine to be provisioned. Within each process, request a page from the web server that provisions a virtual machine.

  3. You are creating a new website for an enterprise organization. The enterprise has a quality assurance team that requires developers to use test-driven development. Additionally, the application architecture must partition according to the principle of SoC. Which template should you use?

    1. Use the ASP.NET 4.0 web application project template.

    2. Use the ASP.NET MVC 2 web application project template.

    3. Use the Silverlight application project template.

    4. Create an ASP.NET 4.0 website.