Library Project Phase 1
Introduction: Create a Windows Forms front end C# application and a business tier for an n-tier project.
Audience:Library employees managing Member and Item information
Project Goals: Build parts of the business tier for a library involving members and items.
Create and test two assemblies.
The first assembly is a class library project containing various interfaces and base classes.
The second assembly is a class library project containing various entity, collection, and validation classes used by existing business processes.
Code should follow best practices
Produce a user interface that is intuitive, requiring minimal training for users while minimizing resource utilization
Include easily maintainable code, form-based field validation, and all required error handling.
Phase 1 – MDI Screen with Member Info
The application runs inside an MDI form. The Member Info screen displays when application is brought up. It displays the items currently checked out. Librarian can select one or more checked out items to check in. If the member has less than four items checked out a check out button will appear. Juvenile members will show two additional fields: Birth Date and Adult Member ID.
Sample Code
public void LoadMemberInfo() { memIDTextBox.Text = memID.ToString(); LibraryAccess la = new LibraryAccess(); try { Member member = la.GetMember(memID); JuvenileMember juv = member as JuvenileMember; if (juv == null) { AdultMember adult = member as AdultMember; if (adult == null) { memInfoStatusLabel.Text = "System Error - Try Again"; return; } else { LoadAdultData(adult); } } else { LoadJuvData(juv); } LoadCheckOutList(); } catch (LibraryException ex) { memInfoStatusLabel.Text = "Member Id was not found"; ClearForm(); } catch { memInfoStatusLabel.Text = "System Error. Check Member ID and Try Again"; ClearForm(); }
}
private void LoadAdultData(AdultMember memb) { firstNameTextBox.Text = memb.FirstName; initialTextBox.Text = memb.MiddleInitial; lastNameTextBox.Text = memb.LastName; streetTextBox.Text = memb.Street; cityTextBox.Text = memb.City; zipTextBox.Text = memb.ZipCode; expDateTextBox.Text = memb.ExpirationDate.ToString("d"); TimeSpan ts = DateTime.Now - memb.ExpirationDate; if (ts.Days > 0) {
expDateTextBox.BackColor = Color.Yellow; memIdExpired = true; } else { expDateTextBox.BackColor = Color.WhiteSmoke; memIdExpired = false; } stateTextBox.Text = memb.State; phoneTextBox.Text = memb.PhoneNumber; birthDateLable.Hide(); birthDateTextBox.Hide(); adultIDLabel.Hide(); adultIDTextBox.Hide(); }
private void LoadJuvData(JuvenileMember memb) { firstNameTextBox.Text = memb.FirstName; initialTextBox.Text = memb.MiddleInitial; lastNameTextBox.Text = memb.LastName; streetTextBox.Text = memb.Street; cityTextBox.Text = memb.City; zipTextBox.Text = memb.ZipCode; expDateTextBox.Text = memb.ExpirationDate.ToString("d"); TimeSpan ts = DateTime.Now - memb.ExpirationDate; if (ts.Days > 0) { expDateTextBox.BackColor = Color.Yellow; memIdExpired = true; } else { expDateTextBox.BackColor = Color.WhiteSmoke; memIdExpired = false; } stateTextBox.Text = memb.State; phoneTextBox.Text = memb.PhoneNumber; birthDateLable.Show(); birthDateTextBox.Show(); adultIDLabel.Show(); adultIDTextBox.Show(); birthDateTextBox.Text = memb.BirthDate.ToString("d"); adultIDTextBox.Text = memb.AdultMemberID.ToString(); }
public void LoadCheckOutList() { LibraryAccess la = new LibraryAccess(); try { ItemsDataSet bookList = la.GetCheckedOutBooks(memID); itemsBindingSource.DataSource = bookList; if (memIdExpired || bookList.Items.Count > 3) checkOutButton.Visible = false;
else checkOutButton.Visible = true; } catch { memInfoStatusLabel.Text = "System Error Try Again"; } }
The Member Information screen displays both Juvenile and Adult information. If the Membership has expired than the expiration date is Highlighted and the Check Out button is hidden. If the member has 4 items checked out the Check Out button is hidden. The Librarian can highlight any checked out item and click the Check In Selected to check in books. There is a separate Check In screen to check in a book without a Member ID.
Add Adult, Add Juvenile and Check In Screens
There are basic edits for the fields. All names must begin with a capital letter. State is selected from a drop down. Zip code must be 5 or 9 numeric characters. Birth date must show member is less than 18 years old. The Adult Member Id must be numeric and will be validated against the data base. ISBN and Copy must be numeric and within valid range.
Sample Validation Code public static bool AdultIDValid(string str) { int memID; if (int.TryParse(str, out memID)) { if (memID > 0 && memID <= 32767) { // get from db return true; } else { return false; } } else { return false; } }
/// <summary> /// Validates first name strats with a capital letter and all others are lower case a-z /// </summary> /// <param name="str"></param> /// <returns></returns> public static bool FirstNameValid(string str) { if(Regex.IsMatch(str,"^[A-Z][a-z]{0,14}$")) return true; else return false; }
/// <summary> /// Validates that the Initial is a capital lettr A-Z or blank /// </summary> /// <param name="str"></param> /// <returns></returns> public static bool InitialValid(string str) { if (Regex.IsMatch(str, @"^[A-Z\s]{0,1}$")) return true; else return false; }
/// <summary> /// Validates Last name strats with a capital letter and all others are lower case a-z /// </summary> /// <param name="str"></param>
/// <returns></returns> public static bool LastNameValid(string str) { if (Regex.IsMatch(str, "^[A-Z][a-z]{0,14}$")) return true; else return false; }
/// <summary> /// Validates that ISBN is a valid positive integer /// </summary> /// <param name="str"></param> /// <returns></returns> public static bool ISBNValid(string str) { int isbn; if (int.TryParse(str, out isbn)) { if (isbn > 0) { return true; } else { return false; } } else { return false; }
}
/// <summary> /// Validated that Copy Number is a valid positive Short int /// </summary> /// <param name="str"></param> /// <returns></returns> public static bool CopyNumValid(string str) { short copyNum; if (short.TryParse(str, out copyNum)) { if (copyNum > 0 && copyNum <= 32767) { return true; } else { return false; } } else {
return false; } } /// <summary> /// Validated that Member ID is a valid positive Short int /// </summary> /// <param name="str"></param> /// <returns></returns> public static bool MemberIDValid(string str) { short memID; if (short.TryParse(str, out memID)) { if (memID > 0 && memID <= 32767) { return true; } else { return false; } } else { return false; } } }}
Library Project Phase 2
Introduction: Create the data access layer for the library application.
Audience:Library employees managing Member and Item information
Project Goals: Build parts Data access Layer
Develop code to call the store procedures.
Return data or throw errors to Business Layer.
Develop Store procedures for all processes
Create Entities classes to pass data between layers.
Member
Adult (derived from member)
Juvenile (derived from member)
Item
ItemsDataSet (holds multiple Items)
Library Exception
Sql ExamplesThe following validates the juvenile data than creates a Juvenile MemberCREATE proc [dbo].[AddJuvenile] @firstname varchar(15), @middleinitial char(1),
@lastname varchar(15), @adultmemno smallint, @birthdate datetime, @member_no smallint outputasif @firstname = null or @firstname = ''
begin raiserror('First Name is required', 11,1) returnend
if @lastname = null or @lastname = ''begin raiserror('Last Name is required', 11,1) returnend
if not exists (select member_no from adult where member_no = @adultmemno and datediff(day,getdate(),expr_date) > 0)
begin raiserror('Invalid Adult Member Number', 12,1) returnend
if datediff(day,@birthdate,getdate()) > 6575begin raiserror('Birthdate is over 17 years not valid for juvenile', 13,1) returnend
begin traninsert into member (lastname,firstname,middleinitial)values (@lastname,@firstname,@middleinitial)if @@ERROR <> 0
begin rollback tran raiserror('System failure. Unable to add member',16,1) return
endset @member_no = scope_identity()insert into juvenile(member_no,adult_member_no,birth_date)values(@member_no, @adultmemno,@birthdate)if @@ERROR <> 0
begin rollback tran raiserror('System failure. Unable to add juvenile',16,1) return
endcommit tran
The following Proc is used to check in an item
CREATE proc [dbo].[CheckInItem] @isbn int,
@copy smallintas
if not exists (select isbn from copy where isbn = @isbn and copy_no = @copy)
begin raiserror('Invalid isbn and copy num', 11,1) returnend
if exists (select isbn from copy where isbn = @isbn and copy_no = @copy and on_loan = 'N')
begin raiserror('Item not currently on loan', 12,1) return
end
begin tranupdate copy set on_loan = 'N' where isbn = @isbn and copy_no = @copyif @@error <> 0 begin rollback tran
raiserror('System error - occured in update to copy', 16,1) return
endinsert into loanhist (isbn,copy_no,out_date,title_no,member_no,due_date, in_date)select isbn,copy_no,out_date,title_no,member_no, due_date, getdate() from loan where isbn = @isbn and copy_no = @copy if @@error <> 0 begin rollback tran
raiserror('System error - occured inserting into loanhist', 16,1) return
enddelete from loan where isbn = @isbn and copy_no = @copyif @@error <> 0 begin rollback tran
raiserror('System error - occured deleting record from loan', 16,1) return
endcommit tran
The following proc will retrieve either the juvenile or adult member
CREATE proc [dbo].[GetMemberInfo] @memno smallintas if not exists (select member_no from member where member_no = @memno)begin raiserror('Member not found for member number',11,1) returnendif exists(select member_no from adult where member_no = @memno) begin select m.firstname,m.middleinitial,m.lastname,a.street,a.city,a.state, a.zip,a.phone_no,a.expr_date,null adult_member_no,null birth_date from member m join adult a on a.member_no = m.member_no where m.member_no = @memno if @@error <> 0 begin raiserror('System error getting adult info',16,1) return end endelse begin select m.firstname,m.middleinitial,m.lastname,a.street,a.city,a.state, a.zip,a.phone_no,a.expr_date,j.adult_member_no,j.birth_date from member m join juvenile j on m.member_no = j.member_no join adult a on a.member_no = j.adult_member_no where m.member_no = @memno if @@error <> 0 begin raiserror('System error getting juvenile info',16,1) return endend
Sample Access Layer Code
Check in an item throwing an exception if insert failes
/// <summary>/// Checks in a book using the ISBN and copy num/// </summary>/// <param name="isbn"></param>/// <param name="copy"></param>/// <returns></returns>public void CheckInItem(int isbn, short copy){ try { using (SqlConnection cnn = new SqlConnection( Properties.Settings.Default.LibraryConnectionString)) { using (SqlCommand cmd = new SqlCommand("CheckInItem", cnn)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddWithValue("@isbn", isbn); cmd.Parameters.AddWithValue("@copy", copy);
cnn.Open(); int results = cmd.ExecuteNonQuery(); if (results > 0) { return; } else { LibraryException lex = new LibraryException("System error no rows were updated"); throw lex; } } } } catch (SqlException ex) { if (ex.Class == 11) throw new LibraryException(ErrorCodes.ItemNotFound, ex.Message); else if (ex.Class == 12) throw new LibraryException(ErrorCodes.ItemNotOnLoan, sex.Message); else throw new LibraryException(ErrorCodes.CheckInFailed,
This procedures gets the member info. It determins if this is a juvenile or adault depending on the presence of an adult member ID.
public Member GetMember(short memID){ try { using (SqlConnection cnn = new SqlConnection( Properties.Settings.Default.LibraryConnectionString)) { using (SqlCommand cmd = new SqlCommand("GetMemberInfo", cnn)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddWithValue("@memno", memID); cnn.Open(); using (SqlDataReader rdr = cmd.ExecuteReader()) { if (rdr.Read()) { if (rdr["adult_member_no"].ToString() == "") { AdultMember amem = new AdultMember(); LoadMember(rdr, amem, memID); return amem; } else { JuvenileMember jmem = new JuvenileMember(); LoadMember(rdr, jmem, memID); return jmem; } } else { LibraryException lex = new LibraryException("System error no rows returned"); throw lex; }
} } } } catch (SqlException sqex) { if (sqex.Class == 11) throw new LibraryException(ErrorCodes.NoSuchMember, sqex.Message); else throw new LibraryException(ErrorCodes.GenericException, sqex.Message, sqex); }}
Sample Item Entity
public class Item{ private int isbn; private short copyNumber; private string title; private string author; private short memberNumber; private DateTime checkoutDate; private DateTime dueDate; /// <summary> /// /// </summary> /// <param name="Isbn"></param> /// <param name="copy"></param> /// <param name="itemtitle"></param> /// <param name="author"></param> /// <param name="memberno"></param> /// <param name="checkoutDate"></param> /// <param name="dueDate"></param> public Item(int Isbn, short copy, string itemtitle, string authr, short memno, DateTime outDate, DateTime due) { isbn = Isbn; copyNumber = copy; title = itemtitle; author = authr; memberNumber = memno; checkoutDate = outDate; dueDate = due; }
/// <summary> /// Get Isbn /// </summary> public int ISBN { get { return isbn; } }
/// <summary> /// Get Copy number /// </summary> public short CopyNumber { get { return copyNumber; } }
/// <summary> /// Get title for isbn /// </summary> public string Title { get { return title; } }
/// <summary> /// get author for the isbn /// </summary> public string Author { get { return author; } }
/// <summary> /// get member no of member who has item checked out /// </summary> public short MemberNumber { get { return memberNumber; } }
/// <summary> /// Date item was last checked out /// </summary> public DateTime CheckoutDate { get { return checkoutDate; } }
/// <summary> /// date item is due back /// </summary> public DateTime DueDate { get { return dueDate; } set { dueDate = value; } }}
Library Project Phase 3
Introduction: Replace Windows front-end with Web front-end
Audience:Library employees managing Member and Item information
Project Goals: Replace all Windows forms with Web front end
Additional requirements.
New screen to add a new item to inventory
Automatic conversion to an adult Membership if Juvenile is age is 18 or above.
Membership renewal for expired Member IDs
Add Security
Add logon screen
Prevent access to any screen without valid login
Member Information Window
The application runs inside a Master Page. The Member Info screen displays when after a successful login. It displays the items currently checked out. Librarian can select one or more checked out items to check in. If the member has less than four items checked out a check out button will appear. Juvenile members will show two additional fields: Birth Date and Adult Member ID.
New Features added. If member is expired it will transfer to a new window asking the librarian if they want to renew. Also if Juvenile is over 17 their membership will automatically be changed to an adult member. A notification of change is sent to librarian.
Sample Code
This code will set up the screen with the current user info when returning from the other screens.
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { ClearForm(); } object newid = Session["NewMemID"]; if(Session["NewMemID"] != null && (short)Session["NewMemID"] != 0) { MemID = (short)Session["NewMemID"]; txtMemberID.Text = MemID.ToString(); Session["NewMemID"] = (short) 0; Session["MemID"] = MemID; LoadMemberInfo(); } else { if (Session["MemID"] != null) { if (short.Parse(txtMemberID.Text) == 0) { MemID = (short)Session["MemID"]; txtMemberID.Text = MemID.ToString(); LoadMemberInfo(); } } } }
private void LoadMemberInfo() { LibraryAccess la = new LibraryAccess(); try { Member member = la.GetMember(memID);
JuvenileMember juv = member as JuvenileMember; if (juv == null) { AdultMember adult = member as AdultMember; if (adult == null) { memInfoStatusLabel.Text = "System Error - Try Again"; return; } else { LoadAdultData(adult);
} } else { LoadJuvData(juv); } if ( TextBoxBirthDate.Text != "" ) { DateTime bday = DateTime.Parse(TextBoxBirthDate.Text); TimeSpan ts = DateTime.Now - bday; if ((ts.Days - 5) / 365 > 17) { CreateAdultIDfromJuv(); } } } catch (LibraryException ex) { memInfoStatusLabel.Text = ex.Message; ClearForm(); } catch { memInfoStatusLabel.Text = "System Error. Check Member ID and Try Again"; ClearForm(); }
}
private void LoadAdultData(AdultMember memb) { TextBoxFirstName.Text = memb.FirstName; TextBoxInit.Text = memb.MiddleInitial; TextBoxLastName.Text = memb.LastName; TextBoxStreet.Text = memb.Street; TextBoxCity.Text = memb.City; TextBoxZip.Text = memb.ZipCode; TextBoxExpDate.Text = memb.ExpirationDate.ToString("d"); TimeSpan ts = DateTime.Now - memb.ExpirationDate; if (ts.Days > 0) { if (Session["RenewalRefused"] == null) Response.Redirect("~/Renew.aspx", true); else { if ((bool)Session["RenewalRefused"]) { TextBoxExpDate.BackColor = Color.Yellow; memIdExpired = true; Session["RenewalRefused"] = null; } } } else {
Session["RenewalRefused"] = null; TextBoxExpDate.BackColor = Color.WhiteSmoke; memIdExpired = false; } TextBoxState.Text = memb.State; TextBoxPhone.Text = memb.PhoneNumber; LabelBirthDate.Visible = false; TextBoxBirthDate.Text = ""; TextBoxBirthDate.Visible = false; LabelAdultID.Visible= false; TextBoxAdultID.Visible = false;}
private void LoadJuvData(JuvenileMember memb) { TextBoxFirstName.Text = memb.FirstName; TextBoxInit.Text = memb.MiddleInitial; TextBoxLastName.Text = memb.LastName; TextBoxStreet.Text = memb.Street; TextBoxCity.Text = memb.City; TextBoxZip.Text = memb.ZipCode; TextBoxExpDate.Text = memb.ExpirationDate.ToString("d"); TimeSpan ts = DateTime.Now - memb.ExpirationDate; if (ts.Days > 0) { TextBoxExpDate.BackColor = Color.Yellow; memIdExpired = true; } else { TextBoxExpDate.BackColor = Color.WhiteSmoke; memIdExpired = false; } TextBoxState.Text = memb.State; TextBoxPhone.Text = memb.PhoneNumber; LabelBirthDate.Visible = true; TextBoxBirthDate.Text = memb.BirthDate.ToString("d"); TextBoxBirthDate.Visible =true; LabelAdultID.Visible= true; TextBoxAdultID.Visible = true; TextBoxAdultID.Text = memb.AdultMemberID.ToString(); }
New Add Item Screen
Librarian can enter the ISBN and click Get Info for ISBN to get current information about an Item. It will also show the highest number copy number used. The librarian can add a new copy by updating the copy no and clicking Add New Item.
To add a new ISBN the librarian must enter all the information except Synopsis is optional.
Selected Code for Add Item Screen
protected void ButtonGetInfo_Click(object sender, EventArgs e) { LibraryAccess la = new LibraryAccess(); try { ItemAddInfo itm = la.GetLastCopyItem(short.Parse(TextBoxISBN.Text)); TextBoxCopy.Text = ""; TextBoxTitle.Text = itm.Title; TextBoxAuthor.Text = itm.Author; TextBoxSynopsis.Text = itm.Synopsis; TextBoxTrans.Text = itm.Translation; LabelLastCopy.Text = string.Format("Highest Copy No used {0}", itm.CopyNumber); if (itm.Cover == "HARDBACK" || itm.Cover == "Hardback") { RadioButtonSoft.Checked = false; RadioButtonHard.Checked = true; } else { RadioButtonHard.Checked = false; RadioButtonSoft.Checked = true; } if (itm.Loanable == "Y" || itm.Loanable == "y") { RadioButtonInHouse.Checked = false; RadioButtonLoan.Checked = true; } else { RadioButtonLoan.Checked = false; RadioButtonInHouse.Checked = true; } } catch (LibraryException lex) { TextBoxTitle.Text = ""; TextBoxAuthor.Text = ""; TextBoxSynopsis.Text = ""; TextBoxTrans.Text = ""; LabelLastCopy.Text = ""; RadioButtonHard.Checked = true; RadioButtonLoan.Checked = true; LabelErrorMsg.Text = lex.Message; } catch (Exception ex) { TextBoxTitle.Text = ""; TextBoxAuthor.Text = ""; TextBoxSynopsis.Text = ""; TextBoxTrans.Text = ""; LabelLastCopy.Text = ""; RadioButtonHard.Checked = true;
RadioButtonLoan.Checked = true; LabelErrorMsg.Text = "System Error " + ex.Message; }
} /// <summary> /// Adds the new Item. If ISBN exist than only a new copy is added /// otherwise it creates a title item and copy row /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void ButtonAddItem_Click(object sender, EventArgs e) { try { LibraryAccess la = new LibraryAccess(); string cover; if(RadioButtonHard.Checked == true) cover = "HARDBACK"; else cover = "SOFTBACK"; string loanable; if(RadioButtonLoan.Checked == true) loanable = "Y"; else loanable = "N"; la.AddNewItem(int.Parse(TextBoxISBN.Text), short.Parse(TextBoxCopy.Text), TextBoxTitle.Text, TextBoxAuthor.Text, TextBoxSynopsis.Text, TextBoxTrans.Text, cover, loanable); LabelErrorMsg.Text = "Item Added"; } catch (LibraryException lex) { LabelErrorMsg.Text = lex.Message; } catch (Exception ex) { LabelErrorMsg.Text = ex.Message; } }}