net portfolio

21
Matt Willmer’s .NET Portfolio Matt Willmer October 28, 2008

Upload: mwillmer

Post on 28-May-2015

1.634 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: NET Portfolio

Matt Willmer’s .NET Portfolio

Matt Willmer

October 28, 2008

Page 2: NET Portfolio

Summary This document contains a detailed explanation for the projects I worked on as part of the .NET Masters

program at SetFocus from August 3, 2009 to October 28, 2009. A full curriculum description for this

program can be found here: http://www.setfocus.com/MastersProgram/curriculum_dotnet.aspx All of

these projects were completed with .NET 3.5 using Visual Studio 2008, SQL Server 2008, IIS 6.0, and

Windows Server 2003.

Overall Requirements SetFocus required that I use a multi-tiered programming model for all of the projects that I’ve

completed. This means that each assignment was divided into at least one Visual Studio project for each

tier. The following is an example of the tiers that I used:

Data Access Tier – A class library consisting of all of the code needed to interact with the

database server. Generally, no other layer should contain any database interface code.

Business Tier – A class library serving as a link to process and pass data between the Data Access

and Presentation layers.

Presentation Tier – A Windows Forms application or ASP.NET web application containing all of

the code to display information to the user, and receive and validate user input.

Entities Tier – A class library consisting of classes that define objects used to pass data. This tier

is used by all other tiers. Typically the Data Access Tier will retrieve data from a database and

use it to populate properties in objects defined in the Entities Tier. Those objects are passed

through the Business Tier to the Presentation Tier to be consumed. When using LINQ to SQL,

this tier is automatically generated by Visual Studio based on the database design, so it’s not

always clearly separated from the Data Access Tier.

Project 1

Overview The purpose of this project was to get familiar with coding in C#, especially with some of the features

that make it unique from other languages like C++ or Java. This project was short and pretty simple--I

was given a very detailed textual description of all of the needed classes and interfaces for a plausible

business layer for a retail company. I put those specifications into code, and used a provided test dll to

ensure that the classes functioned properly. The best way to see how these interact with each other

would be to open the project in Visual Studio and use the Object Browser to look at their properties and

methods. But since I didn’t design the class structure (I only coded it), I’ll select code snippets from the

project to illustrate what I learned.

C# Properties While instance variables (or fields) can be used to store data in a class in most programming languages,

C# also provides a feature called Properties to enhance this. A public property can be accessed from

Page 3: NET Portfolio

outside of a class in the same way that a field would be accessed, but with a property the programmer

can specify code to run at the time the property is set or read through a set and get method. These

come in handy when validating data or when the data comes from some internal structure that must be

private. Additionally, the get and set methods for a public property can be set to private to make the

property read-only or write-only. The following code snippet illustrates this:

Code Snippet 1: AppTypes\Product.cs Lines 108 – 122

Working with System.Runtime.InteropServices.IEnumerator Enumerators allow a programmer to retrieve a series of values in a given order, one by one, often with a

foreach loop. One way to produce an object of type IEnumerable is to inherit from it, and implement its

methods to return values, and adjust the current value index. Another way is to use the yield return

statement. The yield return statement defines the next value that will be returned by the iterator. This

code snippet shows the yield return statement used to produce an IEnumerable that returns each of the

properties stored in this class, one by one:

Code Snippet 2: AppTypes\Supplier.cs Lines 127 - 142

public IEnumerable PropertyAndValuesCollection()

{

yield return String.Format("ID: {0}", ID);

yield return String.Format("CompanyName: {0}", CompanyName);

yield return String.Format("ContactName: {0}", ContactName);

yield return String.Format("ContactTitle: {0}", ContactTitle);

yield return String.Format("Address: {0}", Address);

yield return String.Format("City: {0}", City);

yield return String.Format("Region: {0}", Region);

yield return String.Format("PostalCode: {0}", PostalCode);

yield return String.Format("Country: {0}", Country);

yield return String.Format("Phone: {0}", Phone);

yield return String.Format("Fax: {0}", Fax);

yield return String.Format("HomePage: {0}", HomePage);

yield return String.Format("Type: {0}", Type);

}

//This allows a programmer to write

foreach (string s in PropertyAndValuesCollection()

{

Console.WriteLine(s);

}

protected decimal unitPrice;

public decimal UnitPrice{

get { return unitPrice; }

set {

if (value < 0.0m){

throw new ArgumentOutOfRangeException("UnitPrice",

"UnitPrice cannot be less than 0.");

}

unitPrice = value;

}

}

Page 4: NET Portfolio

Enumerators also come in handy when using LINQ, which I’ll talk about in Project 3.

Comparing Objects Like C++, C# allows for operator overloading to perform math and Boolean operations on objects. The

following code snippet shows the == operator overloaded:

Code Snippet 3: AppTypes\Supplier.cs Lines 144 – 154

It’s important to note that some other.NET languages (Visual Basic, in particular) do not support

operator overloading. Therefore, it’s always best to override the object.Equals() method as well. As you

can see from the code snippet, the actual comparison of Supplier objects is being done by the Equals

method (Which has been overridden, but not shown). It’s best to only implement the actual comparison

code once in the Equals method. Also note that when checking for null input references

object.ReferenceEquals() must be used so the operator function does not become recursive. The ==

method requires != to be overloaded as well (which isn’t shown in the snippet).

Delegates and Events Events can be used to alert other objects that something has occurred without necessarily knowing what

those objects are. They’re most common in programming user interfaces, although Visual Studio writes

them automatically. There are some situations where a programmer may want to use his or her own

event. First, a delegate must be declared:

Code Snippet 4: AppTypes\CollectionModifiedHandler.cs Line 8

An event was declared in the class that will broadcast the event, and the event was checked for a null

value before calling it. An event will be set to a null value before any other classes have registered to

receive it:

public delegate void CollectionModifiedHandler(object sender,

ModificationEventArgs args);

public static bool operator == (Supplier sup1, Supplier sup2){

if (object.ReferenceEquals(sup1, null) &&

object.ReferenceEquals(sup2, null))

{

return true;

}

if (object.ReferenceEquals(sup1, null) ||

object.ReferenceEquals(sup2, null))

{

return false;

}

return sup1.Equals(sup2);

}

Page 5: NET Portfolio

Code Snippet 5: AppTypes\Products.cs Lines 21 – 29

An object can register to receive the event like this:

Code Snippet 6: AppTypes\EventLogger.cs Lines 17 - 24

There are a lot of other small things I learned in this lab, like Attributes and Serialization. These are

rather straightforward, so I won’t go into them in detail.

Project 2

Overview The purpose of the second project was to gain a solid understanding of Windows Forms programming,

and become familiar with as many controls from the Visual Studio Toolbox as possible. For this project,

I wrote the Business and Presentation Layers for a plausible public lending library application. I was

provided with a database structure, a script to populate the database with test data, and a dll containing

code to actually interact with the database (The Data Access Layer). In the next project, I’ll be writing

my own code to replace the dll’s code to connect to the database. (SetFocus choose to teach Windows

Forms before ADO.NET)

User Interface The Library system was required to perform four functions: add an adult member, add a child member,

check out a book, and check in a book. Adult and Child members differ in the information that is

collected about each type. Because only these four simple functions were needed, I chose to put a

simple ListView on the main form, with the View property set to “LargeIcon.” The main form is shown

below:

public void Register(Products pros)

{

pros.CollectionModified += new

CollectionModifiedHandler(pros_CollectionModified);

}

public void pros_CollectionModified(object sender, ModificationEventArgs

args)

{

lastEventDetails = "Modification Attempted " +

args.ModificationEventStatus.ToString() + " " +

args.ModifiedObjectReference.ToString();

}

public event CollectionModifiedHandler CollectionModified;

private void OnCollectionModified(ModificationEventArgs args){

if (CollectionModified != null)

CollectionModified(this, args);

}

Page 6: NET Portfolio

Figure 1: Main Form for the Library Application

The following forms were created for the Add Adult and Add Child functions:

Figure 2: Add Adult and Add Child Forms

For this project, modal feedback was discouraged, that means that it was considered unacceptable to

alert the user of an error with a MessageBox that required them to click OK to continue. An error

provider control was added to all forms to provider user feedback. A method was registered to be

called for each textbox when that textbox’s Validating event occurred (when the user moves focus away

from it). Those methods validated the data in the textbox using a function in the Business Layer, and

used the error provider to give the user an explanation of the error. An error provider displays a red

icon near the fields in error, and provides an explanation in the tooltip that the user sees when hovering

over it, as shown below:

Figure 3: The Error Provider at work in the Add Adult Form

The project required checking out a book to be a three step process:

Page 7: NET Portfolio

1. The Librarian enters the patron’s card number, confirms the name and address, and may view

the books already checked out by the patron.

2. If the Patron’s card is not expired, and he or she has less than four books checked out, the

system allows the librarian to enter identifying info for the book (ISBN and copy number). The

librarian confirms the book’s title and author.

3. If the book is not checked out, the librarian may check it out to the patron. If the book is

marked as checked out (which the project requirements state happens often when a book is

returned and re-shelved without being checked in), the librarian has the opportunity to

automatically have it checked in and then checked out to the patron.

The following shows the book checkout form in each of the three stages of the process:

Page 8: NET Portfolio

Figure 4: Three Stages of the Checkout Process

Similarly, checking in a book was a two stage process: the librarian enters the books identifying info

(ISBN and copy number), the system confirms the title and author of the book, and shows who the book

is checked out to. The librarian can then choose to check the book in or cancel. This process is

illustrated below:

Figure 5: Check in process

Project 3

Overview The purpose of project 3 was gain a solid understanding of database interactivity by using LINQ to SQL.

In Project 2, I was given compiled dlls to define the Data Access and Entities tiers for the library project.

In this project, I wrote code to produce my own dlls to replace those. Although I was required to use

LINQ to SQL in this project, I was also taught the traditional ADO.NET way of sending actual SQL

statements to the database server using connection, command and data reader objects. In fact,

Page 9: NET Portfolio

SetFocus spent just as much class time teaching ADO.NET and Microsoft transact-SQL as they did with

LINQ. I understand that there are arguments for and against using LINQ. Many programmers feel LINQ

produces inefficient SQL queries that can impair performance. While SetFocus acknowledges this, they

have encouraged me and my classmates to use it because Microsoft claims great improvements will be

made to LINQ in Visual Studio 2010. Having prior experience writing SQL queries by hand, I’m willing to

take the time to use ADO.NET to manually submit queries to the database server. But I’m also eager to

see what changes are in store for LINQ in Visual Studio 2010.

The Database Structure I was given a database structure to use for this project. This same database structure was used in

Project 2. The following diagram illustrates the most important tables:

Figure 6: Part of database structure. (More tables were used, but these are the most relevant to the

following examples.)

Each work written by an author has an entry in the title table. Each ISBN has an entry in the item table.

(So a written work might be published under multiple ISBNs, one for each translation, or cover type.)

For each ISBN one or more copies may exist in the library. A copy represents a physical book that a

patron may check out. Each member in the library has one row in the member table, and one row in

either the juvenile or adult tables. Because a juvenile’s address information is required to be the same

Page 10: NET Portfolio

as the juvenile’s parent, the juvenile table contains a pointer to the juvenile’s parent so the data is not

duplicated. The following query was written to select all of the members and their information:

Code Snippet 7: Query to retrieve all members

Similarly, a query was written to select all of the items (books). These queries were put into views on

the database server, and the following entities diagram was created based on those views in Visual

Studio:

Figure 7: Entities Diagram

SELECT

member.member_no, firstname, middleinitial, lastname, street, city,

state, zip, phone_no, expr_date, NULL AS adult_member, NULL AS

birth_date, 'ADULT' AS Type

FROM

adult INNER JOIN member ON adult.member_no = member.member_no

UNION

SELECT

juvenile.member_no, firstname, middleinitial, lastname, street, city,

state, zip, phone_no, expr_date, adult.member_no AS adult_member,

birth_date, 'JUVENILE' AS Type

FROM

juvenile INNER JOIN member ON juvenile.member_no = member.member_no

INNER JOIN adult ON juvenile.adult_member_no = adult.member_no

Page 11: NET Portfolio

This diagram shows that Visual Studio will automatically create Item, Member, JuvenileMember, and

AdultMember classes and will automatically store data from the database in those classes when

performing a query on the DataContext. JuvenileMember and AdultMember inherit from Member, and

the properties have been set such that either a JuvenileMember or AdultMember object will be created

for each row depending on the value in the Type column. Four procedures were created on the

database server, AddAdult, AddJuvenile, CheckOutItem, and CheckInItem. Those procedures were

added to the Entities Diagram (Visual Studio does not show this graphically). A class was created in the

Data Access Layer to provide methods for the Business Layer to call to interact with the database. This

function is an example from that class that uses a LINQ query:

Code Snippet 8: LibraryDataAccess.cs Lines 121-142

This function could’ve been written using ADO.NET like this:

public Member GetMember(int isbn, short copyNumber)

{

Member result;

try

{

LibraryEntitiesDataContext ledc = new LibraryEntitiesDataContext();

var query = (from m in ledc.Members

join i in ledc.Items on m.MemberID equals i.MemberNumber

where i.ISBN == isbn && i.CopyNumber == copyNumber

select m).Single();

result = (Member)query;

}

catch (Exception ex)

{

throw new LibraryException(ErrorCode.NoSuchMember, ex.Message, ex);

}

return result;

}

Page 12: NET Portfolio

Code Snippet 9: Returning a Member using ADO.NET

Project 4

Overview The purpose of this project was to master ASP.NET and gain some familiarity with Microsoft IIS. In this

final stage of the library project, I created a web based presentation layer.

A Note on Cross-Browser Compatibility I feel the issue of cross-browser compatibility is of such importance that every web programmer should

be aware of it even if he or she is not responsible for the web design. A web site that functions properly,

but displays incorrectly can turn away users just easily as one that doesn’t function at all. Different web

browsers and even different versions of the same web browser may display a page differently. The

ASP.NET server controls render correctly in virtually every browser. A programmer who works mostly

with ASP.NET controls and rarely writes any HTML by hand may not even be aware of cross browser

display issues. Users have formed strong bonds with their favorite web browser, so asking them to use a

particular browser is usually unacceptable. Currently, statistics show that Internet Explorer and Firefox

are the most popular browsers representing nearly 90% of all internet users. Although as many

public Member GetMember(short memberNumber, string connectionString){

Member result;

SqlConnection conn = new SqlConnection(connectionString);

conn.Open();

SqlCommand command = conn.CreateCommand();

command.CommandText = "SELECT * FROM Member_View WHERE member_no = " +

memberNumber.ToString();

SqlDataReader reader = command.ExecuteReader();

if (reader.Read()){

if (reader.GetString(12) == "ADULT"){

result = new AdultMember((short)reader.GetValue(0), reader.GetString(1),

reader.GetString(2), reader.GetString(3), reader.GetString(4),

reader.GetString(5), reader.GetString(6), reader.GetString(7),

reader.GetString(8), (DateTime)reader.GetValue(9));

}

else{

result = new JuvenileMember((short)reader.GetValue(0),

reader.GetString(1), reader.GetString(2), reader.GetString(3),

reader.GetString(4), reader.GetString(5), reader.GetString(6),

reader.GetString(7), reader.GetString(8), (DateTime)reader.GetValue(9),

(short)reader.GetValue(10), (DateTime)reader.GetValue(11));

}

}

reader.Close();

conn.Close();

return result;

}

Page 13: NET Portfolio

browsers as possible should be tested, a page that displays correctly in these two browsers will typically

display correctly in most other browsers.

Setting Up the Master Page The following illustration shows the master page that I created for this site:

Figure 8: MainMaster.master

The following code snippet shows the relevant CSS for the master page:

Page 14: NET Portfolio

Code Snippet 10: CSS classes for the master page

As you can see, I applied a style to the body, setting the background color to green and the font to

whatever sans-serif font the browser chooses. I set the margin and padding to 0 to ensure that

elements can reach up to the edges of the page in all browsers. The header class is used to define the

black (actually very dark blue) stripe across the top of the page. The left column contains a TreeView

control with the data source set to a SiteMapDataSource, which loads the data from a sitemap file. This

allows the links in the site map to all be specified in one XML file. centerColumn defines the main

content area in the middle of the page below the dark stripe. I chose to set this to a fixed 950 pixels.

Generally this is the only thing I would set to a fixed pixel value. The left and right columns inside of the

center column are set to widths of 20% and 80% respectively (which is a percent of the center column,

not the entire page). The center column could be set to say 90% of the page instead of 950 pixels. But

this may cause problems. For example, the left column takes up 20% of the center area or about 190

.centerColumn

{

margin-left: auto;

margin-right: auto;

margin-top: 0px;

width: 950px;

}

.leftCenterColumn

{

background-color:

#FFFFFF;

width: auto;

margin-top: 10px;

margin-right: 10px;

border: 1px solid

#000000;

}

.leftCenterColumnContent

{

margin-top: 15px;

margin-bottom: 15px;

}

.rightCenterColumnContent

{

margin: 15px;

}

.rightCenterColumn

{

background-color:

#FFFFFF;

width: auto;

margin-top: 10px;

margin-left: 10px;

border: 1px solid

#000000;

}

body

{

font-family: Sans-Serif;

background-color:

#669900;

margin: 0px;

padding: 0px;

}

.header

{

margin: 0px;

margin-bottom: 10px;

padding: 0px;

width: 100%;

background-color:

#000005;

color: #FFFFFF;

}

.headerContent

{

margin-right: auto;

margin-left: auto;

width: 950px;

}

.patronsServed

{

font-style: italic;

margin-left: 25px;

}

.mainTitle

{

margin-left: 10%;

font-size: x-large;

}

Page 15: NET Portfolio

pixels. The text inside of the column fills it well. If the center column were set to a percent, its width

could easily approach 2,000 pixels on a high resolution wide screen monitor. This could cause the left

column to almost double in width, but the font would likely stay the same size. The font would appear

to take only a small part of the left column. Although this would utilize all of the available space on the

page, it would look awkward. Setting the width of the center column to a fixed width will cause wasted

space on a high resolution display, but it will ensure a uniform look for all users. Up until now, the most

common display resolution for internet users was 768 x 1024. Although display resolutions are

constantly getting higher, planning for this resolution seems to produce the best results.

Typical Page Layout The center of the master page contains a content place holder that will contain the main part of the

page. The following illustration shows the check out page:

Figure 9: Library Check out page

On this page, Label controls were used to display the library patron’s personal information. The Enter

and Select Book buttons cause a postback of the page, and the Visible property of the appropriate labels

as well as the entire Book Information fieldset is set to true or false as needed. Setting the Visible

property of an ASP.NET server control to false prevents it from being rendered. (This should not be

confused with setting the style property to “visibility: none;”, in which case, the HTML for the control is

Page 16: NET Portfolio

still rendered, but the browser will not display it.) The checked out books are displayed by a GridView

control which is bound to an ObjectDataSource control. While the ObjectDataSource control could’ve

been bound to the same ItemsDataSet that was used in the Windows Forms project, additional columns

were desired in this project. In order to not break the functionality in the Windows Forms project, a

new DataSet was created in the Entities Layer. Upon binding the dataset, each value in the Due Date

column is checked and the style property of that cell is changed if the book is overdue. A script manager

was added to the page and most of the page was put inside of an UpdatePanel control. The

UpdatePanel causes ASP.NET to automatically generate Javascript to submit AJAX request to the server

such that the entire page will not reload when a control that would cause a postback is triggered. This

provides a faster response for the users, and causes a small reduction in bandwidth use.

The Modal Dialog This project required that a Librarian be able to check out a book by entering the patron’s card number.

I felt it would also be convenient for a Librarian to look up the patron’s card number by entering his or

her name. A ModalDialog was created which appears when a user clicks the “Lookup” LinkButton and

requires the user to take action within the dialog before being able to interact with the rest of the page -

- just like a MessageBox. The following ModalDialog was created:

Figure 10: The ModalDialog on the check out page

Page 17: NET Portfolio

The Dialog box in the center of the page was initially created as a Panel control at the bottom of the

existing page. The functionality shown above was created by simply adding a ModalPopupExtender

control to the page from the AJAX control toolkit. The properties of the ModalPopupExtender were set

to indicate which Panel control to use, which CSS classes to use for the dialog, as well as making the

outside of the dialog opaque, and to indicate which buttons on the panel are to be used for accept and

cancel. The style property of the Panel was set to “display: none;” so that the Panel’s HTML would be

rendered, but not displayed by the browser until the ModalPopupExtender’s Javascript changed it.

Within the Panel, AJAX requests were used to update the search results in the ListBox. Every time a key

is pressed in the first or last name TextBoxes, the name is sent to the server, and the server returns a list

of name/card number pairs to display in the list box. This allows the user to find a member even if he or

she only partially knows how to spell the name. The following Javascript submits the name to the

server:

Code Snippet 11: JavaScript function to submit names

Note that the actual ID of the text boxes is retrieved from variables. It is not possible to predict the ID

that will be used in the HTML that ASP.NET renders for the server controls. Therefore, it is necessary for

the server to generate Javascript which stores the names of the needed controls in variables for use by

the browser at runtime. The following code is contained in the server-side page markup to produce this

code:

Code Snippet 12: Javascript to store the control IDs in variables

When the updateResults function calls the GetSearchResults function, the ScriptManager submits an

AJAX request to the server. In order to enable this functionality, the ScriptManager’s

EnablePageMethods property must be set to true, and the method being called must be public and

static, and contain the [WebMethod] attribute which requires the System.Web.Services namespace.

The following web method was created:

<script type="text/javascript" language="javascript">

var cardNumberTextBox = '<%=cardNumberTextBox.ClientID%>';

var searchFirstNameTextBox = '<%=searchFirstNameTextBox.ClientID%>';

var searchLastNameTextBox = '<%=searchLastNameTextBox.ClientID%>';

var enterButton = '<%=enterButton.ClientID%>';

</script>

function updateResults() {

var firstName = document.getElementById(searchFirstNameTextBox).value;

var lastName = document.getElementById(searchLastNameTextBox).value;

var name = firstName + "~" + lastName;

PageMethods.GetSearchResults(name, OnSucceeded, OnFailed);

}

Page 18: NET Portfolio

Code Snippet 13: Server-Side web method to return possible patrons

This method calls into the Business layer, which uses the Data Access layer to retrieve possible users.

The actual logic to choose users is contained in a new database procedure. Upon selecting a name and

clicking select, the corresponding card number will be inserted into the card number TextBox, the modal

dialog will disappear, and the Enter Button will be triggered.

Validation Although ASP.NET offers very versatile validation controls, like the RangeValidator or

RegularExpressionValidator, the best choice for this project was the CustomValidator because the actual

validation logic was already written in the Business layer earlier. The main drawback to using the

CustomValidator is that it cannot automatically generate Javascript for client-side validation—so the

validation logic would need to be rewritten in Javascript. However, client-side validation was not

needed for this project, so this was not done. The following illustration shows how the validation

controls will appear to the user:

[WebMethod]

public static string GetSearchResults(string name){

string firstName = name.Substring(0, name.IndexOf("~"));

string lastName = name.Substring(name.IndexOf("~") + 1);

SearchMemberDataSet sm = DataManipulator.FindMember(firstName,

lastName);

string output = "";

foreach (DataRow row in sm.Tables[0].Rows){

if (output == ""){

output += row[0].ToString() + "~" + row[1].ToString();

}

else{

output += "~" + row[0].ToString() + "~" + row[1].ToString();

}

}

return output;

}

Page 19: NET Portfolio

Figure 11: The validation controls

Each of the CustomValidators calls a validation function in the business layer to check the input. It sets

the IsValid property of the event arguments to true or false. If the input is invalid, it then sets the

validator’s error message to the value returned by the business layer.

Code Snippet 14: A validation event handler

protected void firstNameCustomValidator_ServerValidate(object source,

ServerValidateEventArgs args){

string error = MW.LibraryBusiness.Validate.ValidateName(args.Value);

if (error != ""){

error = "First Name " + error;

}

firstNameCustomValidator.ErrorMessage = error;

args.IsValid = (error == "");

}

Page 20: NET Portfolio

Project 5 The purpose of project 5 was to gain an understanding of the Windows Communication Foundation.

WCF allows for the creation of service projects. A service project can then be hosted in a Windows

Service or it can be hosted on a web server running IIS. Remember that in my Library project there is a

clear separation between presentation code and the logic / database interaction code. The goal of this

project was to expose the business layer code to an outside party by creating a service project that calls

into the business layer and emits the results through the service. The service was then hosted in IIS, and

the existing ASP.NET presentation layer was modified to retrieve data from the service hosted on IIS

rather than from the business directly. A service must have a defined ServiceContract that contains

DataContracts and OperationContracts to define how the service will work. These are defined by

creating an interface with abstract methods and applying the appropriate attributes as shown below:

Code Snippet 15: ServiceContract definition

Note that OperationContracts were defined for all methods in the Business Layer (not shown above).

Because a service runs in a process by itself, it cannot throw exceptions to the consuming process.

When an uncaught exception occurs in a service, a fault message is generated and sent to the

consuming process. Every method that throws a fault exception must have a FaultContract attribute as

shown above. The following class defined a custom FaultContract that was used to preserve the error

code from any LibraryExceptions that were thrown:

Code Snippet 16: The custom LibraryFault

The following class was defined to implement the ILibraryService interface:

[DataContract]

public class LibraryFault{

[DataMember]

public string ErrorCode;

[DataMember]

public string Message;

[DataMember]

public string OtherID;

}

[ServiceContract]

public interface ILibraryService

{

[FaultContract(typeof(LibraryFault))]

[OperationContract]

Member GetMember(short id);

Page 21: NET Portfolio

Code Snippet 17: LibraryService implementation

As you can see, the implementation of the GetMember method calls into the business layer, catches any

exceptions that are thrown, and re-throws them as FaultExceptions. The service was hosted in IIS, and a

service reference was added to the ASP.NET website so that it can call into it by creating a

LibraryServiceClient object. The following code from the MainMaster.master.cs file shows a label being

populated with the total number of users in the Library:

Snippet 18: Using the LibraryServiceClient

LibraryServiceClient proxy = new LibraryServiceClient();

patronsServed.Text = string.Format("{0:0,0} " + patronsServed.Text,

proxy.GetTotalMembers());

proxy.Close();

[ServiceBehavior(IncludeExceptionDetailInFaults = true)]

public class LibraryService : ILibraryService{

[PrincipalPermission(SecurityAction.Demand, Role = "LibraryPartner")]

public MW.LibraryEntities.Member GetMember(short id){

MW.LibraryEntities.Member result = null;

try{

result = DataManipulator.GetMember(id);

}

catch (LibraryException le){

var fault = new LibraryFault();

fault.ErrorCode = le.LibraryErrorCode.ToString();

fault.Message = le.Message;

fault.OtherID = le.OtherMemberID.ToString();

throw new FaultException<LibraryFault>(fault);

}

catch (Exception e){

var fault = new LibraryFault();

fault.ErrorCode = ErrorCode.GenericException.ToString();

fault.Message = e.Message;

fault.OtherID = "-1";

throw new FaultException<LibraryFault>(fault);

}

return result;

}