When a user clicks on the Add to Cart button on the Main.aspx page, you'll want the item to be saved in a shopping cart, a key feature of the application that you'll now implement. To save items, you'll use the new Profile service (exposed via the Profile object) in ASP.NET 2.0. Think of the Profile service as an ASP.NET mechanism to persistently store a user's information, similar to the Session object. Unlike a Profile object, however, a Session object is valid only for the duration of a session; after the session has expired, the Session object is deleted. The Profile service, however, retains its information until you explicitly remove it from the data store.
Moreover, the Profile object has several advantages over the Session object, such as:
Profile object data is persisted in data stores, whereas Session variables are saved in memory.
Profile object properties are strongly typed, unlike Session variables, which are stored as objects and typecast during runtime.
Profile properties are loaded only when they're needed, unlike Session variables, all of which are loaded whenever any one of them is accessed.
In this section, you use the Profile service to implement a shopping cart.
In addition to creating the shopping cart itself, you add forms so that when it comes time to check out, users can either log in to access the members' only area of the site, or register and then log in. You'll use Forms authentication rather than Windows authentication to identify a user.
First, you need to create the business object that implements the functionality of a shopping cart. Add a new class to the project and name it ShoppingCart.vb. (Right-click on project name in Solution Explorer and select Add New Item…. Then select the Class template and rename it ShoppingCart.vb.) You will be asked if you wish to save the file in the App_Code folder (see Figure 5-15), which is recommended. Click Yes.
Figure 5-15. Saving the class in the App_Code folder for code reuse
Code the ShoppingCart.vb class file as shown in Example 5-1.
Example 5-1. ShoppingCart.vb
Imports Microsoft.VisualBasic Imports System.Xml.Serialization Namespace OReilly Public Structure itemType Dim isbn As String Dim qty As Integer End Structure <XmlInclude(GetType(itemType))>_ Public Class Cart '---use public for Xml serialization--- Public items As New _ System.Collections.Generic.List(Of itemType) Public Sub AddItem(ByVal isbn As String, _ ByVal qty As Integer) Dim cartItem As New itemType cartItem.isbn = isbn cartItem.qty = qty items.Add(cartItem) End Sub End Class End Namespace
You need to specify the XmlInclude attribute to allow XmlSerializer to recognize a type when it serializes or deserializes the itemType data type.
An item is represented using the itemType structure containing its ISBN number as well as the quantity. The Cart class contains an AddItem method that adds items to a generic List object (located in the System.Collections.Generic namespace). Notice that you use the OReilly namespace to uniquely identify the itemType structure and Cart class that you have defined in this file.
Before building the actual registration and login forms, you'll first specify how users are to be authenticated. With web applications for Internet users, you should use Forms authentication, instead of the Windows authentication that ASP.NET uses by default (see "Forms Versus Windows Authentication"). Changing the authentication mode of a web application from Windows to Forms requires changing the mode attribute of the authentication element in its Web.config file. You'll first need to add a Web.config file to your project.
To add a Web.config file to your project, right-click on the project in Solution Explorer and select Add New Item Web Configuration File). Visual Studio will create and display the contents of the file.
Example 5-2. Setting Forms authentication
… <authentication mode="Forms"/> </system.web> </configuration>
Forms Versus Windows Authentication
In Forms authentication, unauthenticated requests are redirected to a Web Form using HTTP client-side redirection. The user provides a username and password and then submits the form. If the application authenticates the request, the system issues a cookie containing the credentials or a key for reacquiring the identity. Subsequent requests are issued with the cookie in the request headers. They are then authenticated and authorized by an ASP. NET event handler using whatever validation method the application developer specifies.
In Windows authentication, ASP.NET works in conjunction with Microsoft Internet Information Services (IIS) authentication. Authentication is performed by IIS in one of three ways: basic, digest, or Integrated Windows Authentication. When IIS authentication is complete, ASP.NET uses the authenticated identity to authorize access.
It is not feasible for you to create separate Windows accounts for users using your application through the Internet. So Forms authentication is the preferred method for Internet applications.
To use the Profile service to store a user shopping cart, you need to define a profile property for the cart and specify its characteristics. To do that, add the markup shown in bold in Example 5-3 to Web.config.
Example 5-3. Defining the shoppingcart profile proper
<system.web> <anonymousIdentification enabled="true"/> <profile> <properties> <add name="shoppingcart" allowAnonymous="true" type="OReilly.Cart" serializeAs="Xml"/> </properties> </profile> …
You define the type for the shoppingcart profile property as OReilly.Cart. This type refers to the Cart class that you have defined in ShoppingCart.vb. The shoppingcart profile property will be serialized as an XML string so that it can be stored in a database.
To save the value of an object to disk, you need to serialize it into XML or binary format. In this case, you've chosen the XML method.
The <anonymousIdentification> element must be added in addition to the shoppingcart property because an Internet user viewing your cart may not yet be an authenticated user of the application. To keep track of an unknown user, ASP.NET needs to assign a unique identifier to the anonymous user.
Attributes in the Profile Property
Besides defining the name and the type attributes for a profile property, which are both required (any .NET data type; default is string), you can also specify the following attributes:
Indicates whether the property is read-only.
Represents how the property value should be stored in the database. Possible values are String (default), Xml, Binary, and ProviderSpecific.
Is the name of the profile provider to use.
Is the default value of the property.
Indicates whether the property can store values by anonymous users.
Switch to the code behind of Main.aspx and add the code for the imgBtn_Click method shown in Example 5-4. This method retrieves the shopping cart of the current user whether authenticated or anonymous and then adds the selected item to it. The updated shopping cart is then saved to the Profile object.
Example 5-4. Add to Cart button (imgBtn) Click event handler
Protected Sub imgBtn_Click( _ ByVal sender As Object, _ ByVal e As System.Web.UI.ImageClickEventArgs) _ Handles imgBtn1.Click, imgBtn2.Click, imgBtn3.Click Dim myCart As OReilly.Cart '---retrieve the existing cart myCart = Profile.shoppingcart If myCart Is Nothing Then myCart = New OReilly.Cart End If Dim isbn As String Select Case CType(sender, ImageButton).ID Case "imgBtn1" : isbn = "0-596-00812-0" Case "imgBtn2" : isbn = "0-596-00757-4" Case "imgBtn3" : isbn = "0-596-10071-X" End Select lblMessage.Text = "You have added " & isbn myCart.AddItem(isbn, 1) '---save the cart back into the profile Profile.shoppingcart = myCart End Sub
For simplicity, you will add an item selected by the user into the shopping cart, even though the item might already be present in the cart.
Note that this subroutine handles the click event of three ImageButton controls. This is accomplished by the Handles statement:
Handles imgBtn1.Click, imgBtn2.Click, imgBtn3.Click
When any of the ImageButton controls is clicked, this subroutine will check which control fired the event by first converting the sender object into an ImageButton control and then examining the ID (control name) of the control:
Select Case CType(sender, ImageButton).ID Case "imgBtn1" : isbn = "0-596-00812-0" Case "imgBtn2" : isbn = "0-596-00757-4" Case "imgBtn3" : isbn = "0-596-10071-X" End Select
Of course, if you have a lot of titles on a page you can check the ISBN using a database, but for this simple example you will hardcode the information.
Code the Page_Load event so that when the page (Main.aspx) is loaded, it can check the Membership class to check to see if the user is authenticated and print out the related information about the user (see Example 5-5).
Example 5-5. ShoppingCart Page_Load event handler
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim user As MembershipUser = Membership.GetUser If user Is Nothing Then lblMessage.Text = "You have not logged in yet." Else lblMessage.Text = "Hello " & user.UserName End If End Sub
If the user is authenticated, the GetUser method from the Membership class will return information about the authenticated user, or else it will return Nothing.
The Membership class in ASP.NET 2.0 takes on the role of validating user credentials and managing user settings.
To test the application, select Main.aspx in Solution Explorer and then press F5. Since you haven't logged in yet, you should see the message "You have not logged in yet," as shown in Figure 5-16.
If you wish to debug your web application (by using F5), you need to add a Web.config file to your project. By default, if there is no Web.config file when you try to debug your application, Visual Studio will prompt you to add one.
Add a few items into the shopping cart by clicking on the Add to Cart buttons, and the items will then be added to the Profile object. Refresh the App_Data folder in Solution Explorer and you will see the ASPNETDB.MDF database file (see Figure 5-17).
Bear in mind that at this moment, you have not yet been authenticated and are therefore an anonymous user.
Figure 5-16. Loading Main.aspx
Figure 5-17. The ASPNETDB.MDF database file
Let's take a quick look at the information saved by the Profile object. Double-click on the ASPNETDB.MDF file. The Database Explorer window will appear, as shown in Figure 5-18. Expand the Tables item and locate the aspnet_Profile table. This table will contain the items saved in your shopping cart. Right-click on aspnet_Profile and select "Show Table Data."
Improved Debugging Support in ASP.NET 2.0
ASP.NET 1.x required you to explicitly set a start page for your project so that a specific page is loaded when you press F5 to debug the application. In ASP.NET 2.0, you can still set a specific page as the start page if you want. However, in ASP.NET 2.0 the start page by default is the currently selected page (currently selected either because you're editing it or because you selected the page in Solution Explorer). This feature saves you the trouble of setting a start page when you just want to debug a page you're working on at the moment.
This option is configurable via the project Property Pages dialog. To invoke it, right-click on the project name, ShoppingApp, in Solution Explorer and then select Property Pages. Select the Start Options item shown in the figure.
Notice that the shoppingcart profile property is saved as an XML string in the PropertyValuesString field (see Figure 5-19).
The string itself is shown in Example 5-6.
Figure 5-18. Viewing the content of the aspnet_Profile table
Figure 5-19. The content of the aspnet_Profile table
Example 5-6. Content of PropertyValuesString
<?xml version="1.0" encoding="utf-16"?> <Cart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http: //www.w3.org/2001/XMLSchema"> <items> <anyType xsi:type="itemType"> <isbn>0-596-00812-0</isbn> <qty>1</qty> </anyType> <anyType xsi:type="itemType"> <isbn>0-596-00757-4</isbn> <qty>1</qty> </anyType> <anyType xsi:type="itemType"> <isbn>0-596-10071-X</isbn> <qty>1</qty> </anyType> </items> </Cart>
The UserID of the user is a long string of characters (a GUID). You can verify this by looking into the aspnet_Users table (see Figure 5-20).
Figure 5-20. Content of the aspnet_Users table
Anonymous ID and GUID
If anonymous identification is enabled, when an un-authenticated user tries to save information into the Profile object, an anonymous user ID is generated for the user. This ID is a GUID (Globally Unique Identifier) that is guaranteed to be unique for each user.
You can programmatically retrieve the anonymous ID for the user via Request.AnonymousId.