Home  |  FAQ  |  About  |  Contact  |  View Source   
 
SEARCH:
 
BROWSE:
    My Hood
Edit My Info
View Events
Read Tutorials
Training Modules
View Presentations
Download Tools
Scan News
Get Jobs
Message Forums
School Forums
Member Directory
   
CONTRIBUTE:
    Sign me up!
Post an Event
Submit Tutorials
Upload Tools
Link News
Post Jobs
   
   
Home >  Tutorials >  General ASP.NET >  A Rooms Reservation Problem Set
Add to MyHood
   A Rooms Reservation Problem Set   [ printer friendly ]
Stats
  Rating: 4.58 out of 5 by 12 users
  Submitted: 10/09/01
Peter Weng ()

 
This is the problem set that started it all. Before devhood was created, 4 out of the 6 creators didn't even know HTML. Here is the problem set that they completed which taught them all they needed to know to create devhood. If you're looking for a way to learn about database backed web applications and .NET technologies this is the problem set for you. Once you've completed this problem set you'll have a good understanding of everything from databases to ASP.NET web applications to web services.

Devhood Boot Camp: Problem Set 1

Objectives:

  • Developing a Microsoft SQLServer backed web service.
  • Maintaining database consistancy.
  • Introduction to ASP.NET, C#, and Web Forms Processing.
  • Developing and invoking remote web services using the .NET Framework.

Helpful Links:

  • - A .NET student community site featuring tutorials and message forums. The site is also source code viewable which provides a great resource for example code.
  • - A great .NET site written by part of the ASP.NET team. Also source code viewable. Well organized code and samples.
  • - A Microsoft .NET community site.
  • - Philip Greenspun's book on web publishing. Although focused on Oracle backed web applications, provides good design principles.
  • - Also focused on Oracle's SQLPlus but still provides a great reference for using RDBMS's for the web.

The Problem

Your job is to develop a Web-based room reservation system for MIT. To first get an idea of the requirements for the system, you sit down and talk with your client. From this dialog, you (the application programmer) can form a big picture of what needs to be done.

Secretary: MIT has the best computer-science department in the world. We have a building full of top researchers, advanced computer systems, and nice lecture halls. We would like a web-based interface for scheduling lecture halls.
You: How are the lecture hall reservations being done now?
Secretary: People email me with the time they need a lecture hall, and how many people they need to hold. I then look up this information in my appointment book, and email them back with open rooms for that time slot that will hold the desired amount of people.
You: Wow, that's a big headache. So how do you envision the web application working?
Secretary: A professor comes to the server and requests a lecture hall from Time x to Time y, capable of holding n people. The server will then offer the user a choice of available lecture halls, each one with a description of the AV facilities. The user should then be able to pick the lecture hall she likes best.
You: Any other features you would like in the system?
Secretary: Yes, professors should be able to boot only students (i.e., professors can't boot other professors) out of a reserved room if they can't find a room that suits them. The system should then send an email alert to the booted student informing them that they were booted and asking them to come back and select another room.
You: So professors and students are the only 2 types of users that the system needs to support? Can any user reserve any lecture hall?
Secretary: No. Most of the lecture halls are reservable by any MIT student or professor, however, some lecture halls require administrative approval from myself or any other designated administrator of the system, unless the lecture hall was reserved by a professor. Professors shouldn't need approval for reserving any lecture hall. So the system should also support students, professors, and a group of administrators. However, any professor or student can be designated a room administrator.
You: So how exactly do these administrators approve rooms?
Secretary: Whenever a reservation is made for a approval-required lecture hall, an email should be sent out to the administrators of that room. The email should contain an encoded URL where the administrator can click to approve or deny the request.

So what exactly are you building?

You've been given a basic idea of how the client wants the system to work. It is now your job to figure out the implementation details as well as layout the system. We are building a program that keeps track of users, groups of users, rooms, and room reservations. Because we are building this system complete from scratch we have to carfeully plan out each piece step by step.

In this problem set, you'll be creating SQLServer tables to keep track of users, rooms, and room reservations. You'll also be learning ASP.NET and C# techniques for processing transactions on the RDBMS (Relational Database Management System) through the use of ASP.NET's Web Forms.

Exercise 0: Set up your server

Still working on this part. Are they gonna install .NET and SQL on their own comps or work off a central server?

Exercise 1: Users and User Groups

Introduction

First we'll need a table to hold users of the system. Our data model will look something like this:

create table users (
  user_id   integer primary key,
  first_names   varchar(100) not null,
  last_name   varchar(100) not null,
  email   varchar(100) not null,
  password   varchar(8) not null constraint length_check check (LEN(password) >= 4 and LEN(password) <= 8) -- constrains password to be 4-8 characters long
)
Remember that in our conversation with the secretary, she stated that professors should be able to boot students out of their reserved room. However by now you might be saying to yourself, "Hey that data model doesn't support different user groups though." A sharp-minded individual quickly realizes that right now we have no way of differentiating between students and professors.

Cleary we need a way of assigning each user to be either a professor or a student. One way to do this is by adding a column to our users table which shows which group each user belongs. This can be done by adding a single line to the data model:

create table users (
  user_id   integer primary key,
  first_names   varchar(100) not null,
  last_name   varchar(100) not null,
  email   varchar(100) not null,
  password   varchar(8) not null constraint length_check check (LEN(password) >= 4 and LEN(password) <= 8) -- constrains password to be 4-8 characters long,
  group   constraint group_check_constraint check (group = 'student' or group = 'professor') -- constrains the value of group to be student or professor.
)
What's wrong with this approach?
  • What if a user was a professor and a student? Not likely but possible. This approach doesn't allow users to be members of more than one group.
  • What if I wanted to add another group, tenured professors, who could boot regular professors? This approach is not very friendly to new groups. For every new type of group that's created we have to alter our table to update the check constraint on the group column to account for the new group.
  • Anything else wrong with it?

Generally speaking, when designing your data model, you want tables to be in at least . What this means is that you want all columns of your table to be functionally dependent on the whole key. Let's try it again:

create table users (
  user_id   integer primary key,
  first_names   varchar(100) not null,
  last_name   varchar(100) not null,
  email   varchar(100) not null,
  password   varchar(8) not null constraint length_check check (LEN(password) >= 4 and LEN(password) <= 8) -- constrains password to be 4-8 characters long,
  group_id   integer not null constraint users_to_groups references groups
)

create table groups (
  group_id   integer primary key,
  group_name   varchar(100) not null
)
Let's examine what's changed here. We've added a column to the users table to keep track of which group they belong by referencing a groups table. We've also created a simple groups table that holds group names, and ids. By referencing the groups table from the users table we've eliminated the problem of adding group types in the future. Now for every new group type, all we need is to add another row into the groups table, and the users table never has to be altered.

However, what about the problem of having users belong to more than one group? Our data model still doesn't support that. A common approach for matching together multiple tables (such as matching users to groups) is to create a table whose only job is to hold mappings between tables. So our final data model would look like this:

create table users (
  user_id   integer primary key,
  first_names   varchar(100) not null,
  last_name   varchar(100) not null,
  email   varchar(100) not null,
  password   varchar(8) not null constraint length_check check (LEN(password) >= 4 and LEN(password) <= 8) -- constrains password to be 4-8 characters long
)

create table groups (
  group_id   integer primary key,
  group_name   varchar(100) not null
)
 
create table user_group_map (
  user_id   integer constraint user_group_map_to_users references users,
  group_id   integer constraint user_group_map_to_groups references groups
)
Now with this data model, for each group that a user is a member of we simply add a row into the user_group_map table. Note that this table does not need a explicit column set as the primary key, rather the combination of user_id and group_id will be the unique key on the table.

What are the advantages of this data model? Well let's see:

  • We can easily add new types of groups to the system without risking the chance of breaking the users table with an alter command.
  • The system is flexible. Users are not limited to being a member of only one group.
  • What if we wanted to know how many members group_id "1" had? Or if we wanted to know which groups user_id "2" belonged to? A simple count(*) SQL query on the mapping table would provide this information quickly.

Now we've gone through the basics of data modeling, and hopefully you now see the importance of starting off with a good data model.

Your Turn

Create the users, group, and user_group_map table as above. The tables as they are shown above should suffice to get you through this problem set. If you want, you can add any columns to the table that you feel would be important information to store. Fields such as registration_date, registration_ip, or birthdate may be appropriate.

You can test that your tables were successfully created by using the SQLServer "sp_help" command. For example "sp_help users" will list all columns of the table users. Make sure that your tables work before moving on.

Exercise 2: Introduction to Web Forms Processing and Database Transactions

Now that you have your tables in place, we need to fill them with some information. The next step will be to build a user registration page so that users can sign up for access to the system. Now we'll begin building the Web Frontend to our system.

Introduction to ASP.NET

ASP.NET technology brings with it Web Forms that can be used to create programmable webpages. Web Forms also ease the task of connecting and transacting with a database. First create a registration form for users to register for an account on the system. You'll want to use ASP.NET's server controls along with the runat=server attribute to make sure that these controls are processed on the server. For example, to create a text box for the user to submit his email address, you would do the following:

<asp:TextBox id="email" runat=server />
The server (MS IIS Web Server) will process this control and output appropriate html to the client (the user's browser). The output will typically look like regular HTML source code since this is a simple control. However for more complicated server controls, it has the ability to output specific html based on the device or browser the client is using.

By giving it an id="email" we can get or set the contents of the server control in our C# script. For instance if we wanted to set the value of the text during a Page Load event, our code with look something like this:

<script language="C#" runat="server">

void Page_Load(Object o, EventArgs e) {
   email.Text = "";
   ...
}

</script>
Notice that because the script is told to runat="server" and because the email textbox control is also told to be processed on the server, the Page_Load event inside the script block to set the contents of the email textbox control. In the same way, we could have used email.Text as an accessor to get the value of the email textbox.

Create a page using ASP.NET's server controls for the elements of the page that will require server-side processing, i.e., textboxes and other form elements. Don't worry about the submit button or processing the form yet. We'll get to that next. Refer to the .NET Framework Documentation and API to help you build this page. Name this file registration.aspx. The .aspx extension tells the server that there are ASP.NET elements in the page.

Introduction to Database Connections

For our user registration page, all we are concerned about is inserting rows into our users table. ASP.NET contains a lot of rich controls for viewing extracted information from a database which we will be exploring later on. However for now all we want to is to insert new users into our users table.

Use the SQLConnection class to open a connection to the database. Write a method that will handle the processing of the registration form. Your code should insert a row into the users table when the user submits the registration form, as well as send an email confirmation to the user letting them know the registration was successful (use C#'s MailMessage class to send email). Place this method within <script language="C#" runat="server"> blocks at the top of your registration.aspx page. Name this method "SubmitBtn_Click". In general, we'll want to name our methods using the following convention, description_eventtype.

Now add a submit button to the bottom of the page using ASP.NET's Button Control. Connect the Click Event handler of this control to your "SubmitBtn_Click" method. The Button Click event in ASP.NET causes the form to be posted back to the server. What this means is that the form values make a roundtrip to the server where they are processed, then sent back to the client. For this to happen, first you must tell the server to process the form server-side. If you haven't done so already, place <form runat="server"> tags around your registration form elements in registration.aspx. Your registration page should now be able to handle adding new user registrations. Sign yourself up for an account and check the database to see if your account was successfully added to the users table.

Authentication, Cookies, and web.config

Now create a login page and creatively name it login.aspx. This should be a simple page, containing an email textbox, a password textbox, and a login button. Use the SQLDataReader Class to validate the email and password against the database. Redirect the user to a page, login_success.aspx to let the user know that they have logged in successfully. This is a temporary page so keep it simple. Later we'll be redirecting the user straight to our rooms reservation system once they've logged in. A page displaying the text "Hello, First Names" will suffice. In order for you to display the users first name in login_success.aspx you'll want to pass it as a URL variable on the redirection after log in. To pass variables inside a URL, use the following format:

http://www.domain.com/target_page?variable_name1=variable_value1&variable_name2=variable_value2...

or in our case something like
http://www.devhood.com/login_success.aspx?first_names=Peter&last_name=Weng

ASP.NET's Page Object contains a Request Property gets the HttpRequest used to access data from incoming http requests. The HttpRequest object, in turn, has a property Params that provides a name, value collection of URL variables. We can use this collection as follows:
Outside of the script tags, you can call methods and properties by placing them within <% %> tags.
Hello, <% Page.Request.Params["first_names"] %> <% Page.Request.Params["last_name"] %>, you were successfully logged in.

or you can place the code within the Page_Load event in your script block

<script language="C#" runat="server">
void Page_Load (Object o, EventArgs e) {
  //get the name from the URL and concatenate the string
  string first_names = Page.Request.Params["first_names"];
  string last_name = Page.Request.Params["last_name"];
 
  //set the TEXT property of the LABEL control to display it on the page
  lbl_first_names.Text = first_names;
  lbl_last_name.Text = last_name;
  ...
 
}
</script>
 
Hello,
<asp:Label id="lbl_first_names" runat="server" />
<asp:Label id="lbl_last_name" runat="server" />
, You were sucessfully logged in.
Other ways of passing variables between pages include using hidden form variables, or storing them in the State bag or Session bag. The State and Session bags are temporary storage areas for variables and/or Control properties. Anything inside these "bags" will be persisted across roundtrips to the server.

Remember to also display an error message on the login page (login.aspx) if the email and password are incorrect.

You'll also want to give the user the option of remembering their login information at the login page. Add a CheckBox server control allowing the user to "Remember email and password". (Hint: Use ASP.NET's RedirectFromLoginPage method in the CookieAuthentication Class to accomplish this.)

The web.config file is an xml-based file that is used to configure settings on the server. Create a web.config file and place it in the root directory of your application. You'll want to set the authentication mode to "Cookie" within the security section of this file. A sample web.config file is below:

<configuration>
  <security>
    <authentication mode="Cookie">
      <cookie cookie=".ASPXAUTH" loginurl="login.aspx" />
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>
  </security>
</configuration>
Finally place a link on your login.aspx page that points to your registration.aspx page so that new users can register. If you are noticing that your link continues to bounce back to the login.aspx page, what may be happening is that your web.config file is denying anonymous users from accessing registration.aspx. Use the <location> section in web.config to allow all users access to the registration page. Refer to the .NET Framework Reference Documentation for more information about web.config.

Making it Robust

Now that we've got the basic functionality in place, let's concern ourselves with the details to make this page more robust.

In order for your system to be robust, you should always perform validation wherever user input is requested. Explore ASP.NET's Validation Controls by using them to add input validation to your registration form. Fields such as email should be validated for correct format (). Also, add RequiredFieldValidator controls on any field that corresponds to a not null column in the users table.

Finally, we need to maintain consistency in our database. Why is this so important? Well let us consider this scenario. Peter Weng () comes to the site and registers for an account. His account information is inserted into the users table and he is able to log in with his email and password and use the system. 3 months later he comes back to the site, which has now been redesigned. He forgets that he has ever registered on this site before, and therefore registers for another account with the same email, but a different password. Our system inserts another row into the database for the same user. Now Peter tries to log in with his newly created (or so he thinks) account.

So we have a duplicate row in the database. So what? What's the big deal? Well let's think about what the server has to do to authenticate a user. It must check that the password the user has entered matches with the email. It does this getting the password from the database for that particular email using a select query with a where clause, and checking it against the password that the user has provided. However, in this case the select query would return 2 rows for and we wouldn't know which password to authenticate against. In most cases, the web server would throw an error, since it was not expecting more than 1 row.

An effective strategy in maintaining a consistent database and avoiding duplicate rows is to use keys. Let's define duplicate row as 2 or more rows that contain the same datum on any unique column. Since each of our users should have a unique email address, we don't want to have 2 or more rows with the same email address.

Using SQL's alter command, add a unique key constraint to the email column on the user's table.

Making it Usable

Fill this in later with some usability stuff.

Exercise 3: The Room Reservations Data Model

I've walked you through the process of building a data model above (users and user groups). Now you should be able to build your own data model. First read through chapter on . Note that Greenspun's book, assumes that the reader is using an Oracle RDBMS, thus the examples are in SQLPlus. SQL, in fact is not a standardized language and Microsoft SQLServer's syntax will be slightly different from Oracle's. However, his data modeling chapter teaches good data model design principles, so the Oracle syntax should not keep you from learning something just because you're using SQLServer.

Next read through this entire problem set to get an idea of what your data model must be able to support. Otherwise, you'll find yourself altering your tables and rewriting code half through the problem set.

Here are some things you should keep in mind when designing your data model. Remember that this is not a complete listing of all necessary steps.

  • Remember to be consistent with your table naming and variable naming conventions. This will be important for other users who may code-review your work.
  • Boolean data should be prefixed with a "_p", and should all be of type bit.
  • When recording a designated administrator in the rooms table, you should do so by referencing the users table. Your column name should also be the same name as the primary key of the table you are referencing (user_id in this case). We used this same idea in our data model above. Remeber how, in our users table, our group_id column referenced the groups table?
  • Don't use room numbers as the primary key for your rooms table. These numbers, although unique, can sometimes change. Changing the primary key of a table is always a bad idea. Instead use generated keys for your table so that you can gracefully handle room number changes.
  • Use start_time and end_time columns of type datetime in your table. You can then always find the length of time of the reservation from the difference of the 2 columns.
  • Rather than using a boolean column to hold whether a room has been made or approved, use a column of type datetime to hold the time the reservation was made or approved. This way you can get more information out of the column than if you were to use a boolean column. (You can still tell whether a reservation has been approved or not by checking for null in the approval_time column).
  • You'll probably need at least 2 tables, one to hold the rooms, and the other to hold the reservations.

Remember to save your data model in a file somewhere safe. As you go through this problem set, you may find yourself dropping tables and thus will need to refer back to your data model to recreate them.

Exercise 4: Starting Off Simple

The Basic Admin Pages

Let's start off simple. We need to first build some pages so that administrators can create rooms. Don't worry about being able to edit room names, or designate room administrators yet.

We'll need at least the following pages to start out:

  • Create the page index.aspx and place it in a directory named admin. This page should contain the names of all rooms, and also an "Add a Room" LinkButton. Your rooms can be displayed as unordered lists, tables, or drop-down lists. As an engineer, you should choose the most appropriate design for the information being presented, i.e., the design that will make your page the most usable. I strongly suggest you explore ASP.NET's DataTable, DataView, and Repeaters for more choices.
  • The LinkButton "Add a Room" should redirect to room-add.aspx. This page should contain a form that allows the administrator to add new rooms to the system. Make this page redirect to index.aspx after successfully creating the new room. The new room should show up in index.aspx which is confirmation to the user that the rooms has been successfully created.
  • These pages are named admin pages because only administrators should be able to access them. In fact all pages within the admin directory should be secure. You can do this by adding a web.config file in your admin directory to only allow users of certain roles to access these pages. Refer to the .NET Reference Documentation for more information about role access and the ASP.NET authorization process.

Test out your pages by adding a few new rooms to the system.

The Basic User Pages

Again, let's keep it simple for now. Don't worry about room approvals, or professor's booting students, etc. Let's just get our basic system up for now:

  • Build index.aspx, the gateway to the room reservation system. This page should contain a form that contains elements such as start and end date and times, and number of people occupying the room. You may even wish to have a field, room name, for users who want to reserve a specific room. Below the form, display an organized view of all rooms the user currently has reserved for the future. By showing this list, you free yourself from having to write special pages to confirm reservations or check reservation history. When finished, go back and modify login.aspx to redirect to index.aspx instead of login_success.aspx upon successful login.
  • After the server has processed the form on index.aspx, it should redirect the user to new.aspx. This page should show a list of rooms that fit the users needs. It should refrain from showing any rooms that have already been booked for the requested time period. This SQL query can get complicated, so here are some hints:
    • You can select from your rooms table with a WHERE clause that subqueries the room_reservations table.
      select ... from rooms where room_id = (select ... from room_reservations ...)
    • You may choose to use the BETWEEN operator to catch cases of overlapping room reservations although it may not be the best way. A more professional way would probably be something in the form
      select ... from room where not exists (select 1 from room_reservations ...)
    This page, new.aspx, will ultimately be the page that allows the user to reserve the room. One way you might do this is by adding radio buttons beside each detailed room listing on new.aspx, which lets them choose which room they wish to reserve. Another way may be to provide a brief listing of available rooms on new.aspx, and link each room to a page that shows more information about the room. The "Reserve Room" button may then reside on your detailed room view page. However you decide to build the system, just remember to choose the layout that provides the best end-user experience.
  • When processing the page new.aspx (or whichever page you chose to let the user reserve the room), you should check there isn't a time conflict, then insert the reservation into the table. After successfully inserting, the server should redirect the user to index.aspx where they can see that their reservation was properly inserted. However, if there is a time conflict, redirect the user to an error page that shows a message to that effect. This error page should also provide a link back to new.aspx with the original room request parameters (presumably this time new.aspx will not offer the already booked room).

    Now you might be asking yourself why there is a need to check for a time conflict when processing new.aspx when new.aspx only showed free rooms in the first place. How could the user have possible chosen a room with a conflict? Here's an example of a concurrency situation we're concerned about.

    Jonathan logs into our system and request a room for 40 people at 1:00pm tomorrow for a 1.5 hour presentation.  
    Our system quickly and intelligently serves up all available rooms for Jonathan to choose. The Bush Room is among the list of available rooms. Eugene logs in and requests a room for 50 people at 2:00pm tomorrow.
    Jonathan was about to confirm the room when he receives an instant message from his friend requesting him to play . Jonathan quickly responds aknowledging that he will play but only for a short time. He then minimizes his browser window and begins playing.

    Three hours later Jonathan is still playing...

    Since Jonathan has yet to book any rooms, our system shows Eugene that the Bush Room is among the rooms available tomorrow at 2:00pm for 50 people.

    Eugene confirms the Bush Room, and the reservation is inserted into the database.

    Jonathan finally finishes playing but then gets preoccupied with his girlfriend. Sometime later Jonathan finally remembers that he has yet to reserve a room for tomorrow. He restores his browser window, but does not reload the page (i.e., he is looking at the page that was generated for him hours ago), and then chooses to reserve the Bush Room for 1:00pm tomorrow.  

    What is the outcome of this situation? Double booked rooms. To make our system robust (and to prove your worth as a web programmer), you must check for reservation conflicts twice, once when serving up the list of available rooms, and again before inserting into the database.

  • There also arises a rare situation where two users submit room requests at precisely the same second. Set yourself apart from mediocre web programmers by accounting for this situation. Lock the room reservations table before querying to check for conflicts. This will prevent anyone else from updating the table while you are querying for room availability. For more information about transactions and locking tables for Oracle, refer to the of SQL for Web Nerds (). Note again, the example code in this book is for Oracle, but the concepts are consistent across all Relational Database Management Systems.

Exercise 5: Making it Real

Our basic functionality is in place. Administrators can create rooms, and users can reserve rooms. Our system prevents double booking rooms and reservation conflicts. Now it's time to add the features and other little things that make the thing real.

Finishing the Admin Pages

Here's what we need to do for the admin pages:

  • Link each room in the /admin/index.aspx listing to room-edit.aspx where the administrator can change the name of the room, mark a room as requiring approval, etc.
  • When a room is marked as requiring approval, you'll need to designate a room administrator for the room. Create a page user-search.aspx that will search through the users table so that a user can be chosen as the room administrator.
  • Create a directory /admin/reports/ and build a page index.aspx within the reports directory. This page shows usage reports for each room, and should contain a list of rooms, and for each room the total nmber of hours that the room has been reserved. Also show the average number of people in a reservation for that room. Make this list sortable by name, number of hours booked, or average number of people. All statistics should be limited to reservations in the preceding 12 months. Be careful not to leave out of your report rooms which haven't had any reservations. These rooms should show up in the report with total hours being 0.

    Hints:

    • ASP.NET's DataGrid Control allows you to specify sortable columns.
    • Let SQLServer do the any date arithmetic, rounding, summing, or averaging for you.
    • You can do this report with a 4-line SQL query by using the GROUP BY clause.
    • You might find SQLServer's NVL helpful for rooms without any reservations.

  • The room names in your /admin/reports/index.aspx should be links to details.aspx, where all reservations are shown, most recent at the top. Make this list sortable as well.

Finishing the User Pages

  • Edit new.aspx so that professors see a list of both unreserved and reserved-but-bumpable (i.e., rooms not reserved by another professor) rooms. Also, students or any non-professors should see a flag by rooms that require approval. Be sure professors can differentiate between unreserved and reserved-but-bumpable rooms.
  • Edit new.aspx to handle a few more situations:
    • Professors bumping students. This involves deleting (or if you'd prefer, just changing the status) of the original reservation, sending email to the student, then inserting the professor's reservation.
    • Students requesting approval-required rooms. This involves inserting the reservation, emailing the room administrator, and serving a confirmation page to the user saying something along the following lines, "your request has been submitted to ... We'll let you know if it's approved".
  • Room administrators should be able to approve or deny room requests directly from their email client. So the email notice to the room administrator should contain encrypted URLs for "one-click approval." You'll probably want one URL for approval, and one URL for denial. These URLs should allow the room administrator to approve/deny the request without having to log in. You'll want to encrypt these URL's in some way so that a user cannot approval his own request by performing URL surgery.

    One suggestion is to use a random number generator to assign a secret key to an approval-required reservation. By inserting this key into the room_reservations table at the time of the reservation, you can always match it back to the specific reservation. You may then choose to pass this key as a variable in the URL for approval/denial.

Congratulations, you now have a working room reservation system.

Exercise 6: Web Services

A can be thought of as an entry point into your application. Web Services are methods within your application that have been programmatically exposed on the internet. Microsoft's version of web services uses SOAP, which supports Remote Procedure Calls (RPC), so that methods can be invoked across HTTP. What does this mean? Any internet device (browser on a PC, , WAP Phone, etc) will be able to see, invoke, and use your methods.

The idea and importance of Web Methods will become clearer once you start creating some yourself.

Introduction to Creating Web Services

Let's start off with a real simple example. By now you should be familiar enough with C# to recognize the following syntax. If you need to, refer to the .NET Framework Reference Documentation. I also recommend Eric Gunnerson's as a good reference for C#.

We have the following method that returns the factorial value of the given argument:

class Factorial {

public int GetFactorial (int n) {
    int answer = 1;
    int counter = n;

    while (counter > 0) {
      answer *= counter;
      counter--;
    }

    return answer;
  }
}
As you can see, GetFactorial is a public method on the class Factorial. To make this method remotely available, and invokable across HTTP, we'll need to make the following additions:
<%@ WebService Language="C#" Class="Factorial"%>

using System.Web.Services;

class Factorial {

[WebMethod]
public int GetFactorial (int n) {
    int answer = 1;
    int counter = n;

    while (counter > 0) {
      answer *= counter;
      counter--;
    }

    return answer;
  }
}
Notice that we've added a WebService directive at the top of the page. This directive has an attribute Language, which lets the compiler know which language should be used to compile the web method. You'll also have to set the Class attribute within the directive which specifies the name of the web service class. In our example, the web service class happened to be in the same file as the directive. Alternatively, you could have specified a web service class which is wrapped within a dll file inside the \bin directory.

Now let's look at the [WebMethod] portion of the code. This is an attribute which simply specifies that the method should be enabled for Web Services. Notice that we had to import the System.Web.Services namespace to use this attribute.

That's it!! That's the bare minimum you have to do to expose your method as a "Web Method". However you should always practice good documentation techniques to make your method even more accessible. You can take advantage of properties on the WebMethod attribute to help you accomplish this. Most programmers don't document their code, and thus makes it harder for others to decide whether to use it, or impossible to figure out what it does. The whole purpose for setting the WebMethod attribute is to make your method distributable across the internet, so good documentation is even more important. Hold yourself to a higher standard by taking advantage of the Description property to help you document your code. A professional programmer's code would probably look something like this:

<%@ WebService Language="C#" Class="Factorial"%>

using System.Web.Services;

[WebService(Description="The Factorial Class")]
class Factorial {

[WebMethod(Description="A method that returns the factorial value of a given number")]
public int GetFactorial(int n) {
    int answer = 1;
    int counter = n;

    while (counter > 0) {
      answer *= counter;
      counter--;
    }

    return answer;
  }
}
Now let's test it out. Cut and paste the code above into a temporary file named service_tmp.asmx (note the .asmx extension used for Web Service files). Save it to your web server and load the page in your web browser. Note there it is also possible to create webservices out of csharp files. For more information on how to do this, refer to the section Precompiled Web Services in . If you're using your local machine as your web server, remember to load the page using localhost in the address field of the browser rather than the direct path (i.e., http://localhost/service_tmp.asmx, rather than c:\inetpub\wwwroot\service_tmp.asmx). You should see a generated web interface for your web service.

Use the generated HTML interface for your web method by calling the method with some parameters. If you've provided a valid parameter (n > 0) then you should see an XML document as the return result. By returning the value as an XML data structure, SOAP can easily send this data across the wire. Any platform can use this information, provided that they can understand the XML language. This xml document should be in the form:

<?xml version="1.0" ?>
<[data type] xmlns="http://tempuri.org/">[value]
</[data type]>

We'll also need to be able to describe our methods so that other programs can access them. Microsoft's solution for this is the Service Description Language (SDL). The SDL serves as a contract for users or programs on the public internet to be able to use our services and web methods. The SDL is a structured contract that is machine-readable, so that programs will know how to use your services, i.e. what arguments to pass, the return data type, etc. Click on the SDL Contract link on your service_tmp.asmx page. The link should bring you to an another XML document. I've cut out a portion of this document below:

<httpget xmlns="urn:schemas-xmlsoap-org:get-sdl-2000-01-25">
  <service>
    <requestResponse name="GetFactorial" href="http://localhost/test/service.asmx/GetFactorial">
      <request>
        <param name="n"/>
      </request>
      <response>
        <mimeXml ref="s0:int"/>
      </response>
      <info>A method that returns the factorial value of a given number</info>
    </requestResponse>
  </service>
</httpget>
Remember we didn't have to do anything fancy to describe our web service. In fact, the server generated this contract for us. Thus to the programmer, they don't have to do anything special to make sure that their web method or web service will be accessible be other programs. However, it's still important to be familiar with the SDL. Let's briefly examine what the SDL contract is all about.

The portion I've cut out above is for invoking the method over a HTTP-Get Request. You can see this because the code snippet is wrapped around <httpget> tags. Being able to invoke the method via a HTTP-Get means that the method can be called by sending the data to the server via a URL (an example would be http://localhost/test/service_tmp.asmx/GetFactorial?n=3). You'll also notice that the SDL describes things such as what arguments should be passed in (<request>...</request>), what is the return data type (<response>...</response>), and more information about the method (<info>...</info>). If you look over the entire SDL, you'll see that are analogous sections in the SDL for HTTP-Post and SOAP requests.

Look over the SOAP section of the SDL. This can be found at the beginning of contract enclosed within <soap>...</soap> tags. Familiarize yourself with the structure of the document by answering the following questions:

  • What methods can be invoked by SOAP requests?
  • What are the name of the arguments for the method?
  • What is the return data type?

    Hint: You will probably have to reference other sections of the document to answer some of these questions

Obviously you can answer these questions by looking at the source code of the web method created above, but don't cheat. Look over the SDL and verify your findings with the source code. Remember, this exercise is to familiarize yourself with the structure of the SDL contract.

Invoking Web Services through .NET

.NET includes a wsdl tool, which creates a proxy class so we don't have to worry about network or marshalling code. Although Visual Studio 7 will eventually do this, we would use wsdl on the Factorial example in the following fashion:

wsdl /language:CS /namespace:Factorial http://localhost/service_tmp.asmx?sdl

This tells the utility to make a proxy class for service_tmp.asmx, which is written in C#, and make a namespace, Factorial, that contains the proxy class. The utility will create a C# source file for us. The filename will be the namespace we specified followed by the extension cs for C#.

To make our Factorial service programmatically accessible to other .NET users, we compile the resulting source code which creates a dll for our proxy class. Then, we give a copy of the dll to anyone who wants to use our service. When others place our dll in their bin directories, They can use our service in their ASP.NET pages just like they would if they wrote the service themselves. In our Factorial example, what would actually happen is the proxy class would tell our server to instantiate the Factorial class on our server and communicate with the remote sites accessing our service through XML, but all of those details are hidden from the remote sites for simplicity.

What are the advantages of Web Methods?

The Factorial example above is a really simple example. In fact it would probably never be used if it were a real Web Service available on the internet. However, for arguments sake, let's assume that calculating the factorial of a number is an extremely CPU intensive task. If you have an extremely powerful computer running a web service, now exposing this method on the internet becomes important to other users. Now they can "borrow" your CPU for calculating a factorial, and just wait for the return value.

Now lets forget about the Factorial example. Instead, lets think about a method that actually extracts some information out of our database. If we exposed such a method, we are now allowing programmatic access to our database for other programs or programmers to take advantage of. If we were a supplier of some product and a client needed "real-time" inventory to be coupled with one of their scripts to fill or place orders, now their programs can interact with our database to get the latest inventory at any moment.

Your Turn

Your room reservation system has become very popular. Professors and students are constantly booking rooms. However you have heard complaints that users are always forgetting which rooms they have booked or on which days. Email reminders are annoying and increases the chances of your web server crashing, so you don't want to take that approach.

Your buddy, Peter, has a built a popular web calendar service, such as or that is widely being used on campus. You talk to Peter and have both decided that a great service would be to somehow automatically insert the room reservation information into his calendar service.

Write a new method, or expose an existing method so that Peter can have access to users' room reservation information. Assume that Peter is from MIT, and is competent enough to handle an XML data structure. Here are some things to consider:

  • Name your method
  • Peter is not going to know anything about your user_id's. Instead have your method take in an email address as a parameter to identify a specific user's reservation information.
  • Just have your method take in a start date and end date, and return all reservations between those 2 dates. Let Peter determine whether the reservations have already been inserted into the calendar or not.
  • Assume that your method will be invoked using the SOAP protocol. Some data types supported by SOAP include Primitives, Enum, Structs, Classes, DataSets, etc. For a complete set of DataTypes and Descriptions supported by SOAP and HTTP Get/Post refer to page 162 of .
  • You could stop when your method returns XML, but assume that Peter is also running the .NET platform and extend your service to him using the wsdl tool. That way all of the XML is hidden, and as far as Peter is concerned calling your sevice is like calling one of his own methods.

Congratulations! You've just built a real web service that can be used by people and programmers across the internet.

Extra Credit: Invoking and Using Web Services

If you have friends who are also working on this pset, test out each other's service by writing a page that shows the availability of one of your cohorts.
Alternatively, you can also write a page that uses your own web service to show availability if you are doing this problem set alone.

Copyright and Credits

This problem set was written in December 2000 by Peter Weng (). It is copyright 2000 and may be reused provided credit is given to it's original author with a hyperlink to this document. Original inspiration for this problem set comes from and Room Reservations written for TCL and AOLServer.


Return to Browsing Tutorials

Email this Tutorial to a Friend

Rate this Content:  
low quality  1 2 3 4 5  high quality

Reader's Comments Post a Comment
 
So when are we going to see problem set 2???
This problem set was awesome!
-- Jonathan Lau, October 09, 2001
 
hahahah. That's only because you didn't have to do it. Ask the other guys how "awesome" it was.
-- Peter Weng, October 11, 2001
 
Yeah I remember working on this problem set. It was actually a great way to start learning ASP.NET, C#, and SQL since it covered a lot of the most common things you need. Problem Set 2 would be pretty nice. Would Professors Greenspun and Abelson mind if you "translated" a few more of their psets?
-- Edmund Chou, October 22, 2001
 
Interesting.. but is it one of the assignments from your school? :)
-- Kevin Lai, December 03, 2001
 
This is actually a port of an assignment at our school from Tcl/AOLServer to C#/ASP.NET. You can find more information about the original version under the copyrights and credits section at the end of the tutorial.
-- Peter Weng, December 04, 2001
 
What sort of class was this used in?
-- Jay Pujara, December 14, 2001
 
The name of the class was called "Software Engineering of Innovative Web Services". Here is some more about the class. The course has slightly since I took it a year ago.
-- Peter Weng, January 08, 2002
 
Pete nice job!
-- Sean Fitzgerald, March 14, 2002
 
Good tutorial.
-- Brian Simoneau, April 15, 2002
 
Copyright © 2001 DevHood® All Rights Reserved