Many frameworks exist for building these AJAX-based Web applications. One of the more popular frameworks is the ASP.NET AJAX framework. This framework has a great client-side and server-side model for building AJAX-enabled Web applications. It includes many capabilities such as a rich client-side class library, rich AJAX-enabled Web controls, and automatic client-side proxy generation for communication with services. It is also based on ASP.NET, which is Microsoft's technology for building Web applications using .NET. WCF already integrates with ASP.NET in .NET Framework 3.0. .NET Framework 3.5 introduces new support for ASP.NET AJAX applications using the WebScriptEnablingBehavior endpoint behavior. This replaces the WebHttpBehavior endpoint behavior. It adds support for using JSON by default and ASP.NET client-side proxy generation. These new capabilities can be used by replacing the webHttp endpoint behavior configuration element with the enableWebScript configuration element.
We created a sample ASP.NET AJAX application called the XBOX 360 Game Review to see how we can use the WebHttpBinding binding and the WebScriptEnablingBehavior to build AJAX-based applications. This simple Web application enables users to provide reviews about their favorite XBOX 360 game. The application was built using an ASP.NET AJAX Web site project template in Visual Studio 2008. Figure 13.2 shows a picture of this Web site.
Figure 13.2 XBOX 360 Game Review AJAX-enabled application
This site has a number of features. First is a list of games that is displayed in a ListBox control to the user. Users can select a game and see a list of comments for each game. Then a user can add comments for the each game. Listing 13.7 lists the service that provides this functionality.
using System;using System.Collections;using System.Collections.Generic;using System.Runtime.Serialization;using System.ServiceModel;using System.ServiceModel.Activation;using System.ServiceModel.Web;
namespace EssentialWCF{ [ServiceContract(Namespace = "EssentialWCF")] [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class GameReviewService { private string[] gamelist = new string[] { "Viva Pinata", "Star Wars Lego", "Spiderman Ultimate", "Gears of War", "Halo 2", "Halo 3" }; private Dictionary<string, List<string>> reviews;
public GameReviewService() { reviews = new Dictionary<string, List<string>>(); foreach (string game in gamelist) reviews.Add(game, new List<string>()); }
[OperationContract] [WebGet] public string[] Games() { return gamelist; }
[OperationContract] [WebGet] public string[] Reviews(string game) { WebOperationContext ctx = WebOperationContext.Current; ctx.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");
if (!reviews.ContainsKey(game)) return null;
List<string> listOfReviews = reviews[game];
if (listOfReviews.Count == 0) return new string[] { string.Format("No reviews found for {0}.",game) }; else return listOfReviews.ToArray(); }
[OperationContract] [WebInvoke] public void AddReview(string game, string comment) { reviews[game].Add(comment); }
[OperationContract] [WebInvoke] public void ClearReviews(string game) { reviews[game].Clear(); } }}
<%@ ServiceHost Language="C#" Debug="true"Service="EssentialWCF.GameReviewService"CodeBehind="~/App_Code/GameReviewService.cs" %>
Listing 13.9 shows the configuration information used to host the GameReviewService. The most important aspect of this configuration information is the use of the webHttpBinding binding and the enableWebScript endpoint behavior. This enables the use of JSON and generates the necessary client-side proxy code for the GameReviewService with ASP.NET.
<system.serviceModel> <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> <services> <service name="EssentialWCF.GameReviewService" behaviorConfiguration="MetadataBehavior"> <endpoint address="" behaviorConfiguration="AjaxBehavior" binding="webHttpBinding" contract="EssentialWCF.GameReviewService"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <endpointBehaviors> <behavior name="AjaxBehavior"> <enableWebScript/> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="MetadataBehavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl=""/> </behavior> </serviceBehaviors> </behaviors></system.serviceModel>
You configure the GameReviewService to be used with ASP.NET by adding a reference to the service using the ASP.NET ScriptManager. Listing 13.10 shows the markup used to reference the GameReviewService. Behind the scenes this is generating client-side script that references a JavaScript file with the client-side proxy. For our example, the URI to the client-side JavaScript is http://localhost/GameReviewService/GameReviewService.svc/js.
<asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="GameReviewService.svc" /> </Services></asp:ScriptManager>
We have included the ASP.NET Web form used to build the XBOX 360 Game Review Web application. This shows how the services are called from client-side script and how the results are used to dynamically populate controls.
<%@ Page Language="C#" AutoEventWireup="true"CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ Register Assembly="AjaxControlToolkit"Namespace="AjaxControlToolkit" TagPrefix="cc1" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head id="Head1" runat="server"> <title>XBOX 360 Game Reviews</title>
<script type="text/javascript">
function pageLoad() { }
</script>
</head><body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="GameReviewService.svc" /> </Services> </asp:ScriptManager>
<script type="text/javascript"> EssentialWCF.GameReviewService.set_defaultFailedCallback(OnError); function ListGames() { EssentialWCF.GameReviewService.Games(OnListGamesComplete); } function ListReviews() { var gameListBox = document.getElementById("GameListBox"); EssentialWCF.GameReviewService.Reviews(gameListBox.value, OnListReviewsComplete); } function AddReview() { var gameListBox = document.getElementById("GameListBox"); var reviewTextBox = document.getElementById("ReviewTextBox"); EssentialWCF.GameReviewService.AddReview(gameListBox.value, reviewTextBox.value, OnUpdateReviews); }
function ClearReviews() { var gameListBox = document.getElementById("GameListBox"); EssentialWCF.GameReviewService.ClearReviews(gameListBox.value, OnUpdateReviews); } function OnListGamesComplete(result) { var gameListBox = document.getElementById("GameListBox"); ClearAndSetListBoxItems(gameListBox, result); } function OnListReviewsComplete(result) { var reviewListBox = document.getElementById("ReviewListBox"); ClearAndSetListBoxItems(reviewListBox, result); } function OnUpdateReviews(result) { ListReviews(); } function ClearAndSetListBoxItems(listBox, games) { for (var i = listBox.options.length-1; i >-1; i--) { listBox.options[i] = null; }
var textValue; var optionItem; for (var j = 0; j < games.length; j++) {
textValue = games[j]; optionItem = new Option( textValue, textValue, false, false); listBox.options[listBox.length] = optionItem; } } function OnError(result) { alert("Error: " + result.get_message()); } function OnLoad() { ListGames(); var gameListBox = document.getElementById("GameListBox"); if (gameListBox.attachEvent) { gameListBox.attachEvent("onchange", ListReviews); } else { gameListBox.addEventListener("change", ListReviews, false); } } Sys.Application.add_load(OnLoad); </script>
<h1>XBOX 360 Game Review</h1> <table> <tr style="height:250px;vertical-align:top"><td style="width:240px">Select a game:<br /><asp:ListBox ID="GameListBox" runat="server" Width="100%"></asp:ListBox></td> <td style="width:400px">Comments:<br /><asp:ListBox ID="ReviewListBox" runat="server" Width="100%" Height="100%"></asp:ListBox></td></tr> <tr style="vertical-align:top"><td colspan="2"> Enter a comment:<br /> <asp:TextBox ID="ReviewTextBox" runat="server" width="400px"></asp:TextBox> <input id="AddReviewButton" type="button" value="Add" onclick="AddReview()" /> <input id="ClearReviewButton" type="button" value="Clear" onclick="ClearReviews()" /> </td></tr> </table> </div> </form></body></html>