Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 64 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
64
Dung lượng
704,81 KB
Nội dung
111 One of the problems with having lots of people bidding for your time is trying to keep them all posted on your schedule. When I started doing training courses several years ago, I found that it was impossible to maintain static copies of my schedule, so I built a Web-based calendar application. People could now use this to check my availability and schedule appointments. Part of the calendar was still kept hidden, however, since people don’t need to know all the details on my engagements. We’ll be building a similar system in this project. The events you put on the calendar can be single-day or multiday events. They can also be recurring events, using several different methods to schedule them. An event can be scheduled for a particular day in the month, such as the first Friday of the month. Events can also be scheduled to repeat every two weeks. Events can even be scheduled for the same day of the month, each month, if you can have an event that occurs on the 15th of every month, for example. We’ll be building all this logic into a series of objects so that we can display and edit the data through a series of Web pages. We’ll also be creating both a monthly and daily view of the calendar. For the monthly view, we’ll be using the built-in Calendar control to save ourselves some tedious formatting work. Then, all we have to do is fill in the boxes with our data. The daily view will require a bit more programming, but it won’t require a lot of new logic. We’ll also be building a management page for adding and modifying the events that are on the calendar. Building the Calendar Application PROJECT 3 THE PROBLEM You need a calendar that is accessible and modifiable through the Web and that doesn’t require the purchase of an expensive server, such as Microsoft Exchange. THE SOLUTION An ASP.NET application that provides the ability to do simple and recurring events, as well as the ability to publish the calendar on the Web. Project Background You’ll need to complete the following steps to build this project: 1. Design the Events table. 2. Build the business objects. 3. Build the administration Web pages to add, modify, and delete events. 4. Build the monthly calendar viewing page. 5. Build the daily calendar viewing page. You Will Need ✔ Windows 2000 ✔ Internet Information Server 5.0 with .NET Framework installed ✔ Visual Studio .NET ✔ SQL Server 7.0 or 2000 ✔ A text editor, like HomeSite or TextPad, to edit pages if you don’t want to edit them within Visual Studio .NET Designing the Events Table In this section, we’ll design the tblEvents table used to hold our events. We first have to determine how we’re going to handle each type of event. We can then move on to the actual table design and creation. Creating the Recurrence Logic Our calendar is going to support the following types of events: Simple events that occur only once. These events may span several days; how- ever, they only occur once. Events that occur at a regular interval, such as every two weeks or once a year. These events can be ongoing or can have specific starting and ending dates. 112 Project 3 ✄ TEAMFLY Team-Fly ® Events that occur on a particular day in a month. These events can also be ongo- ing or can have specific starting and ending dates. Anniversaries. These events occur on the same day of the month, once a year. They may be a birthday, an anniversary, or some other type of yearly event. You might come up with other types of recurring events you want to add to the sys- tem, but this list will keep us busy in this project. Most other recurrences are simply combinations of these basic types. Once you’ve seen how the system works in han- dling these events, you’ll be able to add your own and expand this system. The first order of business is to design the table into which we’ll store the events shown on the calendar. Part of the table contains basic information common to every type of event, but we also have to make space for storing information about each type of recurring event that we need to store in the system. For simplicity, we’ll be storing the information for each type of recurrence in a different field or set of fields and using a flag in one of the fields to indicate the type of event it is. This will make it easier to select the appropriate events when displaying them on the calendar. The general information we need to store for each event is as follows: Name. A short description of event that can be shown in small views of the calen- dar. Description. A text field for notes about the event. Event Start and End. The starting and ending dates for this particular event. These dates will be used in events that occur periodically so that you can have an event that is a week long occur every two weeks. The starting and ending dates will be used in calculations to determine when the recurrence should occur. Public or Private. Event details are shown to everyone on a public calendar. Pri- vate events are also shown on the calendar, but no details are provided. For recurring events, we have to store the following additional information, based on the rules that we established earlier in the project: Recurrence Type. This is a single-letter code indicating what type of recurrence we’re using. N = Non-recurring, P = periodic event, occurring on a regular basis, M = monthly event, A = anniversary. Recurrence Start, Recurrence End. This indicates when the recurring event should start and end. This is separate from the start and end date we already identified. For an event that doesn’t have an ending date, the Recurrence End can be empty or null. Periodic Code, Periodic Amount. For events that occur at regular intervals, the Periodic Code is the code used by the date functions (DateAdd, DateDiff) to indicate the time period. The Periodic Amount is the number of those units. The codes for the Periodic Code are shown in Table 3.1. We have left out the codes for units smaller than a day. Monthly Code, Monthly Ordinal, Monthly Day. For events that occur once a month, we have to first indicate whether the event is on a particular day of the month or if the event is on a logical day in the month, like the first Thursday, for example. The Monthly Code value will hold an A for the first case, standing for Building the Calendar Application 113 Actual, and a L for the second case, standing for Logical. The Monthly Ordinal field will only be used when we are using a logical date and will have a number in it from 1-5, since there can only be five Mondays, for instance, in any month. The Monthly Day field will be used in both actual and logical date events. For actual events, this value will be the day number to use. For logical events, it will be a number from 1-7 indicating the day of the week, with the value 1 indicating Sunday, which is the U.S. standard. AnniversaryDate. This field will hold the anniversary date for yearly events. Users can store the actual date, such as the date of a wedding or birthday, along with the year. While the year is irrelevant for date calculations, it is easier to store this way than as separate fields. These codes are also documented in the DateAdd and DateDiff function documentation in your .NET installation. It may seem that we’re using a lot of separate and even duplicate fields, but each type of recurrence requires slightly different information to be processed correctly. You could combine them, but it makes it more difficult to work with later. Creating the tblEvents Table Since this application is separate from the previous two applications you’ve built, I decided to start a brand new database for it, which I’m naming ASPNetProject03 in my own system. You could easily combine the application built in the last project with this one, at which point you’d probably want to have a single database for both parts. We’re going to create a new table called tblEvents to hold both single and recurring events. The table definition is shown in Table 3.2. Table 3.1 Periodic Code Values CODE UNIT d Day m Month q Quarter of year w Weekday ww Week of year yyyy Year 114 Project 3 Table 3.2 tblEvents Table Definition FIELD NAME SQL DATA TYPE LENGTH OTHER pkEventID int N/A Identity, Primary Key, Not Null Name varchar 80 Not Null Description text N/A Nullable EventStart datetime N/A Not Null EventEnd datetime N/A Not Null IsPublic bit N/A Not Null, Default = 0 RecurrenceType char 1 Not Null, Default = ‘N’ RecurrenceStart datetime N/A Nullable RecurrenceEnd datetime N/A Nullable PeriodicCode varchar 4 Nullable PeriodicAmount tinyint N/A Nullable MonthlyCode char 1 Nullable MonthlyOrdinal tinyint N/A Nullable MonthlyDay tinyint N/A Nullable AnniversaryDate datetime N/A Nullable If you’re using SQL Server, a copy of the script to create this table is available on the CD-ROM. If you’re not using SQL Server, you can use the table above to convert to your particular database’s data types. We’ll be adding test data along the way to test various conditions and recurrence types of the calendar. For now, I’d hold off adding data until you understand how the table is designed to hold it. Building the Business Objects The next step is to build the objects that will help manage our calendar data. We’re going to use some of the code we built in the previous project. Specifically, we’ll be using the AtWorkUtilities assembly and the BaseServices class. The AtWorkUtilities assembly is already done, so I’m simply making a copy of it for this new project. In real Building the Calendar Application 115 life, you wouldn’t need to copy the project, but I’m doing this so that everything is kept together and so you don’t have to hunt for the files. The second assembly we’ll be building will contain the logic for the CalendarEvent class and will be built on top of the BaseServices class we built in the previous project. I’ll be creating a new assembly project and copying the BaseServices class to be used here. For simplicity, we’ll be calling this assembly CalendarObjects, but feel free to name it whatever you want. If you have decided to combine this application with the application you built in the last project, the Event class will become part of the Busi- nessObjects assembly. The class is called CalendarEvent since the word Event is a reserved word in Visual Basic .NET. Preparing the Environment Since this Web application will use a number of graphical controls, it’s easiest to build it from scratch within the Visual Studio environment. Our Solution file will contain all of these projects when we’re done: AtWorkUtilities. Assembly created in Project 2 with the Database class. CalendarObjects. Assembly you’ll build in this project to manipulate your events. Calendar. ASP.NET application that will be used to show and administer the calen- dar. You may also have a test Windows application to work with your CalendarObjects class, but you won’t typically need to keep that application. Since I want to keep all the projects together in a single solution, I use Visual Studio .NET to create a blank solution, into which I can add projects. This makes it easier to get the Solution file in the right place. You don’t have to use this method, but it makes for easier organization. To create a blank solution, do the following: 1. Select New from the File menu. Then select New Solution. 2. In the dialog box that appears, select where to put the solution. Note that Visual Studio automatically creates a new folder for your solution. Once you have the empty solution, you can copy other project directories to the Solution directory and then add each project to the solution. The first project you’ll want to add, either as an actual project or as a reference to the assembly DLL, is the AtWorkUtilities assembly. If you’re not sure how to build it, you can look at Project 2 for all the details on this assembly and the features and classes it provides. At this time, the only object included in the AtWorkUtilities assembly is the Database class. The code for the class is shown in Listing 3.1. Imports System.Data.SqlClient Imports System.Configuration Public Class Database Listing 3.1 Database class in AtWorkUtilities assembly 116 Project 3 Private m_cnDB As SqlConnection ‘ ‘ This constructor reads the application configuration ‘ file (Web.config for web applications) for a string ‘ called ConnectionString. If it’s not there, an exception ‘ is thrown. Otherwise, the connection is made. ‘ Public Sub New() Dim objCnf As ConfigurationSettings If objCnf.AppSettings(“ConnectionString”) = “” Then Throw New Exception(“Connection string not found “ _ & “in application configuration file.”) Else m_cnDB = New _ SqlConnection(objCnf.AppSettings(“ConnectionString”).ToString) m_cnDB.Open() End If End Sub ‘ ‘ This constructor accepts a connection string as input ‘ and makes a connection to that SQL Server. ‘ Public Sub New(ByVal ConnectionString As String) m_cnDB = New SqlConnection(ConnectionString) m_cnDB.Open() End Sub ‘ ‘ In case there are other objects that need the live ‘ connection, make it available through a read-only ‘ property. ‘ Public ReadOnly Property Connection() As SqlConnection Get Return m_cnDB End Get End Property ‘ ‘ Run a query that does not return records. ‘ Public Function Execute(ByVal SQL As String) As Integer Dim lngRecords As Integer Dim cmdQuery As New SqlCommand() cmdQuery.Connection = m_cnDB cmdQuery.CommandText = SQL cmdQuery.CommandType = CommandType.Text lngRecords = cmdQuery.ExecuteNonQuery() End Function Listing 3.1 Database class in AtWorkUtilities assembly (continued) Building the Calendar Application 117 ‘ ‘ Run a stored procedure that does not return records. ‘ Public Function ExecuteStoredProc(ByVal SQL As String) As Integer Dim lngRecords As Integer Dim cmdQuery As New SqlCommand() cmdQuery.Connection = m_cnDB cmdQuery.CommandText = SQL cmdQuery.CommandType = CommandType.StoredProcedure lngRecords = cmdQuery.ExecuteNonQuery() End Function ‘ ‘ Run a query that returns records in the form ‘ of a SqlDataReader. ‘ Public Function GetDataReader(ByVal SQL As String, _ Optional ByVal blnSkipRead As Boolean = False) As SqlDataReader Dim cmdQuery As New SqlCommand() Dim dr As SqlDataReader cmdQuery.Connection = m_cnDB cmdQuery.CommandText = SQL cmdQuery.CommandType = CommandType.Text dr = cmdQuery.ExecuteReader If Not blnSkipRead Then dr.Read() Return dr End Function ‘ ‘ Run a query that returns records in the form ‘ of a DataSet. ‘ Public Function GetDataSet(ByVal SQL As String) As DataSet Dim da As New SqlDataAdapter(SQL, m_cnDB) Dim ds As New DataSet(“Results”) da.Fill(ds) Return ds End Function ‘ ‘ Replaces all single quotes with two single ‘ quote characters. Useful for building SQL ‘ statements. ‘ Public Function Clean(strInput as string) as string Return strInput.Replace(“‘“, “‘’”) End Function ‘ ‘ Close the database connection. ‘ Listing 3.1 Database class in AtWorkUtilities assembly (continued) 118 Project 3 Public Sub Close() m_cnDB.Close() End Sub End Class Listing 3.1 Database class in AtWorkUtilities assembly (continued) One change that we’ve made is to the GetDataReader method, which now accepts a second, optional argument. The SqlDataReader object requires that you do a read call in order to access data. However, if you’re looping through the data, you’ll use a While loop and read all the records. This new optional argument allows the code to skip the initial read. Once you’ve got the AtWorkUtilities assembly or project added to the solution, your next step is to create a new VB Class Library project called CalendarObjects. This is where you should add a copy of the BaseServices class you built in the previous proj- ect, provided you’re building a new assembly for this project. The BaseServices class is shown in Listing 3.2 and is documented in the previous project. Imports System.Text Imports System.Data Imports System.Data.SqlClient Imports AtWorkUtilities Public MustInherit Class BaseServices Private m_arrErrors As ArrayList Protected m_DB As Database Protected m_DA As SqlDataAdapter Protected m_CB As SqlCommandBuilder Protected m_DS As DataSet ‘ ‘ This constructor should be overloaded and called ‘ by each derived class. It sets up the protected ‘ objects available to all derived classes for handling ‘ database activities. ‘ Protected Sub New(ByVal DB As Database, ByVal strSQL As String) m_DB = DB m_DA = New SqlDataAdapter(strSQL, m_DB.Connection) m_CB = New SqlCommandBuilder(m_DA) m_DS = New DataSet() m_DA.Fill(m_DS) End Sub ‘ ‘ The DataSet will have either zero rows or one row ‘ so we simply return the current row in the dataset. ‘ This code makes it easier to get at the data instead Listing 3.2 BaseServices class Building the Calendar Application 119 ‘ of having to duplicate the full hierarchy in the ‘ calling code. For empty DataSets, we return an empty ‘ row that can be populated. ‘ Public Function GetRow() As DataRow If m_DS.Tables(0).Rows.Count > 0 Then Return m_DS.Tables(0).Rows(0) Else Return m_DS.Tables(0).NewRow() End If End Function ‘ ‘ This routine accepts a data row as input and stores ‘ the data into the dataset. In cases where the row ‘ is new, we add the new row to the DataSet. If the ‘ DataSet has data in it, we read the data row and ‘ replace each field in the DataSet one column at a ‘ time. ‘ Protected Sub SaveRow(ByVal dr As DataRow) Dim val As DataColumn ‘ ‘ Handle new row ‘ If m_DS.Tables(0).Rows.Count = 0 Then m_DS.Tables(0).Rows.Add(dr) Exit Sub End If ‘ ‘ Handle existing row ‘ m_DS.Tables(0).Rows(0).BeginEdit() For Each val In m_DS.Tables(0).Columns m_DS.Tables(0).Rows(0).Item(val) = dr.Item(val) Next m_DS.Tables(0).Rows(0).EndEdit() End Sub ‘ ‘ Adds another validation error to the array list ‘ object. This saves some work for the calling/inheriting ‘ class. ‘ Protected Sub AddError(ByVal strInput As String) If m_arrErrors Is Nothing Then m_arrErrors = New ArrayList() End If m_arrErrors.Add(strInput) End Sub Listing 3.2 BaseServices class (continued) 120 Project 3 [...]... align=”right”>Start Date: Time: End Date: Time:... align=”center”> Update Delete Listing 3. 6 Viewevents.aspx (continued) 133 134 Project 3 We use the same... In m_DS.Tables(0).Rows If dr.RowState = DataRowState.Added _ Or dr.RowState = DataRowState.Modified Then ValidateRow(dr) End If Next End Sub Listing 3. 4 CalendarEvent class (continued) Building the Calendar Application ‘ ‘ Checks an individual row for validation rule ‘ compliance Any errors are added to the errors ‘ collection ‘ Private Sub ValidateRow(ByVal dr As DataRow) If IsDBNull(dr(“Name”)) Then... valign=”top” align=”center”> TE 132 Listing 3. 6 Viewevents.aspx Team-Fly® Building the Calendar Application ... “A” StoreDate(DR, “AnniversaryDate”, _ txtAnniversaryDate) End If End If StoreDate(DR, “RecurrenceStart”, txtRecurrenceStartDate) StoreDate(DR, “RecurrenceEnd”, txtRecurrenceEndDate) Listing 3. 11 Eventdataentry.aspx.vb (continued) 1 43 144 Project 3 E.SaveRow(DR) If Not E.IsValid Then lblErrorMessage.Text = _ E.ValidationError(“ERROR: The following “ _ & “errors were detected in your data:”,... Update Delete . m_DS.Tables(0).Rows If dr.RowState = DataRowState.Added _ Or dr.RowState = DataRowState.Modified Then ValidateRow(dr) End If Next End Sub Listing 3. 4 CalendarEvent class (continued) 124 Project 3 ‘ ‘ Checks an. with later. Creating the tblEvents Table Since this application is separate from the previous two applications you’ve built, I decided to start a brand new database for it, which I’m naming ASPNetProject 03. “N” Listing 3. 4 CalendarEvent class Building the Calendar Application 1 23 Private Const RECUR_PERIODIC = “P” Private Const RECUR_MONTHLY = “M” Private Const RECUR_ANNIV = “A” Private Const MONTHLY_ACTUAL