Library Phase II – Windows Business & Data Access TiersIntroduction:For this project, I was responsible for developing the...
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Michael Colon Portfolio
Upcoming SlideShare
Loading in...5
×

Michael Colon Portfolio

493

Published on

C#, .NET, ADO.NET, SQL

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
493
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
0
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Michael Colon Portfolio"

  1. 1. Library Phase II – Windows Business & Data Access TiersIntroduction:For this project, I was responsible for developing the Windows Forms, Business Layer, and Data Access Layer 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. Created a custom exceptions class to catch Sql Exceptions and give the user a more detailed message, while avoiding an Exception error. The business tier acts as a middle tier between the Windows Application tier and the Data Access tier, passing data as objects to separate Data Access from Presentation Layer completely and ensuring that no SQL Code is in the application to prevent SQL Injection Attacks.Audience:This project is designed for library personnel 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, adhering to windows forms best-practices and multiple document interface usage. Project Goals:Design the Presentation, Business and Data Access tiers.Develop code that is scalable and easily maintainable.Provide adequate error handling.Use database-programming techniques that provide maximum programming flexibility and control while minimizing resource utilization.<br />Use of ADO.NET for Utilizing SQL Stored ProceduresGet Members/// <summary> /// Gets the Member, if one exists, with the specified member number. /// </summary> /// <param name=" memberNumber" >The member's member number (ID).</param> /// <returns>The Member with the specified member number.</returns> public Member GetMember(short memberNumber) { Member member = null; //Establish the connection using (SqlConnection con = new SqlConnection(MC.DataAccess.Properties.Settings.Default.LibraryConnectionString)) { //Instantiate a command with the connection for stored procedure //dbo.Adult_Member_Sel using (SqlCommand cmd = new SqlCommand(" dbo.Member_Sel" , con)) { cmd.CommandType = CommandType.StoredProcedure; #region Add Parameters cmd.Parameters.AddWithValue(" @member_no" , memberNumber); SqlParameter param = new SqlParameter(" @lastname" , SqlDbType.VarChar, 15); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @firstname" , SqlDbType.VarChar, 25); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @middleinitial" , SqlDbType.Char, 1); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @street" , SqlDbType.VarChar, 15); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @city" , SqlDbType.VarChar, 15); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @state" , SqlDbType.VarChar, 2); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); Continued on next page…<br />Get Members Continued… param = new SqlParameter(" @zip" , SqlDbType.VarChar, 10); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @phone_no" , SqlDbType.VarChar, 13); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @expr_date" , SqlDbType.DateTime, 8); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @is_juvenile" , SqlDbType.Bit, 1); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @adult_member_no" , SqlDbType.SmallInt, 2); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); param = new SqlParameter(" @birth_date" , SqlDbType.DateTime, 8); param.Direction = ParameterDirection.Output; cmd.Parameters.Add(param); #endregion try { //Open our connection and execute our query con.Open(); int returnValue = cmd.ExecuteNonQuery(); //Check to see if we have a juvenile if ((bool)cmd.Parameters[" @is_juvenile" ].Value) { JuvenileMember jm = new JuvenileMember(); //Fill member properties jm.MemberID = (short)cmd.Parameters[" @member_no" ].Value; jm.LastName = cmd.Parameters[" @lastname" ].Value.ToString(); jm.FirstName = cmd.Parameters[" @firstname" ].Value.ToString(); jm.MiddleInitial = cmd.Parameters[" @middleinitial" ].Value.ToString(); jm.Street = cmd.Parameters[" @street" ].Value.ToString(); jm.City = cmd.Parameters[" @city" ].Value.ToString(); jm.State = cmd.Parameters[" @state" ].Value.ToString(); jm.ZipCode = cmd.Parameters[" @zip" ].Value.ToString(); jm.PhoneNumber = cmd.Parameters[" @phone_no" ].Value.ToString(); jm.ExpirationDate = Convert.ToDateTime(cmd.Parameters[" @expr_date" ].Value); jm.AdultMemberID = (short)cmd.Parameters[" @adult_member_no" ].Value;Continued on next page…<br />Get Members Continued…jm.BirthDate = Convert.ToDateTime(cmd.Parameters[" @birth_date" ].Value); //Cast the juvenile member to a member member = (Member)jm; } else { member = new Member(); //Fill member properties member.MemberID = (short)cmd.Parameters[" @member_no" ].Value; member.LastName = cmd.Parameters[" @lastname" ].Value.ToString(); member.FirstName = cmd.Parameters[" @firstname" ].Value.ToString(); member.MiddleInitial = cmd.Parameters[" @middleinitial" ].Value.ToString(); member.Street = cmd.Parameters[" @street" ].Value.ToString(); member.City = cmd.Parameters[" @city" ].Value.ToString(); member.State = cmd.Parameters[" @state" ].Value.ToString(); member.ZipCode = cmd.Parameters[" @zip" ].Value.ToString(); member.PhoneNumber = cmd.Parameters[" @phone_no" ].Value.ToString(); member.ExpirationDate = Convert.ToDateTime(cmd.Parameters[" @expr_date" ].Value.ToString()); } } catch (SqlException sqlex) { switch (sqlex.State) { case 2: throw new LibraryException(ErrorCode.NoSuchMember); break; default: throw new LibraryException(ErrorCode.GenericException, sqlex.Message); break; } } catch (Exception ex) { throw new LibraryException(ErrorCode.GenericException, ex.Message); } } } return member; } #endregion }}This method is located in the Data Access class, which handles all calls to the database by use of stored procedures. The SQLCommand object is populated with input and output parameters, as well as calling a Custom Exceptions class. The catch blocks handle any SQLExceptions and throw a new custom Library Exception back to the presentation layer.<br />Custom Exception Handlingpublic class LibraryException : Exception { private ErrorCode libraryErrorCode; public ErrorCode LibraryErrorCode { get { return libraryErrorCode; } } /// <summary> /// Calls base classes non-default constructor with string passed in. /// </summary> /// <param name=" msg" ></param> public LibraryException(string msg) : base(msg) { libraryErrorCode = ErrorCode.None; } /// <summary> /// Initializes a new instance of the LibraryException class with a specified ErrorCode. /// </summary> /// <param name=" errorCode" >The error code that indicates the reason for the exception.</param> public LibraryException(ErrorCode errorCode) { libraryErrorCode = errorCode; } /// <summary> /// Initializes a new instance of the LibraryException class with a specified ErrorCode and a specified error message. /// </summary> /// <param name=" errorCode" >The error code that indicates the reason for the exception.</param> /// <param name=" msg" >The error message that explains the reason for the exception. </param> public LibraryException(ErrorCode errorCode, string msg) : base(msg) { libraryErrorCode = errorCode; } #endregion }This is a partial representation of a custom exceptions class I created for catching SQL Exceptions and rethrowing the exception as a Library Exception with a more detailed error message from the ErrorCode enumeration.<br />Custom Exception Handlingpublic class ExceptionMessages { /// <summary> /// Handles processing Library Exceptions /// </summary> /// <param name=" le" >Library Exception</param> /// <returns>Message</returns> public static string HandleExceptions(LibraryException le) { string s; switch (le.LibraryErrorCode) { case ErrorCode.AddAdultFailed: s = " Add Adult Failed." ; break; case ErrorCode.AddJuvenileFailed: s = " Add Juvenile Failed." ; break; case ErrorCode.CheckInFailed: s = " Check In Failed." ; break; case ErrorCode.CheckOutFailed: s = " Check Out Failed." ; break; case ErrorCode.GenericException: s = le.Message; break; case ErrorCode.ItemAlreadyOnLoan: s = " Item is Already On Loan." ; break; case ErrorCode.ItemNotFound: s = " The Item was Not Found." ; break; case ErrorCode.ItemNotOnLoan: s = " The Item is Not Currently On Loan." ; break; case ErrorCode.MissingAdultMember: s = " Missing Adult Member." ; break; case ErrorCode.NoSuchMember: s = " Member not found." ; break; case ErrorCode.None: s = " No Error." ; break; default: s = " Exception Occurred." ; break; } return s; } }<br />SQL Stored ProcedureCreate a Stored ProcedureCREATE PROCEDURE [dbo].[Member_Sel] -- Add the parameters for the stored procedure here@member_no SMALLINT = NULL,@lastname VARCHAR(15) = NULL OUTPUT, @firstname VARCHAR(15) = NULL OUTPUT, @middleinitial CHAR(1) = NULL OUTPUT,@street VARCHAR(15) = NULL OUTPUT,@city VARCHAR(15) = NULL OUTPUT,@state CHAR(2) = NULL OUTPUT,@zip CHAR(10) = NULL OUTPUT,@phone_no CHAR(13) = NULL OUTPUT,@expr_date DATETIME = NULL OUTPUT,@is_juvenile BIT = 0 OUTPUT,@adult_member_no SMALLINT = NULL OUTPUT,@birth_date DATETIME = NULL OUTPUTASBEGIN-- SET NOCOUNT ON added to prevent extra result sets from-- interfering with SELECT statements.SET NOCOUNT ON;--Check for nullsIF (@member_no IS NULL) OR (@member_no < 1) OR (@member_no > 32767)BEGINRAISERROR('Member ID is not valid.', 11, 1);RETURNEND--Check to see if member_no is in the juvenile tableIF (SELECT COUNT(*) FROM Juvenile WHERE member_no = @member_no) > 0BEGIN --Juvenile functionalitySET @is_juvenile = 1--Get juvenile informationSELECT @lastname = m.lastname, @firstname = m.firstname, @middleinitial = m.middleinitial,@birth_date = birth_date, @adult_member_no = adult_member_noFROM Juvenile AS jJOIN Member as mON j.member_no = @member_no AND m.member_no = @member_no--Get parent's information from @adult_member_noSELECT @street = street, @city = city, @state = state, @zip = zip, @expr_date = expr_date,@phone_no = phone_noFROM Adult WHERE member_no = @adult_member_noEND<br />Stored Procedure Continued…ELSE IF (SELECT COUNT(*) FROM Adult WHERE member_no = @member_no) > 0BEGIN --Adult functionalitySET @is_juvenile = 0--Get adult informationSELECT @lastname = m.lastname, @firstname = m.firstname, @middleinitial = m.middleinitial,@street = a.street, @city = a.city, @state = a.state, @zip = a.zip, @expr_date = a.expr_date,@phone_no = a.phone_noFROM Adult AS a JOIN Member AS mON a.member_no = @member_no AND m.member_no = @member_noENDELSE BEGIN --Member does not exist errorRAISERROR('Member does not exist.', 11, 2);The following stored procedure is being called by an ADO.NET SqlCommand object. The stored proc returns all info into output parameters from either the Juvenile or Adult Table. If an error occurs, it raises it’s own SqlException with a specified State, which the Data Access Layer then bubbles up to the Presentation Layer for proper error handling and notification to the user.<br />Business Layer public class BusinessLayer { /// <summary> /// Constructor /// </summary> public BusinessLayer()… /// <summary> /// Get Member from database /// </summary> /// <param name=" memberId" >memberId to look up</param> /// <returns>Member</returns> public Member GetInformation(short memberId) { //Create a library data access object LibraryDataAccess lda = new LibraryDataAccess(); //Call the Get member method and pass in the memberId Member myMember = lda.GetMember(memberId); return myMember; } /// <summary> /// Get ItemsDataSet of books on loan /// </summary> /// <param name=" memberId" >memberId</param> /// <returns>ItemsDataSet</returns> public ItemsDataSet GetBooks(short memberId) { //Create a library data access object LibraryDataAccess lda = new LibraryDataAccess(); //Call the GetItems method ItemsDataSet ids = lda.GetItems(memberId); return ids; } /// <summary> /// Updates DB for checking out item /// </summary> /// <param name=" memberId" >member who checked out book</param> /// <param name=" isbn" >ISBN of Book</param> /// <param name=" copyNo" >copyNo of Book</param> public void CheckOutBook(short memberId, int isbn, short copyNo) { //Create a library data access object LibraryDataAccess lda = new LibraryDataAccess(); //Call the CheckInItem Method lda.CheckOutItem(memberId, isbn, copyNo); } }The above class is partial and represents some of the methods within the business tier and communicates data between the presentation layer and the data access layer. <br />Implementing Interfaces in Custom Classes [DeveloperInfo(" Mike Colon" , Date = " 04/22/2009" , Title = " Products" )] [CustomDescription(" Products is a collection of Product: IList<Product>" )] [Serializable] public class Products : IList<Product> { #region Properties and Fields private const string duplicateMsg = " Product is already in the collection." ; /// <summary> /// collection of type Product /// </summary> protected List<Product> productsCollection; /// <summary> /// event of type CollectionModifiedHandler /// </summary> public event CollectionModifiedHandler CollectionModified; #endregion #region Constructors /// <summary> /// Default Constructor that initializes our collection. /// </summary> public Products() { productsCollection = new List<Product>(); } #endregion #region Methods /// <summary> /// Raises the CollectionModified event /// </summary> /// <param name=" args" ></param> public void OnCollectionModified(ModificationEventArgs args) { if (CollectionModified != null) CollectionModified(this, args); } /// <summary> /// Raises the event handler with an unsuccessful change to the collection. /// </summary> public void NotifyUnsuccessfulChange(Product p) { //Communicates to the event handler event details ModificationEventArgs mea = new ModificationEventArgs(ModificationEventStatus.unsuccessful, p); //Raise the event OnCollectionModified(mea); }<br />Implementing Interfaces Continued… /// <summary> /// Calls the event handler of a successful change to the collection. /// </summary> public void NotifySuccessfulChange(Product p) { //Communicates to the event handler that the change was successful ModificationEventArgs mea = new ModificationEventArgs(ModificationEventStatus.successful, p); //Raise the event OnCollectionModified(mea); } /// <summary> /// Sort the collection. /// </summary> public void Sort() { productsCollection.Sort(); } /// <summary> /// Sort the collection with comparer /// </summary> /// <param name=" comparer" >IComparer of type Product</param> public void Sort(IComparer<Product> comparer) { productsCollection.Sort(comparer); } /// <summary> /// Creates new Product. /// </summary> /// <returns>object of type Product.</returns> public static Product CreateProduct() { return new Product(); } #endregion #region IList<Product> Members /// <summary> /// Provides the index of the item in the collection. /// </summary> /// <param name=" item" >Item to be found.</param> /// <returns>Index of the item.</returns> public int IndexOf(Product item) { return productsCollection.IndexOf(item); }<br />Implementing Interfaces Continued… /// <summary> /// Insert item of type Product at the specified index. /// </summary> /// <param name=" index" >index to insert item.</param> /// <param name=" item" >item of type Product to insert.</param> public void Insert(int index, Product item) { //Check to make sure object is not already in the collection if (productsCollection.Contains(item)) { NotifyUnsuccessfulChange(item); throw new AppTypesException(duplicateMsg); } else { //Check to make sure index is in range if ((index >= productsCollection.Count) || (index < 0)) { NotifyUnsuccessfulChange(item); throw new IndexOutOfRangeException(" Index to insert item into collection is out of range." ); } else { NotifySuccessfulChange(item); productsCollection.Insert(index, item); } } } /// <summary> /// Remove item of type Product at the specified index. /// </summary> /// <param name=" index" >index of item to be removed from collection.</param> public void RemoveAt(int index) { if ((index >= productsCollection.Count) || (index < 0)) { NotifyUnsuccessfulChange(null); throw new IndexOutOfRangeException(" Index of the item to be removed does not exist in the collection." ); } else { productsCollection.RemoveAt(index); NotifySuccessfulChange(null); } } <br />Implementing Interfaces Continued… /// <summary> /// Retrieves or Replaces Product at specified index. /// </summary> /// <param name=" index" >Product of interest at this index.</param> /// <returns>item of type Product</returns> public Product this[int index] { get { if ((index >= productsCollection.Count) || (index < 0)) throw new IndexOutOfRangeException(" Product does not exist at specified index." ); else return productsCollection[index]; } set { //Check to make sure object is not already in the collection if (productsCollection.Contains(value)) { NotifyUnsuccessfulChange(this[index]); throw new AppTypesException(duplicateMsg); } else { productsCollection[index] = value; NotifySuccessfulChange(this[index]); } } } #endregion #region ICollection<Product> Members /// <summary> /// Add the item to the collection and notify registered listeners of the event status. /// </summary> /// <param name=" item" >item of type Product to be added to the collection.</param> public void Add(Product item) { //Check to make sure object is not already in the collection if (this.Contains(item)) { //Communicates to the event handler event details NotifyUnsuccessfulChange(item); throw new AppTypesException(duplicateMsg); } else { productsCollection.Add(item); NotifySuccessfulChange(item); } }<br />Implementing Interfaces Continued… public void Clear() { productsCollection.Clear(); NotifySuccessfulChange(null); } /// Determine whether or not the collection contains item public bool Contains(Product item) { return productsCollection.Contains(item); } /// Copy the array to the collection. public void CopyTo(Product[] array, int arrayIndex) { productsCollection.CopyTo(array, arrayIndex); //Communicates to the event handler that the change was successful ModificationEventArgs mea = new ModificationEventArgs(ModificationEventStatus.successful, array); //Raise the event OnCollectionModified(mea); } /// Count property of the collection. public int Count { get { return productsCollection.Count; } } /// Indicates whether the list is read only or not. public bool IsReadOnly { get { return false; } } /// <summary> /// Removes item of type Product if the collection contains it. /// </summary> /// <param name=" item" >item to be Removed</param> /// <returns>True if removed</returns> public bool Remove(Product item) { //Make sure our collection has the item before trying to remove if (productsCollection.Contains(item)) { productsCollection.Remove(item); NotifySuccessfulChange(item); return true; } else { NotifyUnsuccessfulChange(item); return false; } } #endregion<br />Presentation Layer Code Sample private void btnGetMember_Click(object sender, EventArgs e) { if (string.Empty.Equals(epMemberID.GetError(tbMemberId))) { //Fill in member info BusinessLayer bl = new BusinessLayer(); Member myMember = null; try { //Try getting the member from LibraryBusiness myMember = bl.GetInformation(short.Parse(tbMemberId.Text)); //Set our goodMemberId; goodMemberId = tbMemberId.Text; //Set the rest of our textboxes tbDateExpires.Text = myMember.ExpirationDate.ToShortDateString(); tbFirstName.Text = myMember.FirstName; tbMiddleInitial.Text = myMember.MiddleInitial; tbLastName.Text = myMember.LastName; tbStreet.Text = myMember.Street; tbCity.Text = myMember.City; tbState.Text = myMember.State; tbZip.Text = myMember.ZipCode; tbPhone.Text = myMember.PhoneNumber; //Bind ItemsDataSet to Data Grid View itemsBindingSource.DataSource = bl.GetBooks(short.Parse(tbMemberId.Text)); itemsBindingSource.DataMember = " Items" ; //Enable Check in button if rows exist in DataSource if (dgvItemsGrid.RowCount > 0) btnCheckIn.Enabled = true; else btnCheckIn.Enabled = false; //If this is a juvenile member, show Juvenile group box JuvenileMember juvMember = myMember as JuvenileMember; if (juvMember != null) { gbJuvenile.Visible = true; tbAdultId.Text = juvMember.AdultMemberID.ToString(); tbDateBorn.Text = juvMember.BirthDate.ToShortDateString(); } else gbJuvenile.Visible = false;Continued on next page…<br />Presentation Layer Code Sample Continued… //Highlight Expired date if it is greater than now if (myMember.ExpirationDate < DateTime.Now) { tbDateExpires.BackColor = Color.Yellow; toolStripStatusLabel1.Text = " Membership has expired." ; } else { tbDateExpires.BackColor = Color.White; toolStripStatusLabel1.Text = " Done." ; } } catch (LibraryException le) { //Call our static method that brings back appropriate exception message ClearAllFields(); goodMemberId = string.Empty; toolStripStatusLabel1.Text = ExceptionMessages.HandleExceptions(le); } } else { //Member ID was incorrect ClearAllFields(); goodMemberId = string.Empty; toolStripStatusLabel1.Text = " Please correct all errors and resubmit." ; } }<br />Library Phase 2This screen shot is of the Member Information form, displaying the member’s information and current books that are on loan. The form is contained within an MDI parent.<br />

×