Uploaded on

Description of some relevant topics from the project.

Description of some relevant topics from the project.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
913
On Slideshare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
0
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Library – Windows/Web, Business and Data Access Tiers Introduction: For this project, I was responsible for developing both the Windows Forms and the Web Pages presentation tiers, the Business and Data Access tiers for this n-tier library application. The Data Access tier utilizes stored T-SQL procedures to query and update SQL Server database tables. Within each stored procedure, I implemented data validation and proper error handling techniques. Exceptions return a specified error code back to the Data Access tier, where customized error handling takes place. If the stored procedure completes without exception, values are assigned to the appropriate object properties and passed back to the Windows Application (or the Web Application) through the business tier. The business tier acts as a middle tier between the Windows Application / Web Application tier and the Data Access tier, handling all calls to the to the Data Access tier. Audience: This project is designed for library personal to carry out typical library functions, such as adding members and checking in/out library books. The forms were designed to be easy to use, with minimal modal window usage and makes use of error icons and help text to notify the user of any errors. The web pages were designed to have in-place requests and allow navigating through the pages via hyperlinks. The user is also allowed for roll-backs on checking in/out books. Project Goals: • Design the Windows and Web Applications tiers. • Design the Business and Data Access tiers. • Develop code that is easily maintainable. • Provide adequate error handling. • Use database-programming techniques that provide maximum programming flexibility and control while minimizing resource utilization.
  • 2. Transact Data and switch to a new Child Windows Form Add an Adult Member through a request form, activate and initialize another child window by specifying the member ID. private void btnAddMember_Click(object sender, EventArgs e) { // Clean up status info anyway. lblUserMessage.Text = string.Empty; // Validate form data. if (IsInvalidData()) { lblUserMessage.Text = "Please correct all indicated errors and try again."; return; } // Instanciate and populate the adult member to be added to // the db. AdultMember adult = new AdultMember(); adult.FirstName = tbxFirstName.Text; adult.MiddleInitial = tbxMiddleInitial.Text; adult.LastName = tbxLastName.Text; adult.Street = tbxStreet.Text; adult.City = tbxCity.Text; adult.State = cbxState.Text; adult.ZipCode = tbxZipCode.Text; adult.PhoneNumber = tbxPhone.Text; // Add it to the database. Call into the business layer. lda.AddMember(adult); if (adult.MemberID > 0) { // The adult was successfully added to the database. // Pass the member ID assigned by the database to make // the new instance of the the member info form to be // posted next. GetMemberInfoForm info = new GetMemberInfoForm(adult.MemberID); info.MdiParent = this.MdiParent; info.Show(); // Close this form. this.DialogResult = DialogResult.OK; this.Close(); } else { // Provide info about the problem while keeping this // form open.
  • 3. lblUserMessage.Text = lda.LastErrorMessage; } } The method above is located in the AddAdultMemberForm class, which is used to pass the member ID to the overloaded constructor of GetMemberInfoForm class. Initializing Form with a piece of info (the Member ID) being passed in to the constructor /// <summary> /// The constructor for working with a given member of the library. /// </summary> /// <param name="memberID">The member ID card.</param> /// <remarks> /// Initializes an instance of business data access, sets the member /// ID to the given one and loads it data to be ready for displaying. /// </remarks> public GetMemberInfoForm(int memberID) { InitializeComponent(); lda = new LibraryDataAccess(); // Make sure the parameter is positive and can be held in a short. if (memberID > 0 && memberID < 32768) { Member member = lda.GetMember((short)memberID); if (member != null) { // The given ID ties to a library member, so initialize // all the controls data. tbxMemberID.Text = memberID.ToString(); PopulateFields(member); loansDataSource.DataSource = lda.GetItems((short)memberID); isMatchDataID = true; } else { // Empty form, use the default text. Text = GetMemberInfoForm.DefaultText; isMatchDataID = false; } } else { // Empty form, use the default text. Text = GetMemberInfoForm.DefaultText; isMatchDataID = false; } // Build the arrays of [ctrl, validator] for validation. beValidated = new Control[1]; beValidated[0] = tbxMemberID;
  • 4. validators = new LibraryDataValidation.ValidateHandler[1]; validators[0] = new LibraryDataValidation.ValidateHandler( LibraryDataValidation.ValidateMemberID); } The method above is the non-default constructor for the GetMemberInfoForm class, which displays member information based on the member ID received from adding a new member. Data to populate the form are obtained from querying the database within the constructor.
  • 5. Check out item (book) through the Tiers Calls Chain Check out item (book): the Business Tier using Library Types /// <summary> /// Checks out the item whose ISBN and copy number are specified to the /// Member whose member number (ID) is specified. /// </summary> /// <param name="memberNumber"> /// The member ID of the member who is checking out the item. /// </param> /// <param name="isbn"> /// The ISBN of the item that is being checked out. /// </param> /// <param name="copyNumber"> /// The copy number of the item that is being checked out. /// </param> /// <remarks> /// This method checks out the specified item to the specified member, /// unless the item is on loan currently, or if the member's account /// is expired. /// </remarks> public void CheckOutItem(short memberNumber, int isbn, short copyNumber) { try { lastErrorMessage = "OK"; dataAccess.CheckOutItem(memberNumber, isbn, copyNumber); } catch (ArgumentOutOfRangeException e) { // The ISBN or the copy number or the member number was not a // positive integer. lastErrorMessage = e.Message; } catch (LibraryException e) { // The item is on loan currently or a database error occurred. switch (e.LibraryErrorCode) { case ErrorCode.CheckOutFailed: lastErrorMessage = "Failed checking out item."; break; case ErrorCode.ItemNotFound: lastErrorMessage = string.Format( "No item with ISBN {0} and copy {1} could be found.", isbn.ToString(), copyNumber.ToString()); break; case ErrorCode.ItemNotLoanable:
  • 6. lastErrorMessage = string.Format( "The item with ISBN {0} and copy {1} is not loanable.", Check out item (business) continued from previous page . . . isbn.ToString(), copyNumber.ToString()); break; case ErrorCode.ItemAlreadyOnLoan: // Check the OtherMemberID property of the LibraryException // to obtain the member ID of the member to whom the item // is on loan currently. Set the error message accordingly. Member other = dataAccess.GetMember(e.OtherMemberID); StringBuilder sb = new StringBuilder("Item is on loan to "); sb.AppendFormat("{0} ", other.FirstName); if (other.MiddleInitial.Length > 0) sb.AppendFormat("{0}. ", other.MiddleInitial); sb.AppendFormat("{0} ({1}). ", other.LastName, other.MemberID); lastErrorMessage = sb.ToString(); break; default: break; } } } The method above is located in the Library.Business.LibraryDataAccess class, which implements the matching method in the IDataAccess class to make calls to the data access layer. All the errors that are raised by the data access layer are caught and handled here. An internal variable accessed via property holds the details of the last error that was raised. Check out item: the Data Access Tier using ADO.NET to run Stored Procedures /// <summary> /// Checks out the item whose ISBN and copy number are specified to the /// Member whose member number (ID) is specified. /// </summary> /// <param name="memberNumber"> /// The member ID of the member who is checking out the item. /// </param> /// <param name="isbn"> /// The ISBN of the item that is being checked out. /// </param> /// <param name="copyNumber"> /// The copy number of the item that is being checked out. /// </param> /// <remarks> /// This method checks out the specified item to the specified member, /// unless the item is on loan currently, or if the member's account /// is expired. /// </remarks>
  • 7. /// <exception cref="System.ArgumentOutOfRangeException"> /// The ISBN or the copy number or the member number was not a positive Check out item (data access) continued from previous page . . . /// integer. /// </exception> /// <exception cref="MS.Library.Entities.ErrorCode.CheckOutFailed"> /// Failed to check out the specified item. /// </exception> /// <exception cref="MS.Library.Entities.ErrorCode.ItemNotFound"> /// No Item with the specified ISBN and copy number was found. /// </exception> /// <exception cref="MS.Library.Entities.ErrorCode.ItemNotLoanable"> /// Item with the specified ISBN and copy number cannot be loaned. /// </exception> /// <exception cref="MS.Library.Entities.ErrorCode.ItemAlreadyOnLoan"> /// Item with the specified ISBN and copy number is already on loan to /// another member. Check the OtherMemberID property of the LibraryException /// to obtain the member ID of the member to whom the item is on loan /// currently. /// </exception> public void CheckOutItem(short memberNumber, int isbn, short copyNumber) { // Inspect against the allowed range of the input parameters. if (memberNumber <= 0) throw new ArgumentOutOfRangeException( "Member ID", "Member ID is not a positive number"); if (isbn <= 0) throw new ArgumentOutOfRangeException( "ISBN", "ISBN is not a positive number"); if (copyNumber <= 0) throw new ArgumentOutOfRangeException( "Copy number", "Copy number is not a positive integer"); // Access the database via ADO (client side). try { using (SqlConnection cnn = new SqlConnection( Properties.Settings.Default.libraryConnectionString)) { using (SqlCommand cmd = new SqlCommand("CheckOutItem", cnn)) { cmd.CommandType = CommandType.StoredProcedure; // Add input parameters. SqlParameter param = cmd.Parameters.Add("@member_no", SqlDbType.SmallInt); param.Value = memberNumber; param = cmd.Parameters.Add("@isbn", SqlDbType.Int); param.Value = isbn; param = cmd.Parameters.Add("@copy_no", SqlDbType.SmallInt); param.Value = copyNumber; // Execute the store procedure. cnn.Open(); cmd.ExecuteNonQuery();
  • 8. } } Check out item (data access) continued from previous page . . . } catch (SqlException e) { // Grab the relevant info to be passed to the business layer. switch (e.Errors[0].State) { case 6: // Item already on loan, loanee ID is in the message short loaneeID = short.Parse(e.Errors[0].Message); throw new LibraryException(loaneeID, ""); default: ErrorCode code = (ErrorCode)e.Errors[0].State; string message = e.Errors[0].Message; throw new LibraryException(code, message); } } } The method above is located in the Library.DataAccess.LibraryDataAccess class, which implements the matching method in the IDataAccess class to handle calls to the database by using stored procedures via ADO. The SQLCommand object is initialized with input parameters after input data has been validated. The stored procedure, dbo.CheckOutItem, is invoked to process the transaction. The catch blocks handle any SQLExceptions and throw a new custom LibraryException back to the business tier. Check out item: the SQL Stored Procedure within the database -- ================================================ -- Author: Mauro Sist -- Create date: 5/28/2009 -- Description: Checks an item out to a member of -- the library. -- ================================================ create procedure [dbo].[CheckOutItem] -- The set of input parameters. @member_no smallint, @isbn int, @copy_no smallint as begin -- Prevent extra result sets from -- interfering with select statements. set nocount on; -- Check parameter values to enforce match with copy,
  • 9. -- loan and member tables table's constraints. if (@member_no is null) Check out item (stored proc) continued from previous page . . . begin raiserror('Member ID must be not null', 16, 1) return end if (@isbn is null) begin raiserror('ISBN must be not null', 16, 1) return end if (@copy_no is null) begin raiserror('Copy number must be not null', 16, 1) return end -- Verify that the item copy with the given ISBN and copy # exists -- (while doing this, also retrieve the title # to be used later). declare @anisbn int declare @title_no int select @anisbn = c.isbn , @title_no = c.title_no from copy as c where c.isbn = @isbn and c.copy_no = @copy_no if (@anisbn is null) begin raiserror('No item with the given ISBN and copy number could be found', 16, 2) return end -- Verify that the item with the given ISBN is loanable. Being loanable -- applies on an item basis, so all copies of any given item are either -- loanable or not loanable. declare @loanable yes_no select @loanable = i.loanable from item as i where i.isbn = @isbn if (@loanable = 'N') begin raiserror('The item with the given ISBN is not loanable', 16, 7) return end -- Verify that the item copy with the given ISBN and copy # -- is currently not on loan to any member of the library. declare @memberID smallint select @memberID = l.member_no from loan as l where l.isbn = @isbn and l.copy_no = @copy_no
  • 10. if (@memberID > 0) begin Check out item (stored proc) continued from previous page . . . -- specify the member number as a message string declare @message char(5) set @message = @memberID -- implicit conversion raiserror(@message, 16, 6) return end -- Verify that the account for this member is not expired. -- It comes down to find the adult to whom the expire date -- will be checked. declare @adultID smallint declare @expDate datetime -- lookup the juvenile table for the sponsoring adult of @member_no select @adultID = j.adult_member_no from juvenile as j where j.member_no = @member_no if (@adultID is null) begin -- @member_no does not identify a juvenile member, just use the -- input @member_no as the adult set @adultID = @member_no end select @expDate = a.expr_date from adult as a where a.member_no = @adultID if (@expDate is null) begin -- The input @member_no does not identify a member of the library raiserror('Member not found', 16, 3) return end if (@expDate < getdate()) begin -- This account is expired raiserror('Account expired', 16, 8) return end -- Set up the loan time frame dates. declare @out_date datetime set @out_date = getdate() declare @due_date datetime set @due_date = dateadd(dd, 14, @out_date) -- Start the transaction to add a row to the loan table -- and update a row on the copy table (derived info). begin transaction begin try
  • 11. -- Add a row to the loan table. insert into loan Check out item (stored proc) continued from previous page . . . (isbn , copy_no , title_no , member_no , out_date , due_date) values (@isbn , @copy_no , @title_no , @member_no , @out_date , @due_date) if @@error <> 0 begin raiserror('Failed inserting loan row', 16, 8) end -- Update a row in the copy table. update copy set on_loan = 'Y' where isbn = @isbn and copy_no = @copy_no if (@@error <> 0) begin raiserror('Failed updating copy row', 16, 8) end end try begin catch -- Roll back everything done so far rollback transaction -- Set error information declare @errorMessage nvarchar(40); declare @errorSeverity int; declare @errorState int; select @errorMessage = error_message() , @errorSeverity = error_severity() , @errorState = error_state(); -- Return error information about the original error -- that caused execution to jump to the catch block. raiserror (@errorMessage, @errorSeverity, @errorState) return end catch commit transaction end
  • 12. All the data validations in the procedure above are performed prior to start the transaction that accomplishes the actual task, since no assumption was made on them. While doing that, some local variables were set to be used through the process. Comment to stored procedure continued from previous page . . . Error handling was implemented consistently as well. Within the transaction, any error that was raised was caught and a new error is raised by using the parameters that were set in the original one, thus making the catch section working independently from where the error was originated.
  • 13. Library – The Windows Presentation Tier The Windows form above is the Member Information Form, displaying the member’s information and current books that are on loan. The form is contained within an MDI parent. The librarian can check books in from this form through the parent’s MDI main menu.
  • 14. Library – The Windows Presentation Tier The Windows form above is the Add Adult Member Form, displaying the input member’s information to be filled in. For each of the requested filed an error icon is shown which displays the reason why the validation is failing once the cursor hover over the icon. The user message at the bottom of the form prompts the user about the action that needs to take place.
  • 15. Handling Data Transactions through Web Pages Get Member Info within a Web Page. /// <summary> /// The event handler for the click event on the Get Member Info button. /// </summary> /// <param name="sender">The sender object.</param> /// <param name="e">Info associated to this event.</param> protected void btnGetMemberInfo_Click(object sender, EventArgs e) { // Clean up status info anyway. lblUserWarning.Text = string.Empty; lblUserMessage.Text = string.Empty; if (!IsValid) { lblUserMessage.Text = "Please correct all indicated errors and try again."; return; } // Retrieve member object. short memberId = short.Parse(tbxMemberID.Text); Member theMember = lda.GetMember(memberId); // Reset anyway. ClearAllFields(); if (theMember == null) { // Let the user know about the problem before hand, // while last error message is still available. lblUserMessage.Text = lda.LastErrorMessage; return; } // If this is a juvenile member and is older than 18 years, // ask the user permission to issue a new membership as an // adult JuvenileMember theJuvenile = theMember as JuvenileMember; if (theJuvenile != null) { DateTime adultPivotDate = DateTime.Today; adultPivotDate = adultPivotDate.AddYears(-18); if (adultPivotDate.CompareTo(theJuvenile.BirthDate) >= 0) { // The juvenile member is 18 years or older. // Force membership conversion to adult anyway. theMember = lda.ConvertMember(theJuvenile);
  • 16. if (theMember == null) { // Let the user know about the problem before hand, // while last error message is still available. Get Member Info event handler continued from previous page . . . lblUserMessage.Text = lda.LastErrorMessage; return; } tbxMemberID.Text = theMember.MemberID.ToString(); // Notify the user about the conversion. lblUserMessage.Text = string.Format( "A new adult member ID = {0} was issued to replace {1}.", theMember.MemberID.ToString(), theJuvenile.MemberID.ToString()); } } else { // See if this account is expired and, if it is, give the // user the opportunity to issue a new membership card. AdultMember theAdult = theMember as AdultMember; DateTime expDate = theAdult.ExpirationDate; if (expDate.CompareTo(DateTime.Today) < 0) { // This account is expired. StringBuilder sb = new StringBuilder(); sb.AppendFormat("{0} ", theAdult.FirstName); if (theAdult.MiddleInitial.Length > 0) sb.AppendFormat("{0}. ", theAdult.MiddleInitial); sb.AppendFormat("{0} member ID ({1}) is expired. ", theAdult.LastName, theAdult.MemberID.ToString()); sb.Append("Proceed to renew his/her membership?"); // Save state before any postbak occurs. oldMemberID = theAdult.MemberID; oldMember = theAdult; // Enable the user to make the decision. lblRequestRenewAccount.Text = sb.ToString(); btnGetMemberInfo.Enabled = false; pnlRenewalRequest.Visible = true; pnlRenewalRequest.Focus(); return; } } // Refresh form controls data. PopulateFields(theMember); RefreshOutstandingLoansGrid(); } The method above is located in the Content_Pages_AddAdultMember class for the Web Presentation tier. After validating data in the web page, if the requested member of the library
  • 17. is a juvenile and its age falls off the juvenile range, a new card is automatically issued with adult membership and the user notified about that. Comment to Get Member continued from previous page . . . If the requested member is an adult and the membership is expired, the librarian is prompted whether to renew it. The librarian is being given both the option to proceed to renew the membership or keep it as is. Proceed to renew the adult membership that is currently expired /// <summary> /// The event handler for the click event on the Yes button to have the /// adult's membership renewed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">Info associated to this event.</param> protected void btnAcceptRenewal_Click(object sender, EventArgs e) { // Make sure the required data are still cached. if (oldMember == null || oldMemberID <= 0) { lblUserMessage.Text = "Internal error occurred."; return; } // Ok, do the renewal. Member theMember = lda.RenewMember(oldMember); if (theMember == null) { // Let the user know about the problem before hand, // while last error message is still available. lblUserMessage.Text = lda.LastErrorMessage; oldMember = null; oldMemberID = 0; return; } tbxMemberID.Text = theMember.MemberID.ToString(); // Notify the user about the conversion. lblUserMessage.Text = string.Format( "A new member ID = {0} was issued to renew expired ID = {1} membership.", theMember.MemberID.ToString(), oldMemberID.ToString()); // Refresh form controls data. PopulateFields(theMember); RefreshOutstandingLoansGrid(); ResetStatusAndRenewalPanel(); }
  • 18. The method above is located within the same Content_Pages_AddAdultMember class to handle the case where the librarian accepts the renewal of the adult membership. Upon successful renewal, the web page is refreshed with the newly created membership data. Reject the request of renewing the adult membership, i.e. keep it as is /// <summary> /// The event handler for the click event on the No button to not have the /// adult's membership renewed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">Info associated to this event.</param> protected void btnRejectRenewal_Click(object sender, EventArgs e) { // Make sure the required data are still cached. if (oldMember == null || oldMemberID <= 0) { lblUserMessage.Text = "Internal error occurred."; return; } // Warn the user that the current account is expired. lblUserWarning.Text = "The current account is expired."; // Refresh form controls data. PopulateFields(oldMember); RefreshOutstandingLoansGrid(); ResetStatusAndRenewalPanel(); } The method above is located within the same Content_Pages_AddAdultMember class to handle the case where the librarian rejects the renewal of the adult membership. The data are shown as they currently are in the database but a warning highlights to the librarian that the membership is expired. This forces restrictions in the service being provided. For instance, no items (books) can be loaned to that adult member.
  • 19. Library – The Web Presentation Tier The Web Page above is the Log in Page where only the members of the Librarian role are granted access to, in accordance to the Specifications.
  • 20. Library – The Web Presentation Tier The Web Page above is the Member Information Page, displaying the member’s information and current books that are on loan. If there are no books currently on loan, the grid is missing. The librarian can check books in from this page.