Building the User Interface


This section presents a sample user interface built on the logic layer developed in the previous section. The coverage here focuses on how the user interface employs the logic layer and the SQL-NS API itself to implement subscription management. It does not cover user interface programming fundamentals.

Note

Although only parts of it are shown in this chapter, the complete source code for the user interface is available in the SMI Visual Studio solution, described in the section "The SMI Visual Studio Solution" (p. 192). This code is thoroughly commented, so reading through it should give you a good idea of how the user interface works.


User Interface Technologies

A number of user interface frameworks are available for you to use when building an SMI. The most common ones are ASP.NET and Windows Forms. ASP.NET allows you to build an SMI as a web application, and Windows Forms allows you to build an SMI as a Windows application.

Both ASP.NET and Windows Forms work with managed code. You can also build an SMI user interface using native code technologies such as ASP (the predecessor to ASP.NET) and Microsoft Foundation Classes (MFC). To do so, you have to use the SQL-NS API through COM interop, as described in the SQL-NS Books Online.

The user interface we look at in this section is a website, built with ASP.NET 2.0, that presents a simple front end to the music store. The site does let you browse the music store's catalog, but most of its functionality is designed to facilitate managing subscriptions in the music store's notification application. For simplicity, other aspects of a typical music store, such as ordering music or playing sample songs, are not implemented in our website.

We'll examine the code behind the website's user interface for managing subscriptions and test it using the local web server built into Visual Studio 2005. You will not need a full Internet Information Server (IIS) installation to run the website.

Note

Although deep knowledge of ASP.NET development is not required to understand the material in this section, some familiarity with the basic concepts is assumed. To learn more about ASP.NET and web development, I recommend the tutorials in the MSDN library and those available online.


Running the Sample User Interface

Before we analyze the code, you should run it and play with the user interface to get a feel for how it works. This section describes the various parts of the interface and walks you through using it to create and manipulate subscriptions. To begin, open the SMI solution in Visual Studio (if you haven't already done so), go to the Debug menu and select Start Debugging (or press F5) to build and run the SMI website.

The website starts on the home page, which displays the list of songs in the music store catalog. This list is read from the database when the page is loaded. Only 10 songs are displayed at a time; to see more songs, use the page navigation links at the bottom of the song list.

A list of links is displayed in the left pane. When you first start the website, you'll see two links in the list: one labeled Login, which leads to a login page for existing users, and another labeled Register New User, leading to a page on which new users can create an account.

Click the Register New User link to create the first account in the music store website. The registration page presents a form in which you specify the new account details. Enter a username, password, email address, and security question and answer for the new account. The username can be any name you choose; it does not need to correspond to an existing Windows or SQL username. When you're done filling out the form, click the Create User button to create the account.

Tip

If clicking the Create User button resulted in an ASP.NET error page with a message that reads

    Could not find stored procedure 'dbo.aspnet_CheckSchemaVersion'. 


or another similar error message, you probably have not created the database objects required by the ASP.NET SqlMembershipProvider. Go back and complete the instructions given in the section "Creating the Database Objects for the ASP.NET Membership Provider" (p. 192) and then try to create the user again.


Caution

The scripts we used to test the music store application previously created subscribers called "Bob", "Mary", "Alex", and "Emily". Do not use any of these as usernames when creating accounts through the SMI website. If you do, you'll encounter an error later on when the SMI attempts to create a subscriber record for the account (because records with these names already exist).


After the account is created, the website will display a status message telling you that the account was created successfully. Click the Continue button to proceed. This redirects you back to the home page. The user account just created is now logged in. Notice that the list of links has changed: the Login link has been replaced with a Logout link and a new link labeled Manage Subscriptions has replaced the Register New User link. As you can see, the list of links changes based on whether a user is logged in. If you log out, the links will revert back to the ones you saw earlier, before you logged in.

Click the Manage Subscriptions link to open the page with the main subscription management user interface. Figure 7.4 shows a screenshot of this page.

Figure 7.4. The user interface for managing subscriptions.


The page is divided into two main sections: one for managing subscriptions by artist and the other for managing subscriptions by genre. Each section has a set of controls for adding new subscriptions, a list of existing subscriptions, and a set of buttons to enable, disable, and remove those existing subscriptions.

To see the page in action, enter a NewSongByArtist subscription by typing an artist name into the text box labeled Artist Name and then clicking the Add Subscription button. The artist name you entered will be added to the list of current subscriptions. You can enable, disable, or remove this subscription by selecting it in the list and clicking the appropriate button on the right.

Add a NewSongByGenre subscription by selecting a genre from the Genre Name drop-down list, specifying a schedule time and time zone using the other drop-down lists, and then clicking the Add Subscription button. The genre name will be added to the list of existing subscriptions by genre. Like the subscriptions by artist, subscriptions by genre can also be enabled, disabled, or removed by selecting the genre name in the list and clicking one of the buttons on the right.

You can see the results of the operations you performed through the interface by querying the subscription tables in the database. Run the following queries in Management Studio to see all the NewSongByArtist and NewSongByGenre subscriptions:

 USE MusicStore SELECT * FROM [SongAlerts].[NSNewSongByArtistSubscriptions] SELECT * FROM [SongAlerts].[NSNewSongByGenreSubscriptions] 


Notice the Enabled column in the results of these queries that shows the enabled state for each subscription. When you enable or disable subscriptions through the interface and then rerun these queries, the value in the Enabled column will change. Before continuing, spend some time adding and manipulating additional subscriptions through the user interface and observe that the changes you expect are made to the records in the database.

Examining the User Interface Code

The SMI website is implemented in a series of ASP.NET web forms. Each form is defined in a .aspx file and supported by C# code in a corresponding .aspx.cs file. You can see these files in the SMI solution using the Visual Studio Solution Explorer. The controls for subscription management operations are laid out in the ManageSubscriptions.aspx web form (this is the page that loaded when you clicked the Manage Subscriptions link in the website). The operations behind these controls are implemented in C# code in the corresponding ManageSubscriptions.aspx.cs file. In this section, we focus on the parts of this code that use the logic layer methods we implemented earlier and those that use the SQL-NS API directly. Other parts of the website code are not covered here because they do not relate specifically to SQL-NS.

Open the ManageSubscriptions.aspx.cs file in Visual Studio (you may need to expand the ManageSubscriptions.aspx node in the Solution Explorer tree to see the ManageSubscriptions.aspx.cs file). The code in this file implements parts of the ManageSubscriptions class, which provides event handlers and other supporting code for the controls in the ManageSubscriptions.aspx page. In the following sections, we'll look at specific methods in the ManageSubscriptions class.

Initializing the Page

The ManageSubscriptions class implements an event handler called Page_Load(). As its name suggests, this handler is called whenever the ManageSubscriptions.aspx page is loaded. It performs the initialization of all the components in the page. Listing 7.12 shows the code in the Page_Load() handler.

Listing 7.12. Implementation of the Page_Load() Event Handler

 public partial class ManageSubscriptions : System.Web.UI.Page {     private MusicStoreSMI.MusicStoreSMIOperations smiOperations;     protected void Page_Load(object sender, EventArgs e)     {         try         {             // Connect to the SMI logic layer.             smiOperations = new MusicStoreSMI.MusicStoreSMIOperations();             if (!IsPostBack)             {                 // Check if a subscriber record exists for the currently                 // logged in user. If not, create one. The subscriber id                 // is the user name.                 string subscriberId = User.Identity.Name;                 if (!string.IsNullOrEmpty(subscriberId))                 {                     if (!smiOperations.SubscriberExists(subscriberId))                     {                         Subscriber subscriber = smiOperations.AddSubscriber(                             subscriberId, true);                     }                     else                     {                         // Display this subscriber's subscriptions.                         PopulateSubscriptions(subscriberId);                     }                 }                 else                 {                     // The user name was null or empty, meaning                     // there is no logged in user. Redirect to the                     // login page.                     Response.Redirect("~/Login.aspx");                 }                 // Populate the list of genres shown in the dropdown.                 PopulateGenres();                 // Populate the drop-down lists used to specify schedules.                 PopulateScheduleControls();              }          }          catch (Exception ex)          {              HandleInternalError(ex);          }     }     ... } 

As Listing 7.12 shows, The ManageSubscriptions class declares an instance of the logic layer class MusicStoreSMIOperations as a private member:

     private MusicStoreSMI.MusicStoreSMIOperations smiOperations; 


The Page_Load() handler creates a new instance of this class and assigns it to the private member:

        smiOperations = new MusicStoreSMI.MusicStoreSMIOperations(); 


This instance of the logic layer class is used to invoke the logic layer operations and helper methods throughout the user interface code.

Recall that in its constructor, the logic layer class MusicStoreSMIOperations established connections to the instance and application databases and threw an exception if these connections could not be created. The instantiation of the logic layer class in the Page_Load() handler appears within a TRy-catch block that catches any exceptions and displays an appropriate error message. As you'll see in subsequent sections, other methods in the ManageSubscriptions class also use a similar TRy-catch block to catch and handle exceptions generated by other logic layer operations.

After instantiating the logic layer class, the Page_Load() handler performs a series of steps that initialize the controls on the page (if the page load is not a post back). The first step is to obtain the name of the currently logged in user. This is used as the subscriber ID for SQL-NS purposes. ASP.NET makes the username of the currently logged in user available through the User.Identity.Name property. If this property returns a null or empty string, no user is currently logged in. In this case, the page redirects to the login page of the website.

If there is a currently logged in user, the next step calls the SubscriberExists() helper function on the logic layer class to determine whether a subscriber record already exists for the user. If no subscriber record exists, the code invokes the AddSubscriber() operation on the logic layer class to create a new subscriber record. As you saw in Listing 7.2, this operation also adds a subscriber device for the subscriber.

If a subscriber record already exists for the currently logged in user, Page_Load() calls a helper method, PopulateSubscriptions(), to display the user's subscriptions in the two list boxes you saw earlier. We'll look at the code for PopulateSubscriptions() in the next section.

The remaining code in the Page_Load() handler calls helper methods to populate the other controls on the page (the drop-down lists used to select genres and specify subscription schedules). I won't go into the details of these helper methods here, but you should look at the code in the source file. Notice that the PopulateGenres() helper method uses the enumeration of genres we implemented on the logic layer class.

Displaying Subscriptions

As you saw in the previous section, when the ManageSubscriptions.aspx page is loaded, its Page_Load() handler calls a helper method, PopulateSubscriptions(), to display the existing subscriptions of the currently logged in user. Listing 7.13 shows the implementation of PopulateSubscriptions().

Listing 7.13. Implementation of the PopulateSubscriptions() Helper Method

 public partial class ManageSubscriptions : System.Web.UI.Page {     ...     private void PopulateSubscriptions(string subscriberId)     {         // Obtain the subscriber object.         Subscriber subscriber = smiOperations.Subscribers[subscriberId];         // Get an enumeration of the NewSongByArtist subscriptions.         SubscriptionEnumeration subscriptions =             smiOperations.GetNewSongByArtistSubscriptions(subscriber);         // Clear any items currently in the list box.         ListBoxNewSongByArtistSubscriptions.Items.Clear();         // Enumerate the subscriptions and add the artist names to         // the list box.         foreach (Subscription s in subscriptions)         {             ListBoxNewSongByArtistSubscriptions.Items.Add(                 (string)s["ArtistName"]);         }         // Get an enumeration of the NewSongByGenre subscriptions.         subscriptions =             smiOperations.GetNewSongByGenreSubscriptions(subscriber);         // Clear any items currently in the list box.         ListBoxNewSongByGenreSubscriptions.Items.Clear();         // Enumerate the subscriptions and add the genre names to the         // list box.         foreach (Subscription s in subscriptions)         {             ListBoxNewSongByGenreSubscriptions.Items.Add(                 (string)s["GenreName"]);         }     }     ... } 

PopulateSubscriptions() takes a subscriber ID as a parameter and displays, in the page's two list boxes, all the NewSongByArtist and NewSongByGenre subscriptions belonging to that subscriber. The code begins by obtaining the Subscriber object for the given subscriber ID from the Subscribers enumeration on the logic layer object.

The logic layer method GetNewSongByArtistSubscriptions() is called to obtain an enumeration of the subscriber's NewSongByArtist subscriptions. The code then iterates over the Subscription objects in this enumeration, adding the value of the ArtistName field in each one to the first list box. In a similar way, the GetNewSongByGenreSubscriptions() method on the logic layer object is called to obtain the NewSongByGenre subscriptions, and the values of the GenreName fields are displayed in the other list box.

Adding Subscriptions

When a user clicks one of the Add Subscription buttons in the user interface, control is routed to the click event handler for the button. Listing 7.14 shows the implementation of the click event handler for the Add Subscription button in the subscriptions by artist area of the user interface.

Listing 7.14. Implementation of the Click Event Handler for the Button That Adds Subscriptions by Artist

 public partial class ManageSubscriptions : System.Web.UI.Page {     ...     protected void ButtonAddNewSongByArtistSubscription_Click(         object sender,         EventArgs e)     {         try         {              // Obtain the artist name from the text box.              string artistName = TextBoxArtistName.Text.Trim();              if (!string.IsNullOrEmpty(artistName))              {                  // Obtain the subscriber id from the user name. We                  // can safely use this here because we're guaranteed                  // to have a logged in user (the page load handler                  // redirects anonymous access to the login page).                  string subscriberId = User.Identity.Name;                  // Obtain the subscriber object from the logic layer.                  Subscriber subscriber =                      smiOperations.Subscribers[subscriberId];                  // Add a subscription for this subscriber.                  smiOperations.AddNewSongByArtistSubscription(                      subscriber, artistName);                  // Add the artist name to the list box showing                  // current subscriptions.                  ListBoxNewSongByArtistSubscriptions.Items.Add(artistName);                  // Change the status display.                  ShowSuccessStatusInLabel(                  LabelAddNewSongByArtistSubscriptionStatus,                  "Subscription added successfully.");              }              else              {                  ShowErrorStatusInLabel(                      LabelAddNewSongByArtistSubscriptionStatus,                      "You must specify an artist name.");              }         }         catch (Exception ex)         {             HandleInternalError(ex);         }     }     ... } 

The event handler first obtains the value entered in the Artist Name text box. If this value is null or an empty string, an error message is displayed telling the user that an artist name must be specified.

If the artist name read from the text box is a non-empty string, the code proceeds to add the subscription. It obtains the subscriber ID of the currently logged on user (again, via the User.Identity.Name property) and then gets the corresponding Subscriber object from the enumeration returned by the logic layer's Subscribers property. The next step calls the AddNewSongByArtist method on the logic layer object, passing the subscriber object and the artist name. Finally, the code updates the list of current subscriptions in the list box by adding the new artist name and displays a status message indicating success.

The click event handler for the button that adds NewSongByGenre subscriptions is very similar to the one shown in Listing 7.14. The main difference is that NewSongByGenre subscriptions require a schedule to be specified, so the code must read the start time and time zone from the appropriate controls in the user interface and pass these into the AddNewSongByGenreSubscription() method on the logic layer. You should look at the code for the handler, called ButtonAddNewSongByGenreSubscription_Click(), in the source file.

Enabling, Disabling, and Removing Subscriptions

In the subscription management page, the buttons to the right of the list boxes allow users to enable, disable, or remove subscriptions. As you might expect, the click event handlers for these buttons implement these operations. Listing 7.15 shows the implementation of the click event handler for the button that enables NewSongByArtist subscriptions.

Listing 7.15. Implementation of the Click Event Handler for the Button That Enables Subscriptions by Artist

[View full width]

 public partial class ManageSubscriptions : System.Web.UI.Page {     ...     protected void ButtonEnableNewSongByArtistSubscription_Click(         object sender,         EventArgs e)     {         try         {             Subscription subscription =                 GetSelectedNewSongByArtistSubscription();             if (null != subscription)             {                 // Set the enabled property.                 subscription.Enabled = true;                 // Update the subscription.                 subscription.Update();                 // Change the status display.                 ShowSuccessStatusInLabel(                     LabelRemoveNewSongByArtistSubscriptionStatus,                     "Subscription enabled successfully.");             }             else             {                 ShowErrorStatusInLabel(                 LabelRemoveNewSongByArtistSubscriptionStatus,                 "You must select an artist name from the list to enable the associated  subscription.");             }         }         catch (Exception ex)         {             HandleInternalError(ex);         }     }     ... } 

First, the click event handler obtains the Subscription object for the subscription currently selected by calling a helper method, GetSelectedNewSongByArtistSubscription(). Before we continue, let's take a look at the implementation of this method, shown in Listing 7.16.

Listing 7.16. Implementation of the GetSelectedNewSongByArtistSubscription() Helper Method

 public partial class ManageSubscriptions : System.Web.UI.Page {     ...     private Subscription GetSelectedNewSongByArtistSubscription()     {         Subscription subscription = null;         // Obtain the artist name from the text box.         string artistName =             ListBoxNewSongByArtistSubscriptions.SelectedValue;         if (!string.IsNullOrEmpty(artistName))         {             // Obtain the subscriber id from the user name. We             // can safely use this here because we're guaranteed             // to have a logged in user (the page load handler             // redirects anonymous access to the login page).             string subscriberId = User.Identity.Name;             // Obtain the subscriber object from the logic layer.             Subscriber subscriber = smiOperations.Subscribers[subscriberId];             // Find the subscription object.             SubscriptionEnumeration subscriptions =                 smiOperations.GetNewSongByArtistSubscriptions(subscriber);             foreach (Subscription s in subscriptions)             {                 if ((string)s["ArtistName"] == artistName)                 {                     subscription = s;                     break;                 }             }         } 1         return subscription;     }     ... } 

GetSelectedNewSongByArtistSubscription() begins by obtaining the selected artist name from the list box. If no artist name was selected (indicated by a null or empty string being returned from the list box's SelectedValue property), the method returns null.

If an artist name was selected, the code proceeds to find the associated Subscription object. It does this by enumerating the subscription objects for the currently logged on user's NewSongByArtist subscriptions and comparing the ArtistName field in each one to the selected artist name. When a match is found, the associated Subscription object is returned.

Going back to Listing 7.15, after the call to GetSelectedNewSongByArtistSubscription(), the click event handler checks the returned value. If it's null (meaning no subscription was selected), it displays an error message telling the user to select a subscription. Otherwise, it sets the Enabled property on the subscription object to true and calls the Update() method.

The code for disabling subscriptions is almost the same. The click event handler for the button that disables NewSongByArtist subscriptions, ButtonDisableNewSongByArtistSubscription_Click(), differs from Listing 7.15 only in the line that sets the Enabled property on the subscription object; it sets Enabled to false, instead of true.

Note

The Enabled property on a subscription object behaves like an on/off switch for the subscription. When a subscription is disabled, it is never evaluated when events arrive. However, undelivered notifications generated when the subscription was enabled may still be delivered after the subscription becomes disabled.

The code shown in Listing 7.15 uses the Update() method on the subscription object to write a change to the Enabled property back to the underlying subscription record in the application database. Update() can also be used to write back changes to other properties, including the values of the subscription fields. This is useful if your SMI supports editing existing subscriptions.


Notice that enabling and disabling subscriptions are cases in which the user interface invokes the SQL-NS API directly to perform subscription management operations. There are no wrappers in the logic layer class for enabling or disabling subscriptions, because the API provides this functionality in a way that's convenient to use directly.

Another example of the user interface calling the SQL-NS API directly is in the code that removes a subscription. Listing 7.17 shows the click event handler for the button that removes NewSongByArtist subscriptions:

Listing 7.17. Implementation of the Click Event Handler for the Button That Removes Subscriptions by Artist

[View full width]

 public partial class ManageSubscriptions : System.Web.UI.Page {     ...     protected void ButtonRemoveNewSongByArtistSubscription_Click(         object sender,         EventArgs e)     {         try         {             Subscription subscription =                 GetSelectedNewSongByArtistSubscription();             if (null != subscription)             {                 // Delete the subscription.                 subscription.Delete();                 // Remove the artist name from the list box.                 ListBoxNewSongByArtistSubscriptions.Items.Remove(                     (string)subscription["ArtistName"]);                 // Change the status display.                 ShowSuccessStatusInLabel(                     LabelRemoveNewSongByArtistSubscriptionStatus,                     "Subscription removed successfully.");             }             else             {                 ShowErrorStatusInLabel(                 LabelRemoveNewSongByArtistSubscriptionStatus,                 "You must select an artist name from the list to remove the associated  subscription.");             }         }         catch (Exception ex)         {             HandleInternalError(ex);         }     }     ... } 

The structure of this event handler is similar to the one that enables subscriptions, shown in Listing 7.15. It uses the same code pattern to obtain the Subscription object for the currently selected subscription. It then calls the Delete() method on the subscription object, which removes the associated subscription record from the database. Finally, it updates the list box of current subscriptions by removing the artist name in the deleted subscription from the list and displaying a status message to the user.

The click event handlers for the buttons that enable, disable, and remove NewSongByGenre subscriptions follow the same patterns as the ones for NewSongByArtist subscriptions shown in this section. Take a look at the code in ManageSubscriptions.aspx.cs to see how these handlers are implemented.




Microsoft SQL Server 2005 Notification Services
Microsoft SQL Server 2005 Notification Services
ISBN: 0672327791
EAN: 2147483647
Year: 2006
Pages: 166
Authors: Shyam Pather

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net