Understanding .NET (2nd Edition)
Creating .aspx pages, using controls, defining applications, and exploiting context are fundamental to creating any ASP.NET application. Most applications need more than this, however, including a way to manage state, mechanisms for managing information about their users, and more. The rest of this chapter takes a look at these important areas. Managing State
Every request a client makes to an ASP.NET application causes the objects on the loaded page to be created, used, and then destroyed. This helps in building scalable applications, since no resources are taken up on the server for clients that aren't currently running requests, and load balancing requests across a group of servers also gets easier. This stateless behavior can also make writing those applications more difficult, however. If all of an application's objects forget everything they've been told after every request, the application won't be very intelligent. To make it easier to create smarter software, ASP.NET provides several different ways for an application to maintain state information between client requests. These options divide into two major categories: storing state on the client and storing state on the server.
Storing State on the Client
One way for an ASP.NET application to maintain state between requests from a client is to send that state to the client, then have the client send the state back on its next request. A common way to do this is by using cookies. A cookie is a chunk of information that the ASP.NET application sends to the browser, and it can contain anything: a key for a database lookup, a user identity, or something else. Whatever it is, the user's browser automatically provides the cookie on the next request (unless the user has disabled this function), allowing the application to access and use the information the cookie contains.
It's also possible to store some simple state in a query string, which is text following a "?" at the end of a URL. This information can be expressed as attribute/value pairs, like this: http://www.qwickbank.com/example.aspx?account=492284 & type=checking
Query strings are only useful for small amounts of state, and they're not at all secure. Still, they're the right answer in some situations, and they're quite commonly used. As already described, the properties in Web controls are automatically saved by inserting their values into the Web pages sent to the user and then reading those values out again when the page is sent back. What actually happens is that this state information is inserted into one or more hidden fields in the page, then read back from those fields when the page is returned. This approach, referred to as view state, offers another way for an application developer to store any other information maintained by a particular page. This mechanism allows an application to store values inside the page sent back to the browser, then have them restored when the user resubmits the page.
Storing State on the Server
Storing state on clients is simple, and it's commonly used. Only a relatively small amount of information can be stored, however, and there are other limitations as well. Accordingly, ASP.NET applications often store state on the server.
A limitation of view state, for example, is that it saves information only for a particular page in an ASP.NET application. What if you want to save information used by several pages in the application? To do this, an application can rely on the Application object mentioned earlier. Rather than being sent to the client with each page, information placed in the Application object is maintained on the server and made accessible to all pages in an application. A reference to the Application object from any page in an ASP.NET application will always access the same instance. For example, if an application wishes to record the time at which some event takes place and then let that time be accessed by another page in the same application, it might contain the following line: <% Application("Timestamp") = Now() %>
A page that wished to access the value of this variable can reference it directly, as in <% Response.Write (Application("Timestamp")) %>
This book hasn't said much about threading in the .NET Framework, but it's important to note that ASP.NET applications are multithreaded. Since there's only one Application object for an entire ASP.NET application, if two different pages running on two different threads write to it simultaneously, problems can occur. Depending on the threading choices an application uses, explicit concurrency controls might be needed to avoid conflicts. Alternatively, the Cache object can be used instead of the Application object. As mentioned earlier, the objects are broadly similar in function. The biggest difference is that the Cache object allows much more control over how long information is held and what causes it to be removed. An application can also ask that it be informed when a particular item is deleted from the Cache object, providing an event handler that runs when the information is deleted.
Storing state in either of these objects has a problem, however: Both are physically stored on a single machine. If an ASP.NET application is deployed on several Web servers, with client requests load balanced across those servers, storing state in the Application or Cache objects will be problematic. Each copy of the application will have its own instance of these objects, so the information these instances contain will likely be different. Applications that will be load balanced should be careful about how, or even if, they use these objects.
The options just described can be useful ways for an application to store state. Yet none of them addresses the most common state management problem in building ASP.NET applications: maintaining per-client state. Think of a Web application that allows its user to add items to a shopping cart, for example. Given that the objects created by the application are destroyed after every request, how can that information be retained? Every page accessed by that client (and only that client) within an ASP.NET application should have easy access to this per-client state, but nothing described so far provides this. Yet building reasonable applications requires some way to store client-specific information across the life of a client session. In ASP.NET, the Session object provides a way to do this.
Accessing a Session object looks much like accessing the Application object. To store a value, an .aspx page can contain a line such as Session("ItemSelected")= 13
To access that value, the page can refer to it like this: Response.Write(Session("ItemSelected"))
Even though Session and Application objects are accessed in a similar way, don't be confused. There's only one Application object shared by all pages in an ASP.NET application, while every client has its own Session object. When an application accesses the Session object, it will always get the instance associated with the client that made this request. To figure out which client each request comes from, ASP.NET assigns each client a unique session identifier that gets stored in a cookie. The client then presents this cookie with each request it makes, allowing ASP.NET to identify all requests that come from the same client. Because users can turn off cookies if they wish, ASP.NET is also capable of embedding this identifier in the URL string returned to a user. However it's done, the creator of an ASP.NET application doesn't need to worry about determining which request comes from which userit's done for him.
But what about load balancing? Suppose an ASP.NET application's Session object is bound to a particular machine, like the Application object. If this application is load balanced across several different machines, only one of those machines will store this object. Requests from a client that get sent to other machines due to load balancing won't be able to access the object's contents, so they won't execute correctly. While it's possible to use the Session object in this way, only relatively simple applications will adopt this approach.
While the Session object for a particular client can still be stored on just one machine, it can also be stored in a separate Session State Store. If this is done, the contents of the Session object are available to all copies of the application running on all machines in a load-balanced configuration. And because the state is in another process (and perhaps another machine), it can remain available even if the ASP.NET application itself is restarted.
The standard Session State Store server provided with version 2.0 of the .NET Framework provides five options:
Any state in the Session object is automatically destroyed when a client hasn't accessed the application for a configurable length of time. Note too that how the Session object's state is stored depends entirely on the contents of an application's Web.config file. No code in the application needs to be modified to change which option is used. Yet another way for an application to store state information on the server, one that's new in ASP.NET 2.0, is using profile properties. Profile properties let an application persistently store information about particular users in an ASP.NET-provided database. Whenever a user accesses the application, her information can be accessed via these properties. Users are identified using one of ASP.NET's login mechanisms, described later, and the association between a user and her properties is made automaticallythe developer isn't required to do any direct database access. The goal is to provide an easy-to-use mechanism for maintaining information about users across multiple accesses.
Maintaining state is an important and challenging part of building Web applications. As this short summary has described, ASP.NET provides a number of different ways to do it. Which options make sense for a particular application depends on what the application does and how it's used. Even though it would make life simpler, there's no one-size-fits-all solution for state management. Caching Data
Clients of an ASP.NET application often access the same information. To make that access faster, it's possible to cache repetitively accessed data. Rather than doing the work required to recreate that data each time it's requested, the information can be read quickly from an in-memory cache and returned. To allow this, ASP.NET provides a way to cache the output of a page. (Don't confuse this with the Cache object described earlier, which is a separate idea.)
To use ASP.NET's output caching mechanism, an .aspx page can contain a directive such as <%@ OutputCache Duration="60" VaryByParam="none"%> The duration specifies how long (in seconds) the results of that page can be cached before the information must be recreated. Requests that arrive for the same information within this period will have their responses returned immediately from the cache.
The required VaryByParam attribute is set to none in this example, but if desired, it can be used to control exactly which results are cached. The parameter this attribute refers to can be any named value contained in a query string, which is the text following a "?" on a client request. For example, suppose an .aspx page contained the directive <%@ OutputCache Duration="60" VaryByParam= "name"%>
Suppose further that this page received two requests with these query strings: http://www.qwickbank.com/page1.aspx?name=Bob http://www.qwickbank.com/page1.aspx?name=Casey
Because output caching was instructed to vary by a parameter called "name," the results of each request would be cached. Requests to this page with names of either Bob or Casey that are received within the 60-second window specified on the Duration attribute will return cached results. Other things, such as the type of browser from which a request originated, can also be used to control exactly which pages are cached. The result is a more responsive application, especially if the same data is accessed over and over. Authentication and Authorization
Securing Web applications is incredibly important. A plethora of well-publicized attacks have made even nonprofessionals aware of the dangers of too little security. Really understanding Web security is a large topic, one that's outside the scope of this book, but grasping the basics of authentication and authorization is essential.
Authentication, requiring a user to prove his identity, is a fundamental part of securing an ASP.NET application. Although a developer is free to create her own authentication mechanism, ASP.NET applications have several built-in authentication options provided for them. The choices, which build on what IIS provides, include the following:
Once a client has been authenticated, the next step is to make an authorization decision, i.e., to determine whether that client is allowed to do what it's requesting. If one of the Windows authentication options is used, a client's attempt to access a particular file such as an .aspx file will be subject to file authorization. This means that Windows will automatically check the access control list (ACL) on the file to ensure that the user's request is allowed. With Windows authentication or any of the other authentication options, an approach called URL authorization is also available. This option allows fine-grained control over who is allowed to perform what operations against a particular ASP.NET application.
Managing Users: Membership
Many Web applications require users to log in before accessing some or all of what the application provides. Knowing who a user is lets the application provide specific services, such as remembering the user's mailing address, and it can also enforce various business relationships, such as payment of a subscription fee. Doing this requires a way to maintain information about the site's users, commonly called membership information, and a mechanism for logging in that uses that information. Version 2.0 of ASP.NET adds support for both of these.
ASP.NET's membership support allows creating and storing information about users, including names, passwords, and more. This information can be stored in SQL Server (the default) or some other data store, although the details of data access are hidden from the developer. Instead, a Membership class is provided that exposes methods such as CreateUser, DeleteUser, FindUsersByName, GeneratePassword, and more. An ASP.NET application can use these methods to perform typical membership operations without dealing explicitly with the underlying data store.
ASP.NET's membership support can be used together with the forms authentication described earlier, an approach that lets the developer specify in detail exactly what the login process looks like. ASP.NET also includes a set of login controls to help implement membership-based authentication. By default, these controls use the ASP.NET membership system. For example, the CreateUserWizard control collects information from a user such as her user name, password, and e-mail address, then creates an account in the ASP.NET membership system for her. Once this is done, the Login control can be used to let a user log in to an account maintained by the membership system. Other controls are also available, such as a ChangePassword control and a PasswordRecovery control that can let a user have her password sent to whatever e-mail address is registered for her account. To secure the interaction with the user's browser, the login controls can be configured to use SSL.
Working with Data: Data Binding
ASP.NET applications frequently need to access data in a database, a file, or someplace else. Like any .NET Framework application, ASP.NET applications can use ADO.NET, System.XML, or other mechanisms to do this. Yet for some very common requirements, such as displaying data to a user and allowing him to update it, using these relatively low-level technologies can entail a significant amount of work. To make these common problems easier to solve, ASP.NET 2.0 includes a set of Web controls for working with data (controls that differ significantly from those in earlier releases). Their goal is to make it as simple as possible to implement the most common things that ASP.NET applications do with data.
The idea of directly connecting what's displayed by a control with data that lives outside that control, such as data in a database, is referred to as data binding. Doing this requires solving two distinct problems: accessing the data, which might be stored in diverse ways, and displaying that data, again in diverse ways. For example, the data an application uses might be contained in SQL Server or some other database, or in an XML file, or someplace else. Similarly, the application might wish to display that data in a table, a list, or some other fashion. Both problems must be addressed for a complete solution, but they're quite distinct. Accordingly, the Web controls provided by ASP.NET for data binding are divided into two categories: data source controls, which know how to access data, and data-bound controls, which know how to display data. Figure 5-6 shows how these two kinds of controls relate to one another. Figure 5-6. Data-bound controls display data that's accessed via data source controls.
As the figure illustrates, data source controls connect to actual sources of data. Several different data source controls are provided, each capable of connecting to a particular type of data source. The SqlDataSource control uses ADO.NET to connect to a database, for example, such as SQL Server or Oracle, while the XmlDataSource control knows how to access and work with information stored in an XML file. ASP.NET applications can also use business logic implemented in a middle tier, and so the ObjectDataSource control is capable of connecting to data contained in objects within this logic. Other data source controls are also provided, and a developer can create her own if necessary.
Once data has found its way into a data source control, it can be accessed by any data-bound control. Each data-bound control knows how to render data in a browser using an appropriate HTML format. The DataList control, for example, is capable of displaying data in a customizable table, while the DetailsView control allows examining and updating individual records. Various list controls, such as ListBox, DropDownList, and BulletedList, allow displaying data in the variety of list styles that user interfaces commonly employ, and the GridView control, new in ASP.NET 2.0, provides a powerful way to examine and modify data in a grid format.
The goal of all of this technology is to make life as simple as possible for ASP.NET developers who build applications that work with data. Rather than writing detailed code to access, display, and update information, a programmer can just configure the appropriate controls. ASP.NET 2.0 introduced significant changes in this area, and the result should be increased developer productivity.
Customizing User Interfaces: Web Parts
People like to be in charge. Even with the potentially mundane, such as how an application looks in a Web browser, giving users the ability to customize things makes people happy. Web Parts, a new addition in ASP.NET 2.0, makes this possible. By allowing users to modify the appearance and behavior of a Web page, add new controls, and more, Web Parts let those users change an application's interface to suit them.
ASP.NET Web Parts is actually just a set of controls. A page that uses these controls can contain one or more zones, each of which can contain one or more Web Parts controls. These controls expose actions to a user, such as the ability to change the content the control displays or change the control's position on the screen. Users can also change the pages themselves, including adding new columns with specific Web Parts controls, changing the page's color, and more. This kind of interface customization is commonly referred to as personalization.
As usual with ASP.NET, applications that use Web Parts can be created using Visual Studio 2005. Like any other control, a Web Parts control can be dragged and dropped onto a page, then customized as needed. More advanced developers can create their own Web Parts controls, all of which inherit from the System.Web.UI.WebControls.WebParts.WebPart class. Wherever the controls come from, a page will always contain one WebPartManager control that's responsible for coordinating the activity of all other Web Parts controls on this page. A page will also commonly contain controls such as CatalogZone, which allows presenting users with a list of Web Parts controls that can be added to this page, and EditorZone, which lets users modify a page's Web Parts controls.
ASP.NET Web Parts also includes its own mechanism for persistently storing the changes a user makes. This allows someone to access the application, modify their interface in any way they like, then have this modified interface presented when they next use this application. This storage mechanism is entirely separate from the profile properties described earlier, as it's intended to be used only by Web Parts controls.
The Web Parts technology is a useful addition to ASP.NET. By letting developers create portal-like applications, it expands the range of problems that the .NET Framework can address. As always, this expansion increases the complexity for developers, but for applications that need it, the capability is essential.
|
Категории