ptg 1484 CHAPTER 31 Working with the HTTP Runtime private string _conString; private SqlConnection _con; private SqlCommand _cmdSelect; private SqlCommand _cmdInsert; public override void Init() { // initialize connection _conString = ➥ WebConfigurationManager.ConnectionStrings[“Log”].ConnectionString; _con = new SqlConnection(_conString); // initialize select command _cmdSelect = new SqlCommand(“SELECT COUNT(*) FROM Log WHERE Path=@Path”, ➥ _con); _cmdSelect.Parameters.Add(“@Path”, SqlDbType.NVarChar, 500); // initialize insert command _cmdInsert = new SqlCommand(“INSERT Log (Path) VALUES (@Path)”, _con); _cmdInsert.Parameters.Add(“@Path”, SqlDbType.NVarChar, 500); } public int NumberOfRequests { get { int result = 0; _cmdSelect.Parameters[“@Path”].Value = Request.AppRelativeCurrentExecutionFilePath; try { _con.Open(); result = (int)_cmdSelect.ExecuteScalar(); } finally { _con.Close(); } return result; } } void Application_BeginRequest(object sender, EventArgs e) { // Record new request From the Library of Wow! eBook ptg 1485 Working with HTTP Applications and HTTP Modules 31 _cmdInsert.Parameters[“@Path”].Value = Request.AppRelativeCurrentExecutionFilePath; try { _con.Open(); _cmdInsert.ExecuteNonQuery(); } finally { _con.Close(); } } </script> The Global.asax page in Listing 31.22 handles the Application BeginRequest() event. You can handle any application event by following the naming pattern Application_EventName where EventName is the name of the HttpApplication event. In Listing 31.22, the Application_BeginRequest() handler records the path of the page requested. A SqlCommand object records the page path to a database table named Log. The Global.asax file also extends the base HttpApplication class with a custom property named NumberOfRequests. This property retrieves the number of requests made for the page at the current path. Finally, the Global.asax includes an Init() method that overrides the base HttpApplication’s Init() method. In Listing 31.22, the Init() method initializes the SqlConnection and two SqlCommand objects used in the Global.asax file. The Init() method is called when the class represented by the Global.asax is initialized. It is called only once, when the class is first created. WARNING The same instance of the HttpApplication object is reused for multiple page requests (although never for multiple page requests at the same time). Any value that you assign to a property in a Global.asax file is maintained over the multiple page requests. The page in Listing 31.23 displays the value of the custom property exposed by the Global.asax file (see Figure 31.7). The ApplicationInstance property is used to refer to the instance of the HttpApplication class associated with the page. Because the Global.asax file is compiled dynamically in the background, any properties that you declare in the Global.asax file are exposed as strongly typed properties. From the Library of Wow! eBook ptg 1486 CHAPTER 31 Working with the HTTP Runtime FIGURE 31.7 Displaying the NumberOfRequests property. LISTING 31.23 ShowGlobal.aspx <%@ Page Language=”C#” %> <!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>Show Global</title> </head> <body> <form id=”form1” runat=”server”> <div> This page has been requested <%= this.ApplicationInstance.NumberOfRequests %> times! </div> </form> </body> </html> From the Library of Wow! eBook ptg 1487 Working with HTTP Applications and HTTP Modules 31 Creating Custom HTTP Modules An HTTP Module is a .NET class that executes with each and every page request. You can use an HTTP Module to handle any of the HttpApplication events that you can handle in the Global.asax file. Behind the scenes, ASP.NET Framework uses HTTP Modules to implement many of the standard features of the framework. For example, ASP.NET Framework uses the FormsAuthenticationModule to implement Forms authentication and the WindowsAuthenticationModule to implement Windows authentication. Session state is implemented with an HTTP Module named the SessionStateModule. Page output caching is implemented with an HTTP Module named the OutputCacheModule, and the Profile object is implemented with an HTTP Module named the ProfileModule. When a new instance of an HttpApplication class is created, the HttpApplication loads all the HTTP Modules configured in the web configuration file. Each HTTP Module subscribes to one or more HttpApplication events. For example, when the HttpApplication object raises its AuthenticateRequest event, the FormsAuthenticationModule executes its code to authenticate the current user. In this section, we create a simple authentication HTTP Module. The HTTP Module doesn’t enable you to request a page unless you include the proper query string with the request. The code for the custom HTTP Module is contained in Listing 31.24. LISTING 31.24 App_Code\QueryStringAuthenticationModule.cs using System; using System.Web; namespace AspNetUnleashed { public class QueryStringAuthenticationModule : IHttpModule { public void Init(HttpApplication app) { app.AuthorizeRequest += new EventHandler(AuthorizeRequest); } private void AuthorizeRequest(Object sender, EventArgs e) { // Get context HttpApplication app = (HttpApplication)sender; HttpContext context = app.Context; // If the request is for Login.aspx, exit From the Library of Wow! eBook ptg 1488 CHAPTER 31 Working with the HTTP Runtime string path = context.Request.AppRelativeCurrentExecutionFilePath; if (String.Compare(path, “~/login.aspx”, true) == 0) return; // Check for password bool authenticated = false; if (context.Request.QueryString[“password”] != null) { if (context.Request.QueryString[“password”] == “secret”) authenticated = true; } // If not authenticated, redirect to login.aspx if (!authenticated) context.Response.Redirect(“~/Login.aspx”); } public void Dispose() { } } } The class in Listing 31.24 implements the IHttpModule interface. This interface includes two methods: . Init—Enables you to subscribe to HttpApplication events. . Dispose—Enables you to clean up any resources used by the HTTP Module. In Listing 31.25, the Init() method adds an event handler for the HttpApplication AuthorizeRequest event. When the HttpApplication raises the AuthorizeRequest event, the HTTP Module’s AuthorizeRequest() method executes. The AuthorizeRequest() method checks for a password=secret query string. If the query string does not exist, the user is redirected to the Login.aspx page. (The method also checks whether the user is requesting the Login.aspx page to avoid a vicious circle.) Before you can use the QueryStringAuthenticationModule, you must register the HTTP Module in the web configuration file. The web configuration file in Listing 31.25 includes an <httpModules> section that registers the module. LISTING 31.25 Web.Config <?xml version=”1.0”?> <configuration> <system.web> <httpModules> From the Library of Wow! eBook ptg 1489 Summary 31 <add name=”QueryStringAuthenticationModule” type=”AspNetUnleashed.QueryStringAuthenticationModule”/> </httpModules> </system.web> </configuration> After you register the HTTP Module, if you attempt to request any page without including the password=secret query string, you are redirected to the Login.aspx page. (If the Login.aspx page doesn’t exist, you receive a 404 Not Found error message.) Summary In this chapter, you learned how to extend the ASP.NET Framework by extending different parts of the HTTP Runtime. In the first section, you learned how to create a custom BuildProvider. For example, you learned how to create a BuildProvider that dynamically generates a data access component from an XML file. Next, you explored the topic of ExpressionBuilders. You learned how to use an ExpressionBuilder to automatically replace one expression with another. For example, we created a custom ExpressionBuilder that enables you to look up a value from an XML file. The topic of HTTP Handlers was also explored. You learned two methods of creating custom HTTP Handlers. You learned how to create a Generic Handler and how to create an HTTP Handler by implementing the IHttpHandler interface. You also saw how you can improve the scalability of your ASP.NET applications by implementing asynchronous HTTP Handlers. Finally, you learned two methods of handling application-wide events. You learned how to create a custom HttpApplication by creating a Global.asax file. You also learned how to handle application events by implementing a custom HTTP Module. From the Library of Wow! eBook ptg This page intentionally left blank From the Library of Wow! eBook ptg CHAPTER 32 Building Dynamic Data Applications IN THIS CHAPTER . Introducing ASP.NET Dynamic Data . Building a Dynamic Data Application . Working with Dynamic Data Templates . Summary ASP.NET Dynamic Data originally appeared in .NET Framework 3.5 Service Pack 1 release and its templates came with Visual Studio 2008 SP1. This chapter provides you with an overview of ASP.NET Dynamic Data and walks you through the process of building an application with it and discuss when you might (and might not) build a dynamic data application. When you finish this chapter you should have a good idea of what it’s like to build dynamic data applications and the productivity benefits they provide for developers, and you can compare and contrast this framework with traditional Web Forms and ASP.NET MVC Framework. Introducing ASP.NET Dynamic Data The hallmark of every good programmer is the ability to locate redundant code and factor it out into a reusable module or library. Many people these days equate good programming skills with writing far less lines of code than we used to while still producing incredible applications. When we build ASP.NET applications that sit on top of a data store of some kind, there are always a set of common tasks that we need to provide a UI for. Although the data models can vary from application to application, several common patterns emerge in virtually any data-driven appli- cation. Just a few of these patterns are listed here: . Entity Lists—Display a list of rows in a table. . Detail Form—Displays the details of a single entity. From the Library of Wow! eBook ptg 1492 CHAPTER 32 Building Dynamic Data Applications . Edit Form—Enables the user to edit the details of a single entity. . Create—Adds a new entity to a list. . Delete—Deletes an entity from a list. . Navigation—Provides Search, Filter, Sort, and Paginate lists of entities. These tasks are common to virtually all web applications regardless of the underlying data model. The ASP.NET Dynamic Data framework takes this into account and, out-of-the-box, gives you scaffolding and templates that can provide all this functionality for you without you writing more than a few lines of code. In the next section, you see how to build a dynamic data application, and you see the features and functionality provided for you as a starting point. Building a Dynamic Data Application Before we start building a sample application, we need something to build. In the spirit of keeping things simple so that we can focus this chapter solely on dynamic data, the appli- cation must also be simple. We call it ZombiePedia, a website for cataloguing the appearances of various zombies. This way, during the zombie apocalypse, we can all hit ZombiePedia from our smart phones to log zombies we’ve seen and look up repeat zombies to find their vital statistics. After all, we need to know if we can touch a zombie with bare hands or if it’s a virus carrier, don’t we? Without dynamic data, we would have to hand-code the page that displays the list of zombie sightings, the sighting detail and edit forms, the ability to create new sightings, and the pages that add, remove, and edit zombie types, and a bunch more plumbing. Doing this by hand, even with a rapid development framework like the MVC Framework, would be tedious and time-consuming. If our goal is to produce a functioning prototype that we can use as the launching point to start building our production application, we should use dynamic data. To start, let’s create a new web application. Open up Visual Studio 2010 and create a new project. Under the Installed Templates panel in the Web category, click ASP.NET Dynamic Data Entities Web Application (shown in Figure 32.1). Call the application whatever you like, but ZombiePedia might be a good choice. This particular template creates an ASP.NET Dynamic Data website that comes pre- equipped with code, scaffolding, and references to build a dynamic user interface on top of ADO.NET Entity Framework Entity Data Model (EDM). Before we go any further, we need to add the entity data model to our project. You can mock this up quickly by creating two simple tables in a SQL database: ZombieType (ID, Name, Description) and ZombieSighting (ID, Name, Longitude, Latitude, and so on). Figure 32.2 shows the dialog box with which you are presented after right-clicking the project and choosing to add a new ADO.NET Entity Model and choosing Generate from Database. From the Library of Wow! eBook ptg 1493 Building a Dynamic Data Application 32 With a dynamic data website created and an entity data model added to the project, we’re almost ready to run the application—no code written yet! First, let’s take a look at the project that Visual Studio 2010 created for us. FIGURE 32.2 Adding an ADO.NET Entity Data Model by Generating from Database. FIGURE 32.1 Creating a new ASP.NET Dynamic Data Entries Project. From the Library of Wow! eBook . Init(HttpApplication app) { app.AuthorizeRequest += new EventHandler(AuthorizeRequest); } private void AuthorizeRequest(Object sender, EventArgs e) { // Get context HttpApplication app = (HttpApplication)sender;. Library of Wow! eBook ptg 148 5 Working with HTTP Applications and HTTP Modules 31 _cmdInsert.Parameters[“@Path”].Value = Request.AppRelativeCurrentExecutionFilePath; try { _con.Open(); _cmdInsert.ExecuteNonQuery(); }. HttpContext context = app.Context; // If the request is for Login.aspx, exit From the Library of Wow! eBook ptg 148 8 CHAPTER 31 Working with the HTTP Runtime string path = context.Request.AppRelativeCurrentExecutionFilePath;