Fundamentals of N Tier

169
Fundamentals of N-Tier design » architecture » security » training » best practices :: pdsa.com

Transcript of Fundamentals of N Tier

Page 1: Fundamentals of N Tier

Fundamentals of N-Tier

design » architecture » security » training » best practices :: pdsa.com

Page 2: Fundamentals of N Tier
Page 3: Fundamentals of N Tier

Fundamentals of N-Tier By: Paul D. Sheriff With Examples in VB.NET and C# 4th Edition – October 2009 Published By: PDSA, Inc. ISBN: 978-0-9793748-1-4 Copyright © 2006-2009, PDSA, Inc. All Rights Reserved Worldwide

Page 4: Fundamentals of N Tier

Introduction

Written By: Paul D. Sheriff Technical Editors: Paul D. Sheriff Craig Shoemaker Every effort has been made to supply complete and accurate information. However, PDSA, Inc. assumes no responsibility for its use, nor for any infringement of the intellectual property rights of third parties which would result from such use. Copyright © 2006-2009 PDSA, Inc. All rights reserved worldwide. No part of this publication may be stored in a retrieval system, transmitted, or reproduced in any way, including but not limited to photocopy, photograph, magnetic or other record, without the prior agreement and written permission of the publisher. Printed in the United States of America

2 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 5: Fundamentals of N Tier

Table of Contents

Table of Contents Table of Contents .................................................................................................. 3 

Introduction ........................................................................................................... 7 Goals of this Book ..................................................................................... 7 Benefits of Reading this Book ................................................................... 7 Prerequisites ............................................................................................. 8 Assumptions .............................................................................................. 8 

Technical Support ................................................................................................. 8 

Installation ............................................................................................................. 9 

About Paul D. Sheriff ............................................................................................. 9 

Chapter 1 ....................................................................................................................... 1-1 

Overview of N-Tier Architecture ..................................................................................... 1-1 

What is N-Tier? .................................................................................................. 1-1 Services .................................................................................................. 1-2 

A Tiered Approach to Development ................................................................... 1-5 

Implementation of N-Tier .................................................................................... 1-5 Why N-Tier is a good choice .................................................................. 1-5 Why use Data Access Classes .............................................................. 1-6 Advantages of Separating Services ....................................................... 1-7 Disadvantages of Separating Services .................................................. 1-7 

Reusable Components ....................................................................................... 1-8 

How to Achieve N-Tier ....................................................................................... 1-9 Map a Database Object to a Class ....................................................... 1-10 The Data Layer Component ................................................................. 1-11 The Data Classes ................................................................................. 1-11 The Business Rules Component .......................................................... 1-12 Goals of Data Classes .......................................................................... 1-13 Goals of Business Classes ................................................................... 1-13 Benefits of Data Classes ...................................................................... 1-13 

Summary .......................................................................................................... 1-15 

Chapter 2 ....................................................................................................................... 2-1 

Creating N-Tier Services ................................................................................................ 2-1 

Sample 1 –Two-Tier Sample .............................................................................. 2-2 List Box Click Event and FormShow() .................................................... 2-3 

Fundamentals of N-Tier 3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 6: Fundamentals of N Tier

Introduction

Sample 2 – Refactored Form Methods .............................................................. 2-5 List Box Click Event and FormShow() .................................................... 2-7 

Sample 3 – Product Data Class ......................................................................... 2-9 List Box Click Event and FormShow() .................................................. 2-10 The Products Class (Version 1) ........................................................... 2-11 

Sample 4 – AppConfig Class ........................................................................... 2-13 App.Config File ..................................................................................... 2-13 Products Class (Version 2) ................................................................... 2-14 

Sample 5 – DataLayer Class ........................................................................... 2-16 Products Class (Version 3) ................................................................... 2-18 

Creating Reuseable Components .................................................................... 2-19 ConfigCommon Assembly .................................................................... 2-22 DataCommon Assembly ....................................................................... 2-22 NTierData Assembly ............................................................................ 2-23 NTierSample1a Project ........................................................................ 2-23 

Summary .......................................................................................................... 2-24 

Chapter 3 ....................................................................................................................... 3-1 

The DataLayer Component ............................................................................................ 3-1 

Methods of the DataLayer Class ........................................................................ 3-2 Overview ................................................................................................ 3-2 

Using the ADO.NET Interfaces .......................................................................... 3-4 

DataProvider Class ............................................................................................ 3-6 CreateConnection Method ..................................................................... 3-6 CreateCommand Method ....................................................................... 3-7 CreateParameter Method ....................................................................... 3-7 CreateDataAdapter Method ................................................................... 3-8 

DataLayer.CreateConnection Method ................................................................ 3-8 

DataLayer.CreateCommand Method ................................................................. 3-9 CreateCommand(SQL) .......................................................................... 3-9 CreateCommand(SQL, ConnectString, OpenConnection) ................... 3-10 CreateCommand(SQL, ConnectString) ................................................ 3-11 

DataLayer.CreateParameter Method ............................................................... 3-12 CreateParameter(ParameterName) ..................................................... 3-12 CreateParameter(ParameterName, DataType) ................................... 3-13 CreateParameter(ParameterName, DataType, Value) ........................ 3-14 

DataLayer.CreateDataAdapter Method ............................................................ 3-15 

GetDataSet Method ......................................................................................... 3-16 Usage ................................................................................................... 3-17 

GetDataTable Method ...................................................................................... 3-18 Usage ................................................................................................... 3-19 

4 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 7: Fundamentals of N Tier

Table of Contents GetDataReader ................................................................................................ 3-20 

Usage ................................................................................................... 3-22 

ExecuteScalar .................................................................................................. 3-24 Usage ................................................................................................... 3-26 

ExecuteSQL Method ........................................................................................ 3-27 ExecuteSQL(cmd, DisposeOfCommand) ............................................. 3-28 Usage ................................................................................................... 3-30 ExecuteSQL(cmd) ................................................................................ 3-32 Usage ................................................................................................... 3-33 Usage with Parameters ........................................................................ 3-35 ExecuteSQL(SQL, ConnectString) ....................................................... 3-38 Usage ................................................................................................... 3-39 

Summary .......................................................................................................... 3-41 

Chapter 4 ....................................................................................................................... 4-1 

Data Classes .................................................................................................................. 4-1 

Standard Properties of the Data Classes ........................................................... 4-2 

Methods of the Data Classes ............................................................................. 4-3 Examples of Usage ................................................................................ 4-4 

Match Columns to Properties ............................................................................. 4-6 Connection String Property .................................................................... 4-9 

Schema Structure .............................................................................................. 4-9 Usage of the Schema Structure ........................................................... 4-10 

Data Retrieval Methods .................................................................................... 4-11 Returning All Rows ............................................................................... 4-11 Returning One Row by Primary Key .................................................... 4-12 Load Method ........................................................................................ 4-13 

Data Modification Methods ............................................................................... 4-15 Insert Method ....................................................................................... 4-15 FillInParameters Method ...................................................................... 4-16 Update Method ..................................................................................... 4-17 Delete Method ...................................................................................... 4-18 

Validate Method ............................................................................................... 4-19 

BusinessRuleException Class ......................................................................... 4-22 

Multiple Tables ................................................................................................. 4-24 Multi-Table Joins .................................................................................. 4-24 Multi-Table Updates ............................................................................. 4-24 

Summary .......................................................................................................... 4-26 

Chapter 5 ....................................................................................................................... 5-1 

Business Classes ........................................................................................................... 5-1 

Fundamentals of N-Tier 5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 8: Fundamentals of N Tier

Introduction

Creating a Business Class ................................................................................. 5-1 

Validating Data ................................................................................................... 5-2 

Summary ............................................................................................................ 5-6 

Chapter 6 ....................................................................................................................... 6-1 

Alternate N-Tier Implementation .................................................................................... 6-1 

ProductsState Class ........................................................................................... 6-1 

ProductsDC Class .............................................................................................. 6-5 

Products Class ................................................................................................... 6-7 

Summary .......................................................................................................... 6-10 

Chapter 7 ....................................................................................................................... 7-1 

N-Tier and Web Services ............................................................................................... 7-1 

Build the Web Service ........................................................................................ 7-1 

wsProducts Web Service ................................................................................... 7-4 

Consuming the Web Service .............................................................................. 7-8 Loading the Products List Box ................................................................ 7-8 Showing the Form Data .......................................................................... 7-9 Inserting Data ....................................................................................... 7-11 

Summary .......................................................................................................... 7-14 

Chapter 8 ....................................................................................................................... 8-1 

N-Tier and Transactions ................................................................................................ 8-1 

Create a "DC" Base Class ................................................................................. 8-1 Create a Transaction Enumeration ........................................................ 8-2 

Create a Transaction Class ................................................................................ 8-5 

Modify the ProductsDC Class ............................................................................ 8-8 Insert Method Changes .......................................................................... 8-9 Update Method Changes ..................................................................... 8-10 Delete Method Changes ....................................................................... 8-11 The UI Layer ......................................................................................... 8-12 

Test the Transaction ........................................................................................ 8-13 ProductTransSample Class .................................................................. 8-15 

Summary .......................................................................................................... 8-16 

6 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 9: Fundamentals of N Tier

Introduction

Introduction This book is designed for anyone who wants to learn how to create N-Tier applications using the .NET Framework. N-Tier applications should be used for all business applications. With the advent of .NET, creating N-Tier applications is much easier since the amount of code you need to write in each data class is greatly reduced. The purpose of this book is to show readers how to create N-Tier applications to build real-world business applications. This book emphasizes good programming standards and practices.

Goals of this Book By the end of reading this book you will have learned several very useful techniques that will allow you to build upon the base set of classes in this book in your own N-Tier applications. In this book you will learn the following:

• Why you want to use N-Tier development techniques

• How to create a class that wraps up ADO.NET using a provider pattern

• How to create data access classes

• How to build business rule classes

• How to consume business rule classes from a front end application

• How to create a Web Service and Smart Client Application

Benefits of Reading this Book There are two major benefits you will derive from reading this book. First, you will learn to create N-Tier business applications. Secondly you will learn many good Object Oriented Programming (OOP) practices. It is impossible to create N-Tier applications without learning about inheritance, polymorphism, overloading, and overriding methods. These are all valid and great OOP techniques. This book will save you hours of time because it already includes the following:

• Data Layer Class

• Sample Data Access Classes

• Sample Business Rule Classes

• Sample Web Service

Fundamentals of N-Tier 7 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 10: Fundamentals of N Tier

Introduction

• Complete Sample Applications If you started from scratch it would probably take you over 40 hours of time to develop all the classes that are included in this book!

Prerequisites This book is designed for programmers who are already experienced with VB.NET, C#, ADO.NET, relational databases, and basic Object Oriented Programming. You should have already created at least one Windows Form application in .NET. You should also be familiar with relational database concepts. You must be familiar with Windows 2000 or later, and have access to Windows 2000 or Windows XP to effectively use this book.

Assumptions You will need several tools at your disposal so you can try out the many exercises contained in this book. Below is a list of the tools that you should have on your computer:

• Microsoft SQL Server 2000 or later

• Windows 2000 Professional or higher, Windows 2003, or Windows XP.

• Microsoft .NET 2.0 CLR Framework SDK

• Microsoft Visual Studio .NET 2005

Technical Support Because of the complexity of .NET software development, PDSA, Inc. does not provide free technical support related to the use of the material in this book. Technical support is available for problems with installation of the samples that are a part of this book but only through email, please do not call.

Tip: It is strongly recommended that you read this book thoroughly before emailing us for technical support.

8 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 11: Fundamentals of N Tier

Installation

Installation This book comes with an MSI file that you download from the PDSA, Inc. web site. This MSI file will install all of the samples for this book. There is no need to create any virtual directories for the samples, because they use the file system web approach. URL: www.pdsa.com/Downloads Just follow the instructions on the screen to download the samples.

About Paul D. Sheriff Paul is the CEO and President of PDSA, Inc. a Microsoft Partner in Southern California. PDSA, Inc. has been providing its customers with high quality custom software, training and architecture services since 1991. Paul has been using .NET since Beta 1 and has written over 30 articles, 10 days of courseware and several eBooks and regular books on .NET. Paul has spoken at various conferences on .NET including the launch of VS.NET at VSLive. Paul is the Microsoft Regional Director for Southern California and helps run the local Developer Days event for Microsoft as well as speaking at various local Microsoft events. Paul has over twenty years experience architecting information systems and his expertise is in much demand from companies such as Microsoft, TransAmerica, Rockwell, Boeing and many other companies. Lately Paul has been helping these companies architect .NET applications and help layout application frameworks such as the one presented in the eBook Architecting ASP.NET Applications. This eBook may also be purchased at www.pdsa.com/ebooks. Over the years Paul has written over 145 articles for many different publications, and is a contributing editor to Advisor magazine. Paul is the author of the QUE book Paul Sheriff teaches Visual Basic 6.0 and has co-authored the Addison-Wesley book ASP.NET Developer's Jumpstart with Ken Getz. Paul also speaks at the Advisor Publications Developer’s Conferences, Microsoft Tech-Ed, VSLive, MSDN Events and Microsoft Developer Days. PDSA, Inc. is available for consulting work and on-site training in Microsoft .NET, Visual Basic, SQL Server and Internet/Intranet applications. Contact PDSA, Inc. toll-free at (888) 899-PDSA (7372), or (714) 734-9792. Fax (714) 734-9793. E-mail: [email protected]. VISIT THE PDSA WEB SITE at: http://www.pdsa.com.

Fundamentals of N-Tier 9 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 12: Fundamentals of N Tier

Introduction

Also visit Paul's web site called Paul Sheriff's Inner Circle. This site is full of tips and tricks that you can't find anywhere else on the web. http://www.PaulSheriffInnerCircle.com.

10 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 13: Fundamentals of N Tier

About Paul D. Sheriff

Fundamentals of N-Tier 11 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 14: Fundamentals of N Tier

Introduction

12 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

THIS PAGE INTENTIONALLY LEFT BLANK

Page 15: Fundamentals of N Tier

Chapter 1

Overview of N-Tier Architecture

In my travels I meet a lot of programmers who would like to implement an N-Tier architecture, but either don’t know how, or say their program does not need an N-Tier architecture. I have found that most business applications should be written as a logical N-Tier application. You will find as you read this book that implementing an N-Tier architecture is very simple. Instead of worrying about a physical implementation of N-Tier (which is what everyone normally thinks has to be done), you would be better to start with a logical separation of services. You will learn a logical implementation of N-Tier in this book. In this chapter you will learn an overview of N-Tier concepts. You will also learn about the different components that you will use in this book. Even though the sample application presented in this book is a logical implementation of N-Tier, the same concepts apply to a physical separation of N-Tier as well.

What is N-Tier? You have most likely heard the term "3-tier architecture". This type of programming has been talked about for quite some time. If you talk with other programmers, most of them do not know how to implement it, or wonder why they should. So first let’s define what it is, then talk about why you want to implement it, and finally show an example of an N-Tier application.

Page 16: Fundamentals of N Tier

Overview of N-Tier Architecture

Services When programmers talk about 3-tier, N-Tier or multi-tier architecture they are talking about breaking up a program into “services”. A service is a component (a class or set of classes working together) that performs a piece of functionality for an application. Each service can reside on one machine, or can be spread across multiple machines. In the most traditional sense, each machine is considered a "tier", however, I like to think of each “service” as a tier. Most programmers realize that it may not always be feasible to move these “services” to other machines, but that it is a great idea to separate each “service” into a logical component. This then becomes a logical separation of services. Try not to focus on a specific number of tiers or services, just break components into logical services that fit your application, and let that be the deciding factor on how many “tiers” you end up with. The typical 3-tier approach dictates that you have a user interface, or display, service, a business rule service, and a database service. I often find this too limiting, as there are typically many services that work together at the same tier, as well as from one tier to another.

Figure 1.1. An N-Tier architecture has many services that work together.

Below is a table of some of the different services you might find yourself building.

1-2 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 17: Fundamentals of N Tier

What is N-Tier?

Service Description

User Interface This service is responsible for creating the user interface. For example, Windows Forms or ASP.NET is the front-end service. This service will typically interact with many of the other services listed in this table.

Exception Management

This service will handle the publication of exceptions. This service (not described in this book) will take any error and write it to a database, a file, or maybe send an email in order to log the errors that occur in a program.

User Tracking This service, when called, will track users as they move throughout your application. It will typically record the user's name, date and time, and the screen(s) they entered. This service is not described in this book.

Configuration Management

This service is responsible for reading configuration information from either the Web.Config file, the registry, XML files or whereever you choose to store configuration information.

Security Management

This service will take care of authenticating and authorizing users. You may need to use this service to allow only certain users into your application. You may need to use this service to turn off certain menu items or fields for certain users. This service is not described in this book.

Business Classes This service will assist you in enforcing data business rules. A good example of this would be that a "start date" must be less than an "end date". These business rules are in the form of code that reside in a method in a class. The front-end service will normally interact with these classes to select, insert, update, delete and validate data. The business rule classes do not select, insert, update or delete, but they inherit from the data access classes in the data access service that do. Business rule classes may also model business processes. If you need to update three tables as part of a business process, you will most likely want to wrap up three other business rule classes into a higher level business rule class.

Data Access Service

The Data Access Service is where you define your set of classes that interact with each object in your database. In this model if you have 10 tables in your database, you will create one class for each table. This allows you to define all data selections and data modifications for one table within one class. This one class can then be used anywhere in your application where you need to select from or update that table. Other objects you will model into classes include stored procedures and views.

Data Layer The Data Layer Service is where you place all your re-useable methods to interact with the database itself. This is where you will wrap up all the calls to ADO.NET.

Database Service This is the database you choose to use such as SQL Server, Oracle, Sybase, Informix or Access.

Fundamentals of N-Tier 1-3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 18: Fundamentals of N Tier

Overview of N-Tier Architecture

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Service Description

Log Management Most likely will be a set of classes that can be called from the Exception Management service and the User Tracking service. This service is responsible for writing to a particular log store.

There are many different types of services that you will most likely need to create for your particular application. The key is to think object-oriented and to always build classes that perform services for other classes. The discussion and creation of all of these services is beyond the scope of this book. In this book you will learn about the Business Classes, Data Access Service and the Data Layer Service.

A Tiered Approach to Development Most programmers are already using a tiered approach but they just don't realize it. Client/Server is comprised of two services (or tiers); a client and a server, and many of you have been programming these types of applications for years. The term Client/Server is short for two-tier Client/Server. When people think of Client/Server, they typically think of a database application, though there are other examples of Client/Server such as a Web Browser and a Web Server. The client tier is the application that the user interacts with. The server tier is the database server that maintains the physical storage and the integrity of the data. The client tier never access the physical data storage directly, but instead communicates with the server to manipulate the data it needs when it needs it. N-Tier means that you are adding layers in between the user interaction tier and the database tier.

Implementation of N-Tier To create an N-Tier application you start by designing classes that perform services for you. You compile these classes into Class Library projects (DLLs). You can then use these DLLs from many client applications (Windows Form, ASP.NET, Web Services or other types of applications). You have many different options of how to connect to these components from your front-end application. First, you can install these components on the client computer with the front-end application. Second, you could install them on a remote computer running Enterprise Services (COM+). Third, you could install

1-4 Fundamentals of N-Tier

Page 19: Fundamentals of N Tier

Implementation of N-Tier

them on a remote computer and connect to them using .NET Remoting. Fourth, you could call them via Web Services.

Why N-Tier is a good choice Let’s look at the problems with traditional programming where you embed the logic and data access routines on the client side. First, you should not embed business rule or data access logic in the user interface layer. If you have several applications using the same database, you will have duplicated logic and business rules across the many applications. If you change one of the rules, you have to change it in all those applications. This can lead to distribution problems and a maintenance nightmare. Another issue that might encounter is if you need to change the data access method you use, you almost have to rewrite the whole application, plus distribute new drivers and DLLs to the user. Of course some people go completely the other way and try to put as much logic on the database server as possible. This leads to a lot of network traffic just to check business rules and forces the server to do a lot of work to check business rules as well. Then the server has to send a message back to the client application. All this takes time and chews up a lot of resources and makes a lot of round trips around the network. One more disadvantage is that the SQL language is not very robust and thus you may not be able to do very complicated logic in business rules in SQL. When you create an intermediate tier using a Class Library project (DLL) that will house the business rules, there is now only one place to update rules and data access methods. Also, if this tier resides on the client machine there is no network traffic to check rules. The user will receive immediate feedback. You also can use a robust language like Visual Basic.NET, C# or any other .NET language to write the logic. You may place these components on a centralized server for ease of updates and distribution. You do need to make sure that the data is protected on the database however so it is a good idea to keep enough rules on the server to protect the data in case someone comes in from some other source other than your component.

Why use Data Access Classes There are many reasons to use data access classes in your database programming. To summarize some of the above discussion:

• Eliminates SQL from the front-end user interface layer.

• All SQL for one object (table, stored procedure, view) is contained in one class.

Fundamentals of N-Tier 1-5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 20: Fundamentals of N Tier

Overview of N-Tier Architecture

• Hides database implementation from other tiers.

• Hides data access method from other tiers.

• Less changes to front end code when a schema changes in your database.

• You can get an IntelliSense listing of columns when each column is mapped to a property.

• Faster development due to generation of code and re-use of data classes.

• Easier to create unit tests for each tier.

• Less bugs when you generate code.

• Reusable components that can be used from any other applications.

• Can use code generators for many of the standard data logic. Code generation leads to more bullet proof code. This leads to less bugs and faster development.

Advantages of Separating Services There are many advantages of separating services into individual components. Let’s look at a few of these advantages:

• Classes in the service define a standard set of properties and methods that can be reused in multiple applications.

• Easier to perform unit testing on the individual classes in the service. Each set of services can be tested independent of one another. This leads to better quality code.

• Reduces the amount of code you need to write in the client application. By moving business rule logic to a separate service, it makes it easier to change the front-end from a Windows Form to a Web Form application.

• Centralized maintenance of business rules and database services. If you need to modify business rules or the database access method you use, then only the one service needs to be modified, you don’t need to change all of the code in your other services.

• Potential to distribute the workload across CPUs. If you choose, you may run some of these DLLs under .NET Enterprise Services (COM+) on a separate computer. This can help increase the number of users that can simultaneously access these services.

1-6 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 21: Fundamentals of N Tier

Reusable Components

Disadvantages of Separating Services Of course there is a downside to implementing components in an N-Tier structure.

• This is an overhead associated with making all of these property and method calls. Depending on the implementation this could cause a decrease in performance.

• There are more projects (components) for you to keep track of.

• If you create classes for each table in your database, this could potentially be a lot of classes. In addition if you make properties for each column in each table, this is a lot more code to write as well. Of course, code generators are available to help generate these properties & standard table access methods.

• If you separate the services onto different machines, this cross-process communication can take extra time to perform. Your network performance may suffer as well since traffic for these calls travel across the network.

You will find that the advantages of creating N-Tier applications far outweigh the disadvantages.

Reusable Components In most applications you will want to be able to reuse components from a previous project. Instead of copy and pasting code, or copy and pasting files from one project to another, you will want to simply reference DLLs where the service you need resides. For example, Figure 1.2, shows how two different types of applications can reuse the same Business Rules, Data Access Service, Data Layer and Database. Each project simply needs to make a reference to the DLL where the classes are located.

Fundamentals of N-Tier 1-7 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 22: Fundamentals of N Tier

Overview of N-Tier Architecture

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Figure 1.2. A typical N-Tier structure.

You can immediately see the advantages in this scenario. If you find a bug in the Business Rules component, you only need to fix that bug in the one DLL. Distribute that new DLL to the web server and to the location where the Windows Forms application is located, and the bug is now fixed in both places. No need to have to remember where the code is you copied from one application to another.

How to Achieve N-Tier To create an N-Tier application you will need to perform several steps. First, you need to remove all data access routines from all of your forms and/or web pages. Second, remove all of the business rule logic from the forms/web pages as well. Next you put all of the data access routines and business rules

1-8 Fundamentals of N-Tier

Page 23: Fundamentals of N Tier

How to Achieve N-Tier

into class modules. This includes the retrieval of data as well as the modification of data.

Map a Database Object to a Class The technique that PDSA, and many other companies, have been using very successfully for the last 7 years, is to perform all data access for one table, stored procedure or view in one class. This means that if you have 50 objects in your database, you will create 50 classes in one or more DLLs. Each individual class is responsible for adding, editing, deleting and retrieving data from one table.

Figure 1.3. Map one table to a class.

The reason we chose this design is to keep things as simple as possible. It is only possible to INSERT, UPDATE or DELETE from one table at a time, so it only makes sense to have one class that performs each of these operations for each table in your application. While it is possible to retrieve data from more than one table at a time (a JOIN), you will not update through a JOIN. In fact, for a JOIN, you will most likely want to build a class, or a method in a common class, that performs that JOIN, but does not perform any modification through that class. Notice the business process that uses both the Customer Class and the Sales Class. This is a good example of creating a class that aggregates other

Fundamentals of N-Tier 1-9 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 24: Fundamentals of N Tier

Overview of N-Tier Architecture

classes to get the functionality of each. The advantage you get is you do not repeat any of the data access routines of either class.

The Data Layer Component The Data Layer component is a separate component that takes care of all of the direct interaction with the database. This layer contains the code that uses ADO.NET to retrieve data and modify data within your database. In the Data Layer class it will be important to create a "provider" pattern to allow you to choose a different data provider such as SQL Server, Oracle, Sybase, DB2, etc. If you do this correctly your core functionality stays the same, and you only have to swap out one class for another to target a different database. You will learn how to do this in this book.

The Data Classes You will typically create one Data Access class for each table within your application. By creating a one-to-one relationship you can have a unique class that houses all of the logic for interacting with that one database object. By wrapping up the logic for a specific table it makes the reuse among different forms much easier as shown in Figure 1.4.

Figure 1.4. Example of a Data Class being reused across multiple forms.

The logical "Customer class" as shown in Figure 1.4 contains not only the business class but the data class as well. These two classes together contain the logic to retrieve data, insert, update, and delete data within the Customer table. Since each of the different forms (Form1 and Form2) both need to access the customer table, they each only need to use the Customer class. If each form needs to insert data into the Customer table, they are only using methods of the Customer class to modify the data. If any changes to the business rules or to the data access logic needs to change, it only changes in the one Customer class not within each of the forms.

1-10 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 25: Fundamentals of N Tier

How to Achieve N-Tier

The Business Rules Component Business Rules are the elements of logic, which control the validity of the data stored in a database for a particular application. Business rules should be placed into a component so they can be used by a number of client applications. In this way you make sure you have a central repository for your business rules. This helps keep the number of places you have to change when a business rule changes to a minimum. The business rule component acts as a go-between for the client and the database server. The business rule server defines and implements any number of interfaces for the client to interact with. Instead of communicating with the database server directly, the client would communicate with the business rules server, which in turn would communicate with the data layer component to access the data in the database. Does this mean that we no longer need to put any data integrity logic into our database server? Definitely not! The job of the database server is to maintain the integrity of our data no matter what. If a client goes around the business rules component and accesses the database server directly, we need to make sure that they cannot violate the integrity of our data. One of the easiest ways to do this is provide limited access to the database server outside of the business rules component. Utilizing the built in security features of most database servers, you can restrict any or all access to the underlying data except via the business rules component.

Advantages of the Business Rules Server There are several advantages to this approach. First, the business rules component can be written in a more robust programming language. This allows the developer the freedom to use all of the programming constructs and features of the language in implementing complicated business logic. Second, well-defined and documented interfaces can be developed for any number of client applications to communicate with. This can reduce the amount of programming time required to develop client applications that manipulate the data since a good portion of the application logic will be contained within the business rules component. Third, if the business rule logic ever changes, it can be changed in one place. If the interfaces to the business rules component are well defined, any changes should minimally affect the client applications if at all.

Disadvantages of the Business Rules Server In a word: overhead! The more tiers there are in the equation, the more overhead in getting to the data. This can dramatically affect client and server performance if not accounted for in the design of the business rules component. With a little careful planning, you can reduce the amount of overhead that the middle-tier introduces to the equation. In fact, if done

Fundamentals of N-Tier 1-11 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 26: Fundamentals of N Tier

Overview of N-Tier Architecture

correctly, this type of design can actually increase the scalability of an application.

Goals of Data Classes Each data class you design should strive to accomplish several goals:

• Model the base table.

• Add, edit, delete and retrieve data.

• Enforce business rules of the table.

• Expose properties for each column.

Goals of Business Classes Each business class you design should strive to accomplish several goals:

• Wrap up the access to the Data Classes.

• Enforce business rules of a business process.

• Expose properties and methods to a UI programmer that encapsulate the logic for business processes.

Benefits of Data Classes The benefits you will get out of meeting these goals will be:

• You can have one programmer designing the data/business classes, while another programmer designs the forms/web pages.

• The form/web designer does not need to understand how the data access methods work, or even where the data is coming from.

• You get a nice division of labor.

• Centralizes all the access to a base table. No duplicate code.

• These classes can be put into very reusable DLLs.

• These components can be used from any language/product.

1-12 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 27: Fundamentals of N Tier

How to Achieve N-Tier

Summary In this chapter you gained some insight into the why and how to design a simple N-Tier application. The simple approach to building N-Tier applications makes a lot of sense as it keeps the code easy to create and easy to maintain later. You will learn the details of how to implement these different services in this book.

Fundamentals of N-Tier 1-13 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 28: Fundamentals of N Tier

Overview of N-Tier Architecture

1-14 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 29: Fundamentals of N Tier

Chapter 2

Creating N-Tier Services

This chapter is a step-by-step walk-through of how to apply N-Tier techniques to a typical .NET Application. The technique presented in this chapter will be done using a Windows Form application, but the same principles will apply to any type of .NET application you create. In this chapter you can look at the sample solution: \NTierSample1\NTierSample1.sln. It is in this solution that you will find each of the samples that illustrate the concepts presented. The sample form looks like Figure 2.1.

Figure 2.1. A sample screen to display product information.

Page 30: Fundamentals of N Tier

Creating N-Tier Services

Sample 1 –Two-Tier Sample If you were to write typical two-tier code to create the screen shown in Figure 2.1, you would most likely write code like the following:

C# private void frmSample1_Load(object sender, EventArgs e) { ProductsLoad(); } private void ProductsLoad() { DataTable dt = new DataTable(); SqlDataAdapter da = new SqlDataAdapter(); string strSQL; string strConn; strSQL = "SELECT * FROM tblProducts"; strConn = "Server=Localhost;Database=NTier-eBook; Integrated Security=SSPI"; // Create a method to retrieve the connection string da = new SqlDataAdapter(strSQL, strConn); da.Fill(dt); lstProducts.ValueMember = "iProduct_id"; lstProducts.DisplayMember = "sProductName"; lstProducts.DataSource = dt; } VB.NET Private Sub frmSample1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ProductLoad() End Sub Private Sub ProductLoad() Dim dt As New DataTable Dim da As SqlClient.SqlDataAdapter Dim strSQL As String Dim strConn As String strSQL = "SELECT * FROM tblProducts" strConn = "Server=Localhost;Database=NTier-eBook;" _ & "Integrated Security=SSPI" da = New SqlClient.SqlDataAdapter(strSQL, strConn) da.Fill(dt) lstProducts.ValueMember = "iProduct_id" lstProducts.DisplayMember = "sProductName" lstProducts.DataSource = dt End Sub

The code above you will find in the frmSample1 form in the sample solution. In the Load event for the form you would call a method to fill a DataSet or a

2-2 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 31: Fundamentals of N Tier

Sample 1 –Two-Tier Sample

DataTable using a DataAdapter for your particular database engine. In the case of the above code, SQL Server is being used. Now of course, you would probably not hard code the connection string in the method, and you might use stored procedures, but the code otherwise is probably fairly representative of how to load a list box.

List Box Click Event and FormShow() Once the user clicks on the ListBox control, you would want to display the rest of the product information in the fields to the right of the list box control. The code you might write to do this might look like the following:

Fundamentals of N-Tier 2-3 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 32: Fundamentals of N Tier

Creating N-Tier Services

C# private void lstProducts_SelectedIndexChanged(object sender, EventArgs e) { FormShow(); } private void FormShow() { DataTable dt = new DataTable(); SqlDataAdapter da = new SqlDataAdapter(); DataRow dr; string strSQL; string strConn; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + lstProducts.SelectedValue.ToString(); strConn = "Server=Localhost;Database=NTier-eBook; Integrated Security=SSPI"; da = new SqlDataAdapter(strSQL, strConn); da.Fill(dt); dr = dt.Rows[0]; lblProductID.Text = dr["iProduct_ID"].ToString(); txtProductName.Text = dr["sProductName"].ToString(); dtpDateIntroduced.Value = Convert.ToDateTime(dr["dtIntroduced"]); txtCost.Text = dr["cCost"].ToString(); txtPrice.Text = dr["cPrice"].ToString(); chkDiscontinued.Checked = Convert.ToBoolean(dr["bDiscontinued"]); } VB.NET Private Sub lstProducts_SelectedIndexChanged( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles lstProducts.SelectedIndexChanged FormShow() End Sub Private Sub FormShow() Dim dt As New DataTable Dim dr As DataRow Dim da As SqlDataAdapter Dim strSQL As String Dim strConn As String strSQL = "SELECT * FROM tblProducts " strSQL &= " WHERE iProduct_id = " & _ lstProducts.SelectedValue.ToString() strConn = "Server=Localhost;Database=NTier-eBook;" _ & "Integrated Security=SSPI" da = New SqlDataAdapter(strSQL, strConn) da.Fill(dt) dr = dt.Rows(0)

2-4 Fundamentals of N-Tier

Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 33: Fundamentals of N Tier

Sample 2 – Refactored Form Methods

lblProductID.Text = dr("iProduct_ID").ToString() txtProductName.Text = dr("sProductName").ToString() dtpDateIntroduced.Value = _ Convert.ToDateTime(dr("dtIntroduced")) txtCost.Text = dr("cCost").ToString() txtPrice.Text = dr("cPrice").ToString() chkDiscontinued.Checked = _ Convert.ToBoolean(dr("bDiscontinued")) End Sub

In this code you once again use a DataAdapter to fill up a DataSet or DataTable with a single row. This single row of data was built by retrieving the primary key that was stored in the List Box Value property (in this case iProduct_id) and using it in a WHERE clause. Of course, you can see the problems with this code immediately. Below is the list of things that are wrong with this code.

• SqlClient should not be in the UI.

• SQL statements should not be in the UI.

• The connect string should be in the Config file (or registry, etc.).

• No exception handling is used.

• The same code is used for building a DataSet in both methods. Let's work on each one of these problems individually.

Sample 2 – Refactored Form Methods So with just a little refactoring of our two methods presented earlier, you could get some of the code more reusable. In frmSample2 you will now see code that looks like the following:

Fundamentals of N-Tier 2-5 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 34: Fundamentals of N Tier

Creating N-Tier Services

C# private void frmSample2_Load(object sender, EventArgs e) { ProductsLoad(); } private void ProductsLoad() { lstProducts.ValueMember = "iProduct_id"; lstProducts.DisplayMember = "sProductName"; lstProducts.DataSource = GetAllProducts(); } private DataTable GetAllProducts() { return GetProducts("SELECT * FROM tblProducts"); } private DataTable GetProducts(string SQL) { DataTable dt = new DataTable(); SqlDataAdapter da = new SqlDataAdapter(); da = new SqlDataAdapter(SQL, GetConnectString()); da.Fill(dt); return dt; } private string GetConnectString() { return "Server=Localhost;Database=NTier-eBook;" + "Integrated Security=SSPI"; } VB.NET Private Sub frmSample2_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ProductsLoad() End Sub Private Sub ProductsLoad() lstProducts.ValueMember = "iProduct_id" lstProducts.DisplayMember = "sProductName" lstProducts.DataSource = GetAllProducts() End Sub Private Function GetAllProducts() As DataTable Return GetProducts("SELECT * FROM tblProducts") End Function Private Function GetProducts(ByVal SQL As String) As DataTable Dim dt As New DataTable Dim da As SqlDataAdapter da = New SqlDataAdapter(SQL, GetConnectString()) da.Fill(dt) Return dt End Function

2-6 Fundamentals of N-Tier Private Function GetConnectString() As String

Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 35: Fundamentals of N Tier

Sample 2 – Refactored Form Methods

Return "Server=Localhost;Database=NTier-eBook;" _ & "Integrated Security=SSPI" End Function

In this version of the form, you have refactored out many of the common chunks of code into separate methods. The ProductsLoad method now makes a call to GetAllProducts. This method calls the GetProducts method passing in a SELECT statement for retrieving all the rows from the products table. The populating of the DataTable and the use of the SqlDataAdapter has now been encapsulated into a single method. This method will be able to be reused by the FormShow method as well. In addition, the connection string has been moved into a centralized method that will also make it more reusable. Don't worry, you will remove the connection string and the SQL code out of the UI layer, but one thing at a time.

List Box Click Event and FormShow() Now if you look at the FormShow method, you can see that the code has been greatly reduced from the previous version. You can now take advantage of the refactored code to retrieve a single product.

Fundamentals of N-Tier 2-7 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 36: Fundamentals of N Tier

Creating N-Tier Services

C# private void lstProducts_SelectedIndexChanged(object sender, EventArgs e) { FormShow(); } private void FormShow() { DataTable dt; DataRow dr; dt = GetAProduct(Convert.ToInt32(lstProducts.SelectedValue)); dr = dt.Rows[0]; lblProductID.Text = dr["iProduct_ID"].ToString(); txtProductName.Text = dr["sProductName"].ToString(); dtpDateIntroduced.Value = Convert.ToDateTime(dr["dtIntroduced"]); txtCost.Text = dr["cCost"].ToString(); txtPrice.Text = dr["cPrice"].ToString(); chkDiscontinued.Checked = Convert.ToBoolean(dr["bDiscontinued"]); } private DataTable GetAProduct(int ProductID) { string strSQL; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + ProductID.ToString(); return GetProducts(strSQL); } VB.NET Private Sub lstProducts_SelectedIndexChanged( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles lstProducts.SelectedIndexChanged FormShow() End Sub Private Sub FormShow() Dim dt As DataTable Dim dr As DataRow dt = GetAProduct(Convert.ToInt32(lstProducts.SelectedValue)) dr = dt.Rows(0) lblProductID.Text = dr("iProduct_ID").ToString() txtProductName.Text = dr("sProductName").ToString() dtpDateIntroduced.Value = _ Convert.ToDateTime(dr("dtIntroduced")) txtCost.Text = dr("cCost").ToString() txtPrice.Text = dr("cPrice").ToString() chkDiscontinued.Checked = _ Convert.ToBoolean(dr("bDiscontinued")) End Sub

2-8 Fundamentals of N-Tier Private Function GetAProduct(ByVal ProductID As Integer) _

Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 37: Fundamentals of N Tier

Sample 3 – Product Data Class

As DataTable Dim strSQL As String strSQL = "SELECT * FROM tblProducts" strSQL &= " WHERE iProduct_id = " & ProductID.ToString() Return GetProducts(strSQL) End Function

In this code you now call a method named GetAProduct to which you will pass in the ProductID (the primary key). This method builds a SELECT statement with a WHERE clause that uses the primary key to retrieve the single row of data that is needed to display the product information in the appropriate text boxes on the screen. This method then passes the SELECT statement to the GetProducts method that was refactored earlier. Already you can see that this version has definitely centralized code and made it better for reuse.

Sample 3 – Product Data Class Of course, there is a lot more to do. In frmSample3 you will now see that a "Products" class is being used. In this version, the ProductsLoad method has now been reduced to just three lines of code, and gone is the SQL and the connection string from the UI code. The class "ProductsVer1" is our "Products" class. You will be refining this class throughout this book. This is simply the first version.

Fundamentals of N-Tier 2-9 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 38: Fundamentals of N Tier

Creating N-Tier Services

C# ProductsVer1 mprod = new ProductsVer1(); private void frmSample3_Load(object sender, EventArgs e) { ProductsLoad(); } private void ProductsLoad() { lstProducts.ValueMember = "iProduct_id"; lstProducts.DisplayMember = "sProductName"; lstProducts.DataSource = mprod.GetProducts().Tables[0]; } VB.NET Private mprod As New ProductsVer1 Private Sub frmSample3_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ProductsLoad() End Sub Private Sub ProductsLoad() lstProducts.ValueMember = "iProduct_id" lstProducts.DisplayMember = "sProductName" lstProducts.DataSource = mprod.GetProducts().Tables(0) End Sub

List Box Click Event and FormShow() Likewise in the FormShow method, the method to retrieve a single product has been moved into the Products class as well.

2-10 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 39: Fundamentals of N Tier

Sample 3 – Product Data Class

C# private void lstProducts_SelectedIndexChanged(object sender, EventArgs e) { FormShow(); } private void FormShow() { DataTable dt; DataRow dr; dt = mprod.GetProduct( Convert.ToInt32(lstProducts.SelectedValue)).Tables[0]; dr = dt.Rows[0]; lblProductID.Text = dr["iProduct_ID"].ToString(); txtProductName.Text = dr["sProductName"].ToString(); dtpDateIntroduced.Value = Convert.ToDateTime(dr["dtIntroduced"]); txtCost.Text = dr["cCost"].ToString(); txtPrice.Text = dr["cPrice"].ToString(); chkDiscontinued.Checked = Convert.ToBoolean(dr["bDiscontinued"]); } VB.NET Private Sub lstProducts_SelectedIndexChanged( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles lstProducts.SelectedIndexChanged FormShow() End Sub Private Sub FormShow() Dim dt As DataTable Dim dr As DataRow dt = mprod.GetProduct( _ Convert.ToInt32(lstProducts.SelectedValue)).Tables(0) dr = dt.Rows(0) lblProductID.Text = dr("iProduct_ID").ToString() txtProductName.Text = dr("sProductName").ToString() dtpDateIntroduced.Value = _ Convert.ToDateTime(dr("dtIntroduced")) txtCost.Text = dr("cCost").ToString() txtPrice.Text = dr("cPrice").ToString() chkDiscontinued.Checked = _ Convert.ToBoolean(dr("bDiscontinued")) End Sub

The Products Class (Version 1) Open up the ProductsVer1 class in the solution and take a look at the code. Below is what you should see.

Fundamentals of N-Tier 2-11 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 40: Fundamentals of N Tier

Creating N-Tier Services

C# using System.Data.SqlClient; class ProductsVer1 { public DataSet GetProducts() { return GetProducts("SELECT * FROM tblProducts"); } public DataSet GetProducts(string SQL) { DataSet ds = new DataSet(); SqlDataAdapter da = new SqlDataAdapter(); da = new SqlDataAdapter(SQL, GetConnectString()); da.Fill(ds); return ds; } public DataSet GetProduct(int ProductID) { string strSQL; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + ProductID.ToString(); return GetProducts(strSQL); } protected string GetConnectString() { return "Server=Localhost;Database=NTier-eBook;" + "Integrated Security=SSPI"; } } VB.NET Imports System.Data.SqlClient Public Class ProductsVer1 Public Function GetProducts() As DataSet Return GetProducts("SELECT * FROM tblProducts") End Function Public Function GetProducts(ByVal SQL As String) As DataSet Dim ds As New DataSet Dim da As SqlDataAdapter da = New SqlDataAdapter(SQL, GetConnectString()) da.Fill(ds) Return ds End Function Public Function GetProduct(ByVal ProductID As Integer) _ As DataSet Dim strSQL As String strSQL = "SELECT * FROM tblProducts"

2-12 Fundamentals of N-Tier strSQL &= " WHERE iProduct_id = " & ProductID.ToString()

Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 41: Fundamentals of N Tier

Sample 4 – AppConfig Class

Return GetProducts(strSQL) End Function Protected Function GetConnectString() As String Return "Server=Localhost;Database=NTier-eBook;" _ & "Integrated Security=SSPI" End Function End Class

You see that the exact methods that you refactored out previously have all been moved over into this class and just been made public so they can be used by the UI layer. This is encapsulation and makes for re-useable and easier to read code in the UI layer. Yep, we still need to get that pesky connection string out of there, so let's do that now.

Sample 4 – AppConfig Class Open up the frmSample4 form and you will see that a class called AppConfig is now used to retrieve the connection string from the <connectionStrings> element in the configuration file App.Config.

App.Config File An App.Config file is used to store any configuration items you need for your application. One of the items you can add is a connection string. Using the <connectionStrings> element is a convenient location for connection string storage.

<configuration> <connectionStrings> <add name="NTier" connectionString="Server=Localhost; Database=NTier-eBook;Integrated Security=SSPI"/> </connectionStrings> </configuration>

In the AppConfig class you use the ConfigurationManager class to retrieve the appropriate connection string by its name attribute.

Fundamentals of N-Tier 2-13 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 42: Fundamentals of N Tier

Creating N-Tier Services

C# using System.Configuration; class AppConfig { public static string ConnectString { get { return ConfigurationManager. ConnectionStrings["NTier"].ConnectionString; } } } VB.NET Imports System.Configuration Public Class AppConfig Public Shared ReadOnly Property ConnectString() As String Get Return ConfigurationManager. _ ConnectionStrings("NTier").ConnectionString End Get End Property End Class

To use the ConfigurationManager class you need to add a reference to the System.Configuration.dll to your project.

Products Class (Version 2) The frmSample4 uses the ProductsVer2 class for its data retrieval. The only difference between ProductsVer2 and ProductsVer1 is the use of the AppConfig.ConnectString property to retrieve the connection string.

2-14 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 43: Fundamentals of N Tier

Sample 4 – AppConfig Class

C# class ProductsVer2 { public DataSet GetProducts() { return GetProducts("SELECT * FROM tblProducts"); } public DataSet GetProducts(string SQL) { DataSet ds = new DataSet(); SqlDataAdapter da = new SqlDataAdapter(); // Use AppConfig class to get Connect String da = new SqlDataAdapter(SQL, AppConfig.ConnectString); da.Fill(ds); return ds; } public DataSet GetProduct(int ProductID) { string strSQL; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + ProductID.ToString(); return GetProducts(strSQL); } } VB.NET Public Class ProductsVer2 Public Function GetProducts() As DataSet Return GetProducts("SELECT * FROM tblProducts") End Function Public Function GetProducts(ByVal SQL As String) As DataSet Dim ds As New DataSet Dim da As SqlDataAdapter ' Use AppConfig class to get Connect String da = New SqlDataAdapter(SQL, AppConfig.ConnectString) da.Fill(ds) Return ds End Function Public Function GetProduct(ByVal ProductID As Integer) _ As DataSet Dim strSQL As String strSQL = "SELECT * FROM tblProducts" strSQL &= " WHERE iProduct_id = " & ProductID.ToString() Return GetProducts(strSQL) End Function End Class

Fundamentals of N-Tier 2-15 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 44: Fundamentals of N Tier

Creating N-Tier Services

Sample 5 – DataLayer Class When looking at ProductsVer1 and ProductsVer2 you can see that the code in the GetProducts method is very generic and could be used to retrieve data in any table, not just the Products table. Anytime you see code that is this generic, it should be refactored out into another class to be reused. In frmSample5 this is exactly what is done. This form uses ProductsVer3, which in turn uses a new class called DataLayer. The DataLayer class (shown below) is a generic data retrieval class into which you can pass any SQL SELECT statement and a connection string, and this class can build either a DataSet or a DataTable from those two pieces of information. This class is highly reusable and very easy to use.

2-16 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 45: Fundamentals of N Tier

Sample 5 – DataLayer Class

C# using System.Data; using System.Data.SqlClient; class DataLayer { public static DataSet GetDataSet(string SQL, string ConnectString) { DataSet ds = new DataSet(); SqlDataAdapter da = new SqlDataAdapter(); da = new SqlDataAdapter(SQL, ConnectString); da.Fill(ds); return ds; } public static DataTable GetDataTable(string SQL, string ConnectString) { DataSet ds = new DataSet(); DataTable dt = null; ds = GetDataSet(SQL, ConnectString); if (ds.Tables.Count > 0) { dt = ds.Tables[0]; } return dt; } } VB.NET Imports System.Data.SqlClient Public Class DataLayer Public Shared Function GetDataSet(ByVal SQL As String, _ ByVal ConnectString As String) As DataSet Dim ds As New DataSet Dim da As SqlDataAdapter da = New SqlDataAdapter(SQL, ConnectString) da.Fill(ds) Return ds End Function Public Shared Function GetDataTable(ByVal SQL As String, _ ByVal ConnectString As String) As DataTable Dim ds As DataSet Dim dt As DataTable = Nothing ds = GetDataSet(SQL, ConnectString) If ds.Tables.Count > 0 Then dt = ds.Tables(0) End If

Fundamentals of N-Tier 2-17 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 46: Fundamentals of N Tier

Creating N-Tier Services

Return dt End Function End Class

Products Class (Version 3) If you now look at ProductsVer3 you will see that again the code has been greatly simplified. Gone are all objects references in the SqlClient namespace. This means that the Products class no longer cares whether its data lives in SQL Server, Oracle, Sybase or any other database system. Given the appropriate connection string, this class can retrieve products data from any of these as long as the DataLayer can talk to that database.

2-18 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 47: Fundamentals of N Tier

Creating Reuseable Components

C# class ProductsVer3 { public DataSet GetProducts() { return GetProducts("SELECT * FROM tblProducts"); } public DataSet GetProducts(string SQL) { DataSet ds; // Use the DataLayer to Build DataSet ds = DataLayer.GetDataSet(SQL, AppConfig.ConnectString); return ds; } public DataSet GetProduct(int ProductID) { string strSQL; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + ProductID.ToString(); return GetProducts(strSQL); } } VB.NET Public Class ProductsVer3 Public Function GetProducts() As DataSet Return GetProducts("SELECT * FROM tblProducts") End Function Public Function GetProducts(ByVal SQL As String) As DataSet Dim ds As DataSet ' Use the DataLayer to Build DataSet ds = DataLayer.GetDataSet(SQL, AppConfig.ConnectString) Return ds End Function Public Function GetProduct(ByVal ProductID As Integer) _ As DataSet Dim strSQL As String strSQL = "SELECT * FROM tblProducts" strSQL &= " WHERE iProduct_id = " & ProductID.ToString() Return GetProducts(strSQL) End Function End Class

Creating Reuseable Components Fundamentals of N-Tier 2-19 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 48: Fundamentals of N Tier

Creating N-Tier Services

Now that you have solved the basic problems of a two-tier structure by moving the connection string out of the code, getting the SQL out of the UI, encapsulating the SqlClient namespace into a separate class, and making code that is very easy to use, there is just one more step to making the code very reusable. This is accomplished by breaking the classes into separate class libraries (Components). By moving the components into class libraries, these libraries (DLLs) can be referenced and re-used from any type of application that needs these services. For example, you should create a class library for the AppConfig class. Retrieving a connection string is something that almost any business application can use. The DataLayer class should also be placed into its own DLL as it too can be used from just about any business application. The Products class is most likely pretty unique to a particular database, but it too could be reused in different applications within a particular company. For example, you may create a windows application used by internal people in the company to look up products, but also have a web application that customers can use to look up products. Both these applications can use the Products class. In the sample solution that comes with this book named NTierSample1a.sln you will find all of these classes broken out into the appropriate components. Figure 2.2 and Figure 2.3 are screen shots of the solution that you will find.

2-20 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 49: Fundamentals of N Tier

Creating Reuseable Components

Figure 2.2. The C# solution with all the classes in separate DLLs.

Fundamentals of N-Tier 2-21 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 50: Fundamentals of N Tier

Creating N-Tier Services

Figure 2.3. The VB.NET solution with all the classes in separate DLLs.

ConfigCommon Assembly This DLL contains the AppConfig class. It is used to retrieve the connection string from the <connectionStrings> element in the configuration file for the application. To use this version you must add a reference to the System.Configuration.dll to the project. However, with just a few changes, this class could be used to retrieve the connection string from the Registry, an XML file, or any other location. By wrapping up this method in a class, you can make this change and not have to change any of the code where this class is used from.

DataCommon Assembly The DataCommon DLL just contains the DataLayer class for now. Later you will add more to this class and to this project. Once again, having this class in a separate component makes this component able to be used from any application.

2-22 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 51: Fundamentals of N Tier

Creating Reuseable Components

NTierData Assembly This project contains the Products class. You must set a reference to the ConfigCommon and DataCommon projects in order to use this, as it takes advantage of the services of both DLLs.

NTierSample1a Project This solution file only needs to reference the NTierData project. The NTierSample1a UI project must contain the App.Config file with the <connectionStrings> element in it because that is where the Products class (and thus the AppConfig class) is expecting to find the connection string. You will also need to add the following statements at the top of any form that needs to use the Products class.

C# using NTierData VB.NET Imports NTierData

Fundamentals of N-Tier 2-23 Copyright © 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 52: Fundamentals of N Tier

Creating N-Tier Services

2-24 Fundamentals of N-Tier Copyright © 2006-2009 PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Summary In this chapter you learned how to take a typical two-tier type of application and break it up into a set of classes that will form the basis for an N-Tier application. You now know the basics of N-Tier design, but there is still some work to do. You need to add data modification capabilities to this products class. You also need to be able to validate business rules. You will learn this and more in the next chapters.

Page 53: Fundamentals of N Tier

Chapter 3

The DataLayer Component

In the previous chapter you learned how to break out the methods to retrieve a DataSet and a DataTable into a separate class called DataLayer. This DataLayer class is a good start, but there are a few more things you need to add to it to make it robust, flexible and be able to perform data modification. The main problem with the DataLayer class in the sample application is it is specific to SQL Server databases. It uses the SqlClient namespace and the SqlConnection, SqlCommand and SqlDataAdapter classes. This might be fine if you are using SqlServer, but if you are using Oracle, JET (Access), Sybase or some other database, this class will not work. So some adjustments will need to be made. The goal of this chapter is to…

• Add the ability to return a DataReader from the DataLayer class.

• Add data modification methods to the DataLayer class.

• Remove the SqlClient dependency from the DataLayer class.

• Use the ADO.NET Interfaces for data classes. To follow along and see how the DataLayer class was built, you should load the Solution File: NTierDataTesterCS\NTierDataTesterCS.sln or NTierDataTesterVB\NTierDataTesterVB.sln

Page 54: Fundamentals of N Tier

The DataLayer Component

NOTE: Please create a database on your local SQL Server called NTier-eBook. Run the two .SQL scripts in this new database. You will find these scripts in the SQLServerScripts folder where you installed the eBook.

Methods of the DataLayer Class Within the DataCommon component is the DataLayer class that handles all the interaction with a database. The methods in this class are all implemented as static/Shared members so it is not necessary to instantiate an instance of this class. There should be no need for multiple instances of the DataLayer class and thus static/Shared methods work great.

Overview Each of the static/Shared methods in the DataLayer class wrap up calls to ADO.NET. In some cases the methods return instances of ADO.NET classes such as a DataSet or a DataTable. In other cases, you will want to return a DataReader, or maybe even a connection or command object. The following table describes each of the static/Shared methods in the DataLayer class.

Method Name Description

Data Retrieval Methods

GetDataSet(SQL, ConnectString) Return a DataSet given a SQL string and a Connection String.

GetDataTable(SQL, ConnectString) Return a DataTable given a SQL string and a Connection String.

GetDataReader(SQL, ConnectString)

Return a DataReader given a SQL string and a Connection String.

ExecuteScalar(SQL, ConnectString) Returns an Object data type given a SQL string and a Connection String. The SQL string must use one of the appropriate database scalar functions such as Min(), Max(), Sum(), etc.

Data Modification Methods

3-2 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 55: Fundamentals of N Tier

Methods of the DataLayer Class

ExecuteSQL(cmd, DisposeOfCommand)

This method will perform the actual ExecuteNonQuery on a command object. The other ExecuteSQL methods are simply overrides of this one. The DisposeOfCommand parameter is a boolean value to let this method know whether or not to dispose of the command and connection object after the ExecuteNonQuery method has been performed.

ExecuteSQL(cmd) This overloaded method calls the first ExecuteSQL() method and passes in a "true" value to the DisposeOfCommand parameter.

ExecuteSQL(SQL, ConnectString) This overloaded method calls the first ExecuteSQL() method after creating a Command and Connection object and passing in a "true" value to the DisposeOfCommand parameter.

ADO.NET Object Creation Methods

CreateConnection(ConnectString) This method will create an ADO.NET Connection object. This method is called by all the Data Retrieval methods and the Data Modification methods.

CreateCommand(SQL) This method will create an ADO.NET Command object. This method is called by all the Data Retrieval methods and the Data Modification methods.

CreateCommand(SQL, ConnectString)

This method calls the CreateCommand(SQL) to create the command object. Then it calls the CreateConnection() method to create a connection and place the new connection into the Connection property of the newly created command object.

CreateParameter(ParameterName) This method creates a new parameter object with the given name.

CreateParameter(ParameterName, DataType)

This method creates a new parameter object with the given name. It also fills in the DataType property with the passed in data type.

CreateParameter(ParameterName, DataType, Value)

This method creates a new parameter object with the given name. It fills in the DataType property with the passed in data type. It also fills in the value with the passed in Value.

CreateDataAdapter(SQL, ConnectString)

This method creates a DataAdapter object from the given SQL string and Connection String.

Fundamentals of N-Tier 3-3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 56: Fundamentals of N Tier

The DataLayer Component

Using the ADO.NET Interfaces When you read most articles in magazines or on the web, you see explicit calls to ADO.NET data providers such as SqlClient or OracleClient. While this is the most efficient way to access the data in SQL Server and Oracle respectively, there is a problem when you use these providers. The problem is if you ever need to switch from SQL Server to Oracle, for example, you must replace all instances of SqlClient to OracleClient. In addition all the class names within each of these namespaces are different. Instead of SqlConnection it is OracleConnection. Thus there is a lot of renaming to do. Instead of using these classes directly, you can instead work with the Interfaces in .NET. Consider the following hard coded SQL Server code fragment:

C# SqlClient.SqlConnection cnn; cnn = new SqlClient.SqlConnection( "Server=Localhost;Database=Northwind;uid=sa;pwd="); cnn.Open(); VB.NET Dim cnn As SqlClient.SqlConnection cnn = New SqlClient.SqlConnection( _ "Server=Localhost;Database=Northwind;uid=sa;pwd=") cnn.Open()

To change this to Oracle, you would have to change it as shown below:

C# OracleClient.OracleConnection cnn; cnn = new OracleClient.OracleConnection( "Server=OracleTest;Database=Sample;User Id=sysdba"); cnn.Open(); VB.NET Dim cnn As n OracleClient.OracleConnectiocnn = New OracleClient.OracleConnection( _ "Server=OracleTest;Database=Sample;User Id=sysdba") cnn.Open()

While there are not too many changes, think about if you had to do this across your whole application? If you used the above objects 500 times, that would be a lot of changes to make. Even with refactoring you would still have to retest everything. Instead it would make better sense to create a class with a method that you can call that would return the appropriate provider. So the code above might be converted to look like the following:

3-4 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 57: Fundamentals of N Tier

Using the ADO.NET Interfaces

C# IdbConnection cnn; cnn = DataProvider.CreateConnection( "Server=OracleTest;Database=Sample;User Id=sysdba"); cnn.Open(); VB.NET Dim cn IDbConnectionn As cnn = DataProvider.CreateConnection( _ "Server=OracleTest;Database=Sample;User Id=sysdba") cnn.Open()

Now you can create a DataProvider class that has a CreateConnection method to return the appropriate provider. In fact, you can create many different DataProvider classes that all implement the same methods, one for each database you need to support. Since all vendors that produce a .NET native provider for their databases must implement the appropriate ADO.NET interfaces, you can be assured that your code will always work. Here is an example of a DataProvider class that returns a SqlConnection from the CreateConnection method.

C# using SqlClient; public class DataProvider { public static IDbConnection CreateConnection( string ConnectString) { SqlConnection cnn = new SqlConnection(ConnectString); return cnn; } } VB.NET Imports SqlClient Public Class DataProvider Public Shared Function CreateConnection( _ ByVal ConnectString Ss String) As IDbConnection Dim cnn As New SqlConnection(ConnectString) Return cnn End Function End Class

You can see how easy it would be to create different DataProvider classes for each provider you wish to support. You would simply create a new DataProvider class that returns OracleClient classes, or any other native provider ADO.NET class. You could also use a factory design pattern within

Fundamentals of N-Tier 3-5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 58: Fundamentals of N Tier

The DataLayer Component

this method to return the appropriate provider based on some value you pass in, or maybe by reading the connection string.

DataProvider Class In the samples that you will explore in this chapter you will see that there is a DataProvider class already implemented. Below are the methods that are a part of this DataProvider class.

CreateConnection Method This method returns a connection object from the appropriate provider. Once again notice the use of the interface, IDbConnection, that is being returned.

C# public static IDbConnection CreateConnection() { SqlConnection cnn = new SqlConnection(); return cnn; } VB.NET Public Shared Function CreateConnection() As IDbConnection Dim cnn As New SqlConnection Return cnn End Function

CreateCommand Method This method returns a command object from the appropriate provider.

3-6 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 59: Fundamentals of N Tier

DataProvider Class

C# public static IDbCommand CreateCommand() { SqlCommand cmd = new SqlCommand(); return cmd; } VB.NET Public Shared Function CreateCommand() As IDbCommand Dim cmd As New SqlCommand Return cmd End Function

CreateParameter Method This method returns a Parameter object from the appropriate provider.

C# public static IDataParameter CreateParameter() { SqlParameter param = new SqlParameter(); return param; } VB.NET Public Shared Function CreateParameter() As IDataParameter Dim param As New SqlParameter Return param End Function

CreateDataAdapter Method This method returns a DataAdapter object from the appropriate provider.

Fundamentals of N-Tier 3-7 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 60: Fundamentals of N Tier

The DataLayer Component

C# public static IDbDataAdapter CreateDataAdapter() { SqlDataAdapter da = new SqlDataAdapter(); return da; } VB.NET Public Shared Function CreateDataAdapter() As IDbDataAdapter Dim da As New SqlDataAdapter Return da End Function

DataLayer.CreateConnection Method Now that you have seen the DataProvider class, this class will be used from the DataLayer class. The DataLayer class will create methods with the same names, but are simply wrappers to the DataProvider class. Then each of the other methods such as GetDataSet, GetDataTable, and GetDataReader will call these methods to create the appropriate ADO.NET objects they need. The first method is called CreateConnection and returns a Connection object with the connection string filled in.

3-8 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 61: Fundamentals of N Tier

DataLayer.CreateCommand Method

C# public static IDbConnection CreateConnection( string ConnectString) { IDbConnection cnn; cnn = DataProvider.CreateConnection(); cnn.ConnectionString = ConnectString; return cnn; } VB.NET Public Shared Function CreateConnection( _ ByVal ConnectString As String) As IDbConnection Dim cnn As IDbConnection cnn = DataProvider.CreateConnection() cnn.ConnectionString = ConnectString Return cnn End Function

Remember, the idea with the DataLayer class is that no particular provider is ever referenced. Instead all the Interface classes are used.

DataLayer.CreateCommand Method The CreateCommand method has two overloads; one to which you will pass in just an SQL string, and one that passes both an SQL string and a connection string.

CreateCommand(SQL) The CreateCommand method returns a Command object. This overload only creates the command object and fills in the CommandText property. It does not assign a connection object.

Fundamentals of N-Tier 3-9 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 62: Fundamentals of N Tier

The DataLayer Component

C# public static IDbCommand CreateCommand(string SQL) { IDbCommand cmd; cmd = DataProvider.CreateCommand(); cmd.CommandText = SQL; return cmd; } VB.NET Public Shared Function CreateCommand(ByVal SQL As String) As IDbCommand Dim cmd As IDbCommand cmd = DataProvider.CreateCommand() cmd.CommandText = SQL Return cmd End Function

CreateCommand(SQL, ConnectString, OpenConnection) The CreateCommand method returns a Command object. This overload will create a Connection object, fill in the ConnectionString property and optionally open the connection based on the OpenConnection parameter that is passed in. The reason you might wish to delay the opening of the connection is maybe this command object is going to be passed into a DataAdapter. If that is the case, you want to let the DataAdapter do the opening and closing of the connection. This avoids you having to worry about the closing and disposing of the connection.

3-10 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 63: Fundamentals of N Tier

DataLayer.CreateCommand Method

C# public static IDbCommand CreateCommand(string SQL, string ConnectString, bool OpenConnection) { IDbCommand cmd; cmd = CreateCommand(SQL); cmd.Connection = CreateConnection(ConnectString); if(OpenConnection) { cmd.Connection.Open(); } return cmd; } VB.NET Public Shared Function CreateCommand(ByVal SQL As String, _ ByVal ConnectString As String, _ ByVal OpenConnection As Boolean) As IDbCommand Dim cmd As IDbCommand cmd = CreateCommand(SQL) cmd.Connection = CreateConnection(ConnectString) If OpenConnection Then cmd.Connection.Open() End If Return cmd End Function

CreateCommand(SQL, ConnectString) The CreateCommand method returns a Command object. This overload will create a Connection object, fill in the ConnectionString property and open the connection.

Fundamentals of N-Tier 3-11 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 64: Fundamentals of N Tier

The DataLayer Component

C# public static IDbCommand CreateCommand(string SQL, string ConnectString) { return CreateCommand(SQL, ConnectString, true); } VB.NET Public Shared Function CreateCommand(ByVal SQL As String, _ ByVal ConnectString As String) As IDbCommand Return CreateCommand(SQL, ConnectString, True) End Function

DataLayer.CreateParameter Method The CreateParameter method has a few overloads that can be used to build parameters for calls to stored procedures through Command objects.

CreateParameter(ParameterName) This CreateParameter overloaded method just takes in a Parameter name and creates the parameter and returns it to the caller.

3-12 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 65: Fundamentals of N Tier

DataLayer.CreateParameter Method

C# public static IDataParameter CreateParameter(string ParameterName) { IDataParameter param; param = DataProvider.CreateParameter(); param.ParameterName = ParameterName; return param; } VB.NET Public Shared Function CreateParameter(ByVal ParameterName As String) As IDataParameter Dim param As IDataParameter param = DataProvider.CreateParameter() param.ParameterName = ParameterName Return param End Function

CreateParameter(ParameterName, DataType) This overload of the CreateParameter method will assign the ParameterName property and the appropriate DbType for the parameter.

Fundamentals of N-Tier 3-13 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 66: Fundamentals of N Tier

The DataLayer Component

C# public static IDataParameter CreateParameter(string ParameterName, DbType DataType) { IDataParameter param; param = DataProvider.CreateParameter(); param.DbType = DataType; param.ParameterName = ParameterName; return param; } VB.NET Public Shared Function CreateParameter(ByVal ParameterName As String, ByVal DataType As DbType) As IDataParameter Dim param As IDataParameter param = DataProvider.CreateParameter() param.DbType = DataType param.ParameterName = ParameterName Return param End Function

CreateParameter(ParameterName, DataType, Value) This CreateParameter overload will set the ParameterName, the DbType and Value properties based on the values passed into the method.

3-14 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 67: Fundamentals of N Tier

DataLayer.CreateDataAdapter Method

C# public static IDataParameter CreateParameter(string ParameterName, DbType DataType, Object Value) { IDataParameter param; param = DataProvider.CreateParameter(); param.DbType = DataType; param.Value = Value; param.ParameterName = ParameterName; return param; } VB.NET Public Shared Function CreateParameter(ByVal ParameterName As String, ByVal DataType As DbType, ByVal Value As Object) As IDataParameter Dim param As IDataParameter param = DataProvider.CreateParameter() param.DbType = DataType param.Value = Value param.ParameterName = ParameterName Return param End Function

DataLayer.CreateDataAdapter Method The CreateDataAdapter method creates a new instance of a DataAdapter for the appropriate provider, sets the SelectCommand property with a Command object already filled up with a SQL string and ConnectionString.

Fundamentals of N-Tier 3-15 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 68: Fundamentals of N Tier

The DataLayer Component

C# public static IDbDataAdapter CreateDataAdapter(string SQL, string ConnectString) { IDbDataAdapter da; da = DataProvider.CreateDataAdapter(); da.SelectCommand = CreateCommand(SQL, ConnectString, false); return da; } VB.NET Public Shared Function CreateDataAdapter(ByVal SQL As String, ByVal ConnectString As String) As IDbDataAdapter Dim da As IDbDataAdapter da = DataProvider.CreateDataAdapter() da.SelectCommand = CreateCommand(SQL, ConnectString, False) Return da End Function

GetDataSet Method The GetDataSet method returns a DataSet based on the SQL statement and ConnectString you pass to this method.

3-16 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 69: Fundamentals of N Tier

GetDataSet Method

C# public static DataSet GetDataSet(string SQL, string ConnectString) { DataSet ds = new DataSet(); IDbDataAdapter da; da = CreateDataAdapter(SQL, ConnectString); da.Fill(ds); return ds; } VB.NET Public Shared Function GetDataSet(ByVal SQL As String, _ ByVal ConnectString As String) As DataSet Dim ds As New DataSet Dim da As IDbDataAdapter ' Create Data Adapter da = CreateDataAdapter(SQL, ConnectString) da.Fill(ds) Return ds End Function

Usage Below is a sample from the solution for this chapter that shows the usage of the GetDataSet method from a front-end application.

Fundamentals of N-Tier 3-17 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 70: Fundamentals of N Tier

The DataLayer Component

C# private void btnGetDataSet_Click(object sender, EventArgs e) { DataSet ds; try { ds = DataLayer.GetDataSet(txtSELECT.Text, txtConnectString.Text); grdResults.DataSource = ds.Tables[0]; } catch (Exception ex) { MessageBox.Show(ex.Message); } } VB.NET Private Sub btnGetDataSet_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnGetDataSet.Click Dim ds As DataSet Try ds = DataLayer.GetDataSet(txtSELECT.Text, _ txtConnectString.Text) grdResults.DataSource = ds.Tables(0) Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub

GetDataTable Method The GetDataTable method is exactly like the GetDataSet method (in fact it calls the GetDataSet method). It will return a DataTable object.

3-18 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 71: Fundamentals of N Tier

GetDataTable Method

C# public static DataTable GetDataTable(string SQL, string ConnectString) { DataSet ds = new DataSet(); DataTable dt = null; ds = GetDataSet(SQL, ConnectString); if (ds.Tables.Count > 0) dt = ds.Tables[0]; return dt; } VB.NET Public Shared Function GetDataTable(ByVal SQL As String, _ ByVal ConnectString As String) As DataTable Dim ds As DataSet Dim dt As DataTable = Nothing ds = GetDataSet(SQL, ConnectString) If ds.Tables.Count > 0 Then dt = ds.Tables(0) End If Return dt End Function

Usage Below is an example of using the GetDataTable method from the solution file for this chapter.

Fundamentals of N-Tier 3-19 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 72: Fundamentals of N Tier

The DataLayer Component

C# private void btnGetDataTable_Click(object sender, EventArgs e) { DataTable dt; try { dt = DataLayer.GetDataTable(txtSELECT.Text, _ txtConnectString.Text); grdResults.DataSource = dt; } catch (Exception ex) { MessageBox.Show(ex.Message); } } VB.NET Private Sub btnGetDataTable_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btnGetDataTable.Click Dim dt As DataTable Try dt = DataLayer.GetDataTable(txtSELECT.Text, _ txtConnectString.Text) grdResults.DataSource = dt Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub

GetDataReader The GetDataReader method will return an appropriate DataReader.

3-20 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 73: Fundamentals of N Tier

GetDataReader

C# public static IDataReader GetDataReader(string SQL, string ConnectString) { IDataReader dr; IDbCommand cmd = null; try { // Create Command with Connection Object cmd = CreateCommand(SQL, ConnectString); // Create the DataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); } catch { // If there is an exception, close the connection if (cmd.Connection.State != ConnectionState.Closed) { cmd.Connection.Close(); cmd.Connection.Dispose(); } // Dispose of the Command cmd.Dispose(); // Rethrow the exception throw; } return dr; } VB.NET Public Shared Function GetDataReader(ByVal SQL As String, _ ByVal ConnectString As String) _ As IDataReader Dim dr As IDataReader Dim cmd As IDbCommand = Nothing Try ' Create Command with Connection Object cmd = CreateCommand(SQL, ConnectString) ' Create the DataReader dr = cmd.ExecuteReader( _ CommandBehavior.CloseConnection) Catch ' If there is an exception, close the connection If cmd.Connection.State <> ConnectionState.Closed Then cmd.Connection.Close() cmd.Connection.Dispose() End If ' Dispose of the Command cmd.Dispose() ' Rethrow the exception Throw End Try

Fundamentals of N-Tier 3-21 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 74: Fundamentals of N Tier

The DataLayer Component

Return dr End Function

Notice the use of the CommandBehavior.CloseConnection argument passed to the ExecuteReader method. This is necessary since the DataReader is returned out of this method. If you did not do this, the connection would not be closed until the garbage collector cleaned up the DataReader object after you released it. By setting this value you inform the DataReader that when you invoke the Close method on the DataReader that it should also close the connection.

Usage Below is an example of calling the GetDataReader method from the example in the solution for this chapter.

3-22 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 75: Fundamentals of N Tier

GetDataReader

C# private void btnGetDataReader_Click(object sender, EventArgs e) { IDataReader dr = null; try { dr = DataLayer.GetDataReader(txtSELECT.Text, txtConnectString.Text); while (dr.Read()) { lstProducts.Items.Add(dr["sProductName"]); } } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { if(dr != null) { dr.Close(); dr.Dispose(); } } } VB.NET Private Sub btnGetDataReader_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btnGetDataReader.Click Dim dr As IDataReader = Nothing Try dr = DataLayer.GetDataReader(txtSELECT.Text, _ txtConnectString.Text) Do While dr.Read() lstProducts.Items.Add(dr("sProductName")) Loop Catch ex As Exception MessageBox.Show(ex.Message) Finally If dr IsNot Nothing Then dr.Close() dr.Dispose() End If End Try End Sub

Notice the clean up code in the Finally block. You must be sure to close and dispose of the DataReader object after you are finished with it to avoid leaving a connection open.

Fundamentals of N-Tier 3-23 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 76: Fundamentals of N Tier

The DataLayer Component

ExecuteScalar The ExecuteScalar method will return a single value from the SQL statement you pass in. The SQL statement you pass should only return one row and one column. Examples would be using a scalar function such as Avg, Min, Max, etc.

3-24 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 77: Fundamentals of N Tier

ExecuteScalar

C# public static Object ExecuteScalar(string SQL, string ConnectString) { IDbCommand cmd = null; Object value = null; try { // Create Command with Connection Object cmd = CreateCommand(SQL, ConnectString); // Execute SQL value = cmd.ExecuteScalar(); } catch { throw; } finally { // Close the connection if (cmd.Connection.State == ConnectionState.Open) cmd.Connection.Close(); // Dispose of the Objects cmd.Connection.Dispose(); cmd.Dispose(); } return value; } VB.NET Public Shared Function ExecuteScalar(ByVal SQL As String, _ ByVal ConnectString As String) As Object Dim cmd As IDbCommand = Nothing Dim value As Object = Nothing Try ' Create Command with Connection Object cmd = CreateCommand(SQL, ConnectString) ' Execute SQL value = cmd.ExecuteScalar() Catch Throw Finally ' Close the connection If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() End If ' Dispose of the Objects cmd.Connection.Dispose() cmd.Dispose() End Try Return value End Function

Fundamentals of N-Tier 3-25 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 78: Fundamentals of N Tier

The DataLayer Component

The ExecuteScalar method looks very similar to the ExecuteSQL method. The major difference is the call to ExecuteScalar as opposed to ExecuteNonQuery and the return value. The ExecuteScalar method returns an Object data type. When you invoke one of the scalar functions this method does not know the data type of the column on which you executed the function. Thus any valid numerical data type could be returned. The only data type equipped to handle any data type is Object. This means that you will be required to cast the data type to an appropriate type for your application prior to using the return value from this method.

Usage Below is an example of using the ExecuteScalar method.

C# private void btnExecuteScalar_Click(object sender, EventArgs e) { decimal dec; try { dec = Convert.ToDecimal(DataLayer.ExecuteScalar( txtScalar.Text, txtConnectString.Text)); txtScalarResult.Text = dec.ToString(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } VB.NET Private Sub btnExecuteScalar_Click( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnExecuteScalar.Click Dim dec As Decimal Try dec = Convert.ToDecimal(DataLayer.ExecuteScalar( _ txtScalar.Text, txtConnectString.Text)) txtScalarResult.Text = dec.ToString() Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub

Notice that you must use the Convert class to cast the object data type returned from the ExecuteScalar method to the type you are expecting back.

3-26 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 79: Fundamentals of N Tier

ExecuteSQL Method

ExecuteSQL Method The ExecuteSQL method is used when you need to submit an action statement (INSERT, UPDATE or DELETE) to the back end database. This method will return the number of rows affected by the statement. You may also use this method to submit a stored procedure name as long as that stored procedure also performs an INSERT, UPDATE or DELETE.

ExecuteSQL(cmd, DisposeOfCommand) This overload takes a Command object and a Boolean value to specify whether or not you wish to dispose of the command object when you are done with it. You might wish to keep the command object around if you plan on reusing it to submit other values on the same command object.

Fundamentals of N-Tier 3-27 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 80: Fundamentals of N Tier

The DataLayer Component

C# public static int ExecuteSQL(IDbCommand cmd, bool DisposeOfCommand) { int intRows = 0; bool boolOpen = false; try { // Open the Connection if (cmd.Connection.State != ConnectionState.Open) { cmd.Connection.Open(); } else { boolOpen = !DisposeOfCommand; } // Execute SQL intRows = cmd.ExecuteNonQuery(); } catch { throw; } finally { if (!boolOpen) { // Close the connection if (cmd.Connection.State == ConnectionState.Open) { cmd.Connection.Close(); } // Dispose of the Objects cmd.Connection.Dispose(); } if (DisposeOfCommand) cmd.Dispose(); } return intRows; } VB.NET Public Shared Function ExecuteSQL(ByVal cmd As IDbCommand, _ ByVal DisposeOfCommand As Boolean) As Integer Dim intRows As Integer = 0 Dim boolOpen As Boolean = False Try ' Open the Connection If cmd.Connection.State <> ConnectionState.Open Then cmd.Connection.Open() Else boolOpen = Not DisposeOfCommand End If ' Execute SQL intRows = cmd.ExecuteNonQuery() Catch

3-28 Fundamentals of N-Tier

Throw

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 81: Fundamentals of N Tier

ExecuteSQL Method

Finally If Not boolOpen Then ' Close the connection If cmd.Connection.State = ConnectionState.Open Then cmd.Connection.Close() End If ' Dispose of the Objects cmd.Connection.Dispose() End If If DisposeOfCommand Then cmd.Dispose() End If End Try Return intRows End Function

Usage Below is an example of using this overload of the ExecuteSQL method.

Fundamentals of N-Tier 3-29 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 82: Fundamentals of N Tier

The DataLayer Component

C# private void btnExecuteSQL1_Click(object sender, EventArgs e) { IDbCommand cmd = null; int intRet = 0; try { cmd = DataLayer.CreateCommand(ProductInsertSQL(), txtConnectString.Text); intRet = DataLayer.ExecuteSQL(cmd, true); txtResult.Text = intRet.ToString(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } VB.NET Private Sub btnExecuteSQL1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecuteSQL1.Click Dim cmd As IDbCommand = Nothing Dim intRet As Integer = 0 Try cmd = DataLayer.CreateCommand(ProductInsertSQL(), _ txtConnectString.Text) intRet = DataLayer.ExecuteSQL(cmd, True) txtResult.Text = intRet.ToString() Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub

Notice the call to the ProductInsertSQL() method. This method is called by several methods in this example. This method looks like the following:

3-30 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 83: Fundamentals of N Tier

ExecuteSQL Method

C# private string ProductInsertSQL() { string strSQL; strSQL = "INSERT INTO tblProducts"; strSQL += "(sProductName, dtIntroduced, cCost, cPrice, bDiscontinued)"; strSQL += " VALUES('A New Product', '{0}', 25, 50, 0)"; strSQL = string.Format(strSQL, DateTime.Now.ToString("G")); return strSQL; } VB.NET Private Function ProductInsertSQL() As String Dim strSQL As String strSQL = "INSERT INTO tblProducts" strSQL &= "(sProductName, dtIntroduced, cCost, cPrice, bDiscontinued)" strSQL &= " VALUES('A New Product', '{0}', 25, 50, 0)" strSQL = String.Format(strSQL, DateTime.Now.ToString("G")) Return strSQL End Function

ExecuteSQL(cmd) This overload of the ExecuteSQL method accepts just a Command object. This assumes that you have already created the appropriate command object and it is ready to be submitted to the database.

C# public static int ExecuteSQL(IDbCommand cmd) { return ExecuteSQL(cmd, false); } VB.NET Public Shared Function ExecuteSQL(ByVal cmd As IDbCommand) As Integer Return ExecuteSQL(cmd, False) End Function

Usage Below is an example of creating a command object with a SQL statement, creating a connection, opening the connection and executing the SQL in that command object.

Fundamentals of N-Tier 3-31 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 84: Fundamentals of N Tier

The DataLayer Component

C# private void btnExecuteSQL2_Click(object sender, EventArgs e) { IDbCommand cmd = null; IDbConnection cnn = null; int intRet = 0; try { cmd = DataLayer.CreateCommand(ProductInsertSQL()); cnn = DataLayer.CreateConnection(txtConnectString.Text); cnn.Open(); cmd.Connection = cnn; intRet = DataLayer.ExecuteSQL(cmd, false); txtResult.Text = intRet.ToString(); } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { if (cnn != null) { cnn.Close(); cnn.Dispose(); } if (cmd != null) { cmd.Dispose(); } } } VB.NET Private Sub btnExecuteSQL2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecuteSQL2.Click Dim cmd As IDbCommand = Nothing Dim cnn As IDbConnection = Nothing Dim intRet As Integer = 0 Try cmd = DataLayer.CreateCommand(ProductInsertSQL()) cnn = DataLayer.CreateConnection(txtConnectString.Text) cnn.Open() cmd.Connection = cnn intRet = DataLayer.ExecuteSQL(cmd, False) txtResult.Text = intRet.ToString() Catch ex As Exception MessageBox.Show(ex.Message) Finally If cnn IsNot Nothing Then cnn.Close()

3-32 Fundamentals of N-Tier

cnn.Dispose() End If

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 85: Fundamentals of N Tier

ExecuteSQL Method

If cmd IsNot Nothing Then cmd.Dispose() End If End Try End Sub

Usage with Parameters The example below shows creating parameters and filling them in with the appropriate values.

Fundamentals of N-Tier 3-33 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 86: Fundamentals of N Tier

The DataLayer Component

C# private void btnExecuteSQL4_Click(object sender, EventArgs e) { IDbCommand cmd = null; int intRet = 0; try { cmd = DataLayer.CreateCommand( ProductInsertSQLWithParams(), txtConnectString.Text); cmd.Parameters.Add( DataLayer.CreateParameter("@sProductName", DbType.String, "A New Product (Param)")); cmd.Parameters.Add( DataLayer.CreateParameter("@dtIntroduced", DbType.DateTime, DateTime.Now)); cmd.Parameters.Add( DataLayer.CreateParameter("@cCost", DbType.Decimal, 50)); cmd.Parameters.Add( DataLayer.CreateParameter("@cPrice", DbType.Decimal, 150)); cmd.Parameters.Add( DataLayer.CreateParameter("@bDiscontinued", DbType.Int16, 0)); intRet = DataLayer.ExecuteSQL(cmd); txtResult.Text = intRet.ToString(); } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { if (cmd != null) { cmd.Dispose(); } } } VB.NET Private Sub btnExecuteSQL4_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecuteSQL4.Click Dim cmd As IDbCommand = Nothing Dim intRet As Integer = 0 Try cmd = DataLayer.CreateCommand( _ ProductInsertSQLWithParams(), txtConnectString.Text) cmd.Parameters.Add(DataLayer.CreateParameter( _ "@sProductName", DbType.String, _ "A New Product (Param)"))

3-34 Fundamentals of N-Tier

cmd.Parameters.Add(DataLayer.CreateParameter( _

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 87: Fundamentals of N Tier

ExecuteSQL Method

"@dtIntroduced", DbType.DateTime, DateTime.Now)) cmd.Parameters.Add(DataLayer.CreateParameter("@cCost", DbType.Decimal, 50)) cmd.Parameters.Add(DataLayer.CreateParameter("@cPrice", _ DbType.Decimal, 150)) cmd.Parameters.Add( _ DataLayer.CreateParameter("@bDiscontinued", _ DbType.Int16, 0)) intRet = DataLayer.ExecuteSQL(cmd) txtResult.Text = intRet.ToString() Catch ex As Exception MessageBox.Show(ex.Message) Finally If cmd IsNot Nothing Then cmd.Dispose() End If End Try End Sub

Notice the use of the ProductInsertSQLWithParams method.

Fundamentals of N-Tier 3-35 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 88: Fundamentals of N Tier

The DataLayer Component

C# private string ProductInsertSQLWithParams() { string strSQL; strSQL = "INSERT INTO tblProducts"; strSQL += "(sProductName, dtIntroduced, cCost, cPrice, bDiscontinued)"; strSQL += " VALUES(@sProductName, @dtIntroduced, @cCost, @cPrice, @bDiscontinued)"; return strSQL; } VB.NET Private Function ProductInsertSQLWithParams() As String Dim strSQL As String strSQL = "INSERT INTO tblProducts" strSQL &= "(sProductName, dtIntroduced, cCost, cPrice, bDiscontinued)" strSQL &= " VALUES(@sProductName, @dtIntroduced, @cCost, @cPrice, @bDiscontinued)" Return strSQL End Function

ExecuteSQL(SQL, ConnectString) This overload of the ExecuteSQL method accepts a SQL string and a Connection String.

3-36 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 89: Fundamentals of N Tier

ExecuteSQL Method

C# public static int ExecuteSQL(string SQL, string ConnectString) { IDbCommand cmd = null; int intRows; cmd = CreateCommand(SQL, ConnectString); // Execute SQL intRows = ExecuteSQL(cmd, true); return intRows; } VB.NET Public Shared Function ExecuteSQL( _ ByVal SQL As String, _ ByVal ConnectString As String) As Integer Dim cmd As IDbCommand = Nothing Dim intRows As Integer cmd = CreateCommand(SQL, ConnectString) ' Execute SQL intRows = ExecuteSQL(cmd, True) Return intRows End Function

Usage Below is an example of how to use this overload.

Fundamentals of N-Tier 3-37 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 90: Fundamentals of N Tier

The DataLayer Component

C# private void btnExecuteSQL3_Click(object sender, EventArgs e) { int intRet = 0; try { intRet = DataLayer.ExecuteSQL(ProductInsertSQL(), txtConnectString.Text); txtResult.Text = intRet.ToString(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } VB.NET Private Sub btnExecuteSQL3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecuteSQL3.Click Dim intRet As Integer = 0 Try intRet = DataLayer.ExecuteSQL(ProductInsertSQL(), _ txtConnectString.Text) txtResult.Text = intRet.ToString() Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub

NOTE: If any exceptions occur within any of the methods in the DataLayer class they are simply ignored here. It is up to the calling method to either do something with this error, or throw it up the call stack as well. Another exception design pattern you could use would be to create a custom exception from within these methods, gather as much information about the SQL string that is submitted and the connection string, and then throw that back to the caller.

3-38 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 91: Fundamentals of N Tier

ExecuteSQL Method

Summary In this chapter you learned how to create a very flexible and robust DataLayer class and a DataProvider class. All of the classes that you create will either directly or indirectly use this class to submit SQL to the back end database. You should always use this data layer class instead of referencing ADO.NET directly in your front-end applications. This will make your application more portable and allows you to change the implementation quickly and easily.

Fundamentals of N-Tier 3-39 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 92: Fundamentals of N Tier

The DataLayer Component

3-40 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

THIS PAGE INTENTIONALLY LEFT BLANK

Page 93: Fundamentals of N Tier

Chapter 4

Data Classes A Data Class is a class that knows about a single database table. It knows how to SELECT information from it, and how to modify the data within the table. It also knows about the schema (the column information) of the table. The reason for having a data class map to a single table is because SQL can only change one table at a time. In other words, an INSERT, UPDATE or DELETE SQL statement can only affect one table. To be able to do multi-table operations, you need to create multiple SQL data modification statements and put them together into a transaction. Consider the case where in one operation you need to update an Order Header and Order Detail table. You have to create two different SQL statements and submit them within a transaction. Well, what if in another operation, you just need to update the Order Detail table. You can use the same data class you used in the first operation, without having to re-write the SQL used to update the Order Detail table. In the first operation all you have to do is create an instance of an Order Detail data class, and in the second instance all you have to do is create an instance of the Order Detail class. The only difference is in the first operation, you additionally create an instance of the Order Header data class as well. Other types of Data Classes might also be ones that know how to call a View in a database. This is useful if you want to perform joins between tables and want an object that will call that view for you from the UI layer. Another type of Data Class would be one that knows how to call a stored procedure in your database. These data classes can call one or many stored procedures via different methods in the data class, and can contain properties that map to the appropriate parameters into the stored procedure. Figure 4.1 shows an overview of the class hierarchy that you are eventually going to build. You can see that you have four distinct layers (or tiers) in this hierarchy. You have the User Interface layer, the Business Rule and Data Access Class layer, the Data Layer and the Database Server layer. In this chapter you will focus on the Data Access Classes and the Schema Structure.

Page 94: Fundamentals of N Tier

Data Classes

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Figure 4.1. An overview of the Data Class Hierarchy you will create.

To follow along and see the final example of the data class, load the solution file: NTierSample2CS\NTierSample2CS.sln or NTierSample2VB\NTierSample2VB.sln

Standard Properties of the Data Classes

4-2 Fundamentals of N-Tier For each data class you should have at a minimum the following properties:

Page 95: Fundamentals of N Tier

Methods of the Data Classes

Method Name Description

Schema This is an instance of the Schema structure. The Schema structure contains one string element for each column in your table. This string element returns the name of the column. This is used so you get an IntelliSense list of column names.

ConnectString The ConnectString property needs to be sent into this class so it is not hard coded to one particular location. The ConnectString property can be initialized within the business class, or even passed down from the UI layer. This gives you the ultimate in flexibility.

<Column Properties>

You should have one property for each column in your table. For example, if you have a Products table, you may have the following properties in the class; ProductName, Cost, Price, DateIntroduced, etc. Each of these elements will be strongly typed to represent the data type from the table.

Methods of the Data Classes You should strive to create a basic design pattern for each data class. A design pattern means that you will have the same methods and same property names for each class that uses the design pattern. Each data class should be defined as abstract/MustInherit. This keeps anyone from directly creating an instance of this class. Consumers of this class should always use the business class that inherits from this class. This will ensure that business rules are checked prior to calling any of the modification methods in this data class. You will learn to create the business rule class in the next chapter. For each data class you should have at a minimum the following methods:

Method Name Description

(Constructor) Initializes the Schema structure and performs any other necessary initialization of the class.

Get[TableName]s() This will be one or many methods that will return all rows and columns from the underlying table, stored procedure or view. For example: GetProducts(), GetOrders(), GetCustomers(), etc.

Get[TableName](<PK>) This method will return a single row based on a primary key value passed in. For example: GetProduct(), GetCustomer(), GetOrder(), etc.

Fundamentals of N-Tier 4-3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 96: Fundamentals of N Tier

Data Classes

Load(<PK>) You will pass in the primary key for the row you wish to find in the base table. This method will call the Get?(<PK>) method and then fill in all of the column properties with the values from that row.

Insert This method is called to perform an INSERT into the table. Prior to calling the Insert method, you will fill in each of the Schema properties in the class. This method will use these values to create the INSERT statement.

Update This method is called to perform an UPDATE on the table. Prior to calling the Update method, you will fill in each of the Schema properties in the class. This method will use these values to create the UPDATE statement.

Delete This method is called to DELETE a row from a table. Prior to calling this method, you must fill in the primary key property with value of the row you wish to delete.

FillInParameters This method is called by the Insert, Update and Delete methods to build the parameters for the call to the stored procedure to perform the actual INSERT, UPDATE or DELETE. Or it could use dynamic SQL with replaceable parameters.

NOTE This is just a proposed list of methods that you might create. Feel free to come up with different names, or additional methods that you want to have as a part of your base architecture.

Examples of Usage The following examples show you how to consume a Products class from a front end application. The first example shows the use of the GetProducts method to fill the DataSource property of a List Box control. Also note the use of the Schema class to fill in the column names into the ValueMember property and the DisplayMember property. Instead of you having to remember the column name, or potentially misspell it, you use the Schema property which provides you with an IntelliSense list of column names.

4-4 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 97: Fundamentals of N Tier

Methods of the Data Classes

C# private void ProductGetSample() { Products prod = new Products(); lstProducts.ValueMember = prod.Schema.ProductId; lstProducts.DisplayMember = prod.Schema.ProductName; lstProducts.DataSource = prod.GetProducts(); } VB.NET Private Sub ProductGetSample() Dim prod As Products = New Products() lstProducts.ValueMember = prod.Schema.ProductId lstProducts.DisplayMember = prod.Schema.ProductName lstProducts.DataSource = prod.GetProducts() End Sub

NOTE Remember that the Products class is the business class, not the data class presented in this chapter. The Products business class inherits from the ProductsDC data class, so you get all members of the ProductsDC class automatically.

When you are ready to add a new product into the tblProducts table you will use the properties that map to each column in the table. Each property is filled in with the appropriate data (hard coded in the following example). The Insert method is called after filling in the properties. This method will be responsible for building the correct INSERT statement.

Fundamentals of N-Tier 4-5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 98: Fundamentals of N Tier

Data Classes

C# private void ProductInsertSample() { Products prod = new Products(); prod.ProductName = "A New Product";

prod.Introduced = DateTime.Now.ToString(); prod.Cost = 20; prod.Price = 55; prod.Discontinued = false; prod.Insert(); } VB.NET Private Sub ProductInsertSample() Dim prod As Products = New Products() prod.ProductName = "A New Product" prod.Introduced = DateTime.Now.ToString() prod.Cost = 20 prod.Price = 55 prod.Discontinued = False prod.Insert() End Sub

You can see from the examples that using a class in your front-end UI layer makes your code much more readable. Using the IntelliSense features of VS.NET means you do not have to remember column names, and since the column properties are strongly typed, you don't have to worry about passing in the wrong type of data. There are a lot of advantages to this approach.

Match Columns to Properties A very common approach for a basic CRUD (Create, Read, Update, Delete) set of data classes is to create one class for each table in your database. To do this, you will need to create a property for every column in your table. The property you create should match the name and the data type of the column in your table. Creating a property per column allows you to get IntelliSense and strong typing in your classes. These properties can be filled in with the data in a very easy to use manner. The data modification methods in the class can grab the data from the properties to create the INSERT, UPDATE and DELETE statements. This is a very simple way of modifying tables in your database. Here is an example of what the Product data properties for each column might look like.

4-6 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 99: Fundamentals of N Tier

Match Columns to Properties

C# public class ProductsDC { private int mintProductId; private string mstrProductName; private string mstrIntroduced; private decimal mdecCost; private decimal mdecPrice; private bool mboolDiscontinued; public int ProductId { get { return mintProductId; } set { mintProductId = value; } } public string ProductName { get { return mstrProductName; } set { mstrProductName = value; } } public string Introduced { get { return mstrIntroduced; } set { mstrIntroduced = value; } } public decimal Cost { get { return mdecCost; } set { mdecCost = value; } } public decimal Price { get { return mdecPrice; } set { mdecPrice = value; } } public bool Discontinued { get { return mboolDiscontinued; } set { mboolDiscontinued = value; } } } VB.NET Public Class ProductsDC Private mintProductId As Integer Private mstrProductName As String Private mstrIntroduced As String Private mdecCost As Decimal Private mdecPrice As Decimal Private mboolDiscontinued As Boolean Property ProductId() As Integer Get Return mintProductId End Get Set(ByVal Value As Integer) mintProductId = Value

Fundamentals of N-Tier 4-7 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 100: Fundamentals of N Tier

Data Classes

End Set End Property Property ProductName() As String Get Return mstrProductName End Get Set(ByVal Value As String) mstrProductName = Value End Set End Property Property Introduced() As String Get Return mstrIntroduced End Get Set(ByVal Value As String) mstrIntroduced = Value End Set End Property Property Cost() As Decimal Get Return mdecCost End Get Set(ByVal Value As Decimal) mdecCost = Value End Set End Property Property Price() As Decimal Get Return mdecPrice End Get Set(ByVal Value As Decimal) mdecPrice = Value End Set End Property Property Discontinued() As Boolean Get Return mboolDiscontinued End Get Set(ByVal Value As Boolean) mboolDiscontinued = Value End Set End Property End Class

NOTE For columns in your table that are of type date, you might want to use a string property. In this way you can set the date field to an empty string to represent no date.

4-8 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 101: Fundamentals of N Tier

Schema Structure

Connection String Property Each of the Data Classes that map to a table should also contain a ConnectionString property. This is then set prior to calling any of the data retrieval or data modification statements from your front end. The business object could also set this ConnectionString property as well.

Schema Structure In addition to having a property to hold the data about the columns in the table, it is also a good idea to create a structure to hold the names of each column. This structure can be exposed as a public property on the class as well. This allows you to use this schema name in a front-end application where you would otherwise have to remember the column name. Using a structure you get an IntelliSense list of the column names. This is much easier to use.

C# public class ProductsDC ... public struct SchemaStruct { public string ObjectName; public string ProductId; public string ProductName; public string Introduced; public string Cost; public string Price; public string Discontinued; } } VB.NET Public Class ProductsDC ... Public Structure SchemaStruct Public ObjectName As String Public ProductId As String Public ProductName As String Public Introduced As String Public Cost As String Public Price As String Public Discontinued As String End Structure End Class

This structure belongs in the ProductsDC class and will be exposed via a public property, and initialized in the constructor for the class as shown below:

Fundamentals of N-Tier 4-9 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 102: Fundamentals of N Tier

Data Classes

C# public class ProductsDC { public ProductsDC.SchemaStruct Schema; public ProductsDC() { Schema = new ProductsDC.SchemaStruct(); Schema.ObjectName = "tblProducts"; Schema.ProductId = "iProduct_id"; Schema.ProductName = "sProductName"; Schema.Discontinued = "bDiscontinued"; Schema.Introduced = "dtIntroduced"; Schema.Price = "cPrice"; Schema.Cost = "cCost"; } VB.NET Public Class ProductsDC Public Schema As ProductsDC.SchemaStruct Public Sub New() Schema = New ProductsDC.SchemaStruct() Schema.ObjectName = "tblProducts" Schema.ProductId = "iProduct_id" Schema.ProductName = "sProductName" Schema.Discontinued = "bDiscontinued" Schema.Introduced = "dtIntroduced" Schema.Price = "cPrice" Schema.Cost = "cCost" End Sub

Usage of the Schema Structure You can see in the code example below that you now can use the IntelliSense feature of the Schema class to fill in column names in your front-end UI without having to remember column names.

4-10 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 103: Fundamentals of N Tier

Data Retrieval Methods

C# private void ProductGetSample() { Products prod = new Products(); lstProducts.ValueMember = prod.Schema.ProductId; lstProducts.DisplayMember = prod.Schema.ProductName; lstProducts.DataSource = prod.GetProducts(); } VB.NET Private Sub ProductGetSample() Dim prod As Products = New Products() lstProducts.ValueMember = prod.Schema.ProductId lstProducts.DisplayMember = prod.Schema.ProductName lstProducts.DataSource = prod.GetProducts() End Sub

Data Retrieval Methods Now that you have the basic structure of the class completed, you can now start creating the methods that will perform data retrieval from this class. At this point you should just worry about retrieving the data from this single table. Remember these are the CRUD classes and as such should typically deal with just a single table. You will learn about multi-table joins and multi-table updates later.

Returning All Rows You will need to create a method that returns all rows and all columns from this table. You can feel free to create other methods as well, but this will be your basic design pattern to follow:

Fundamentals of N-Tier 4-11 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 104: Fundamentals of N Tier

Data Classes

C# public DataSet GetProducts() { DataSet ds = null; string strSQL; strSQL = "SELECT * FROM tblProducts"; // Use the DataLayer to Build DataTable ds = DataLayer.GetDataSet(strSQL, mstrConnectString); return ds; } VB.NET Public Function GetProducts() As DataSet Dim ds As DataSet Dim strSQL As String strSQL = "SELECT * FROM tblProducts" ' Retrieve DataTable from Data Layer ds = DataLayer.GetDataSet(strSQL, mstrConnectString) Return ds End Function

The GetProducts method (or GetCustomers, or GetOrders, etc.) returns a DataSet by using the DataLayer class. You simply create the appropriate SQL statement that returns the data you wish, pass that SQL statement with the connection string to the GetDataSet method of the DataLayer class and that class will do the rest.

NOTE: In this book you see dynamic SQL used in all the classes. Each of these statements could be replaced with calls to stored procedures. The choice is up to you.

Returning One Row by Primary Key At some point in your data retrieval you will most likely need to get all the columns for just a single row. For this, build another method that uses the primary key that is passed in to create a WHERE clause in your SQL statement. In the tblProducts table, the ProductID column is the primary key. Thus the GetProduct method looks like the following:

4-12 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 105: Fundamentals of N Tier

Data Retrieval Methods

C# public DataSet GetProduct(int ProductID) { DataSet ds = null; string strSQL; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + ProductID.ToString(); // Use the DataLayer to Build DataSet ds = DataLayer.GetDataSet(strSQL, mstrConnectString); return ds; } VB.NET Public Function GetProduct(ByVal ProductID As Integer) As DataSet Dim ds As DataSet = Nothing Dim strSQL As String = String.Empty strSQL = "SELECT * FROM tblProducts" strSQL &= " WHERE iProduct_id = " + ProductID.ToString() ' Use the DataLayer to Build DataSet ds = DataLayer.GetDataSet(strSQL, mstrConnectString) Return ds End Function

Notice that the design pattern for this method is exactly the same as for the method that retrieved all the rows. You will create the SQL using the parameter passed in. The SQL is then passed to the DataLayer.GetDataSet method.

Load Method The GetProduct method returned a DataSet with one DataTable in it, and this DataTable has one DataRow in it. It is logical to then retrieve all of the column values from this one DataRow and place these values into each of the corresponding properties in the class. This is the purpose of the Load method.

Fundamentals of N-Tier 4-13 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 106: Fundamentals of N Tier

Data Classes

C# public bool Load(int ProductID) { DataSet ds = null; DataRow dr; bool boolRet = false; ds = GetProduct(ProductID); if (ds != null) { if (ds.Tables.Count > 0) { boolRet = true; dr = ds.Tables[0].Rows[0]; mintProductId = Convert.ToInt32(dr[this.Schema.ProductId]); mstrProductName = dr[this.Schema.ProductName].ToString(); mstrIntroduced = dr[this.Schema.Introduced].ToString(); mdecCost = Convert.ToDecimal(dr[this.Schema.Cost]); mdecPrice = Convert.ToDecimal(dr[this.Schema.Price]); mboolDiscontinued = Convert.ToBoolean(dr[this.Schema.Discontinued]); } } return boolRet; } VB.NET Public Function Load(ByVal ProductID As Integer) As Boolean Dim ds As DataSet = Nothing Dim dr As DataRow Dim boolRet As Boolean = False ds = GetProduct(ProductID) If ds IsNot Nothing Then If ds.Tables.Count > 0 Then boolRet = True dr = ds.Tables(0).Rows(0) mintProductId = _ Convert.ToInt32(dr(Me.Schema.ProductId)) mstrProductName = dr(Me.Schema.ProductName).ToString() mstrIntroduced = dr(Me.Schema.Introduced).ToString() mdecCost = Convert.ToDecimal(dr(Me.Schema.Cost)) mdecPrice = Convert.ToDecimal(dr(Me.Schema.Price)) mboolDiscontinued = _ Convert.ToBoolean(dr(Me.Schema.Discontinued)) End If End If Return boolRet End Function

The Load method calls the GetProduct method to retrieve the single DataRow. It then takes the values from the DataRow, and using the Schema

4-14 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 107: Fundamentals of N Tier

Data Modification Methods

class, accesses each of the values in to appropriate columns and loads them into the properties. Feel free to add on any additional methods to retrieve different combinations of data from the tblProducts table. In fact, you could even write methods in here that return data from Views or Stored Procedures that do joins with the Products table. You will learn more about this later.

Data Modification Methods Now that you have the basic data retrieval methods written, you can now turn your attention to writing the methods that will modify the data within the table. You will create three methods; Insert, Update and Delete, and one additional method that will be called by all three of these.

Insert Method The Insert method is responsible for creating a call to a stored procedure, or creating a dynamic SQL INSERT statement. It creates a command object and places the SQL statement into this command object. In the SQL you need to put actual parameter names that match the parameters in the stored procedure, or create parameters that you can replace in the dynamic SQL statement. The FillInParameters method is responsible for creating the appropriate parameter objects, adding them to the parameters collection of the command object, and filling in the value of the parameters from the appropriate properties on the object. There is also a call to a method called Validate(). This Validate method is used to check business rules that apply to the columns in the table. You will see this Validate method a little later in this chapter.

Fundamentals of N-Tier 4-15 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 108: Fundamentals of N Tier

Data Classes

C# public int Insert() { IDbCommand cmd; string strSQL; // Check Business Rules Validate(); strSQL = "INSERT INTO tblProducts(sProductName, "; strSQL += " dtIntroduced, cCost, cPrice, bDiscontinued) "; strSQL += "VALUES(@sProductName, @dtIntroduced, "; strSQL += " @cCost, @cPrice, @bDiscontinued) "; cmd = DataLayer.CreateCommand(strSQL, mstrConnectString); FillInParameters(cmd); return DataLayer.ExecuteSQL(cmd, true); } VB.NET Public Function Insert() As Integer Dim cmd As IDbCommand Dim strSQL As String ' Check Business Rules Validate() strSQL = "INSERT INTO tblProducts(sProductName, " strSQL &= " dtIntroduced, cCost, cPrice, bDiscontinued) " strSQL &= "VALUES(@sProductName, @dtIntroduced, " strSQL &= " @cCost, @cPrice, @bDiscontinued) " cmd = DataLayer.CreateCommand(strSQL, mstrConnectString) FillInParameters(cmd) Return DataLayer.ExecuteSQL(cmd, True) End Function

FillInParameters Method The FillInParameters method will create new parameters using the DataLayer.CreateParameter method. It will then add these parameters to the command object that is passed into this method from the Insert, Update and Delete methods.

4-16 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 109: Fundamentals of N Tier

Data Modification Methods

C# protected void FillInParameters(IDbCommand cmd) { cmd.Parameters.Add(DataLayer.CreateParameter( "@iProduct_id", DbType.Int32, mintProductId)); cmd.Parameters.Add(DataLayer.CreateParameter( "@sProductName", DbType.String, mstrProductName)); cmd.Parameters.Add(DataLayer.CreateParameter( "@dtIntroduced", DbType.DateTime, mstrIntroduced)); cmd.Parameters.Add(DataLayer.CreateParameter( "@cCost", DbType.Decimal, mdecCost)); cmd.Parameters.Add(DataLayer.CreateParameter( "@cPrice", DbType.Decimal, mdecPrice)); cmd.Parameters.Add(DataLayer.CreateParameter( "@bDiscontinued", DbType.Decimal, mboolDiscontinued)); } VB.NET Protected Sub FillInParameters(ByVal cmd As IDbCommand) cmd.Parameters.Add(DataLayer.CreateParameter( _ "@iProduct_id", DbType.Int32, mintProductId)) cmd.Parameters.Add(DataLayer.CreateParameter( _ "@sProductName", DbType.String, mstrProductName)) cmd.Parameters.Add(DataLayer.CreateParameter( _ "@dtIntroduced", DbType.DateTime, mstrIntroduced)) cmd.Parameters.Add(DataLayer.CreateParameter( _ "@cCost", DbType.Decimal, mdecCost)) cmd.Parameters.Add(DataLayer.CreateParameter( _ "@cPrice", DbType.Decimal, mdecPrice)) cmd.Parameters.Add(DataLayer.CreateParameter( _ "@bDiscontinued", DbType.Decimal, mboolDiscontinued)) End Sub

Update Method The Update method is exactly the same as the Insert method, other than it builds a call to a stored procedure that does an update, or creates a dynamic SQL UPDATE statement. After building the call, it invokes the FillInParameters method to fill in the appropriate properties into the command object.

Fundamentals of N-Tier 4-17 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 110: Fundamentals of N Tier

Data Classes

C# public int Update() { IDbCommand cmd; string strSQL; // Check Business Rules Validate(); strSQL = "UPDATE tblProducts "; strSQL += "SET sProductName = @sProductName,"; strSQL += " dtIntroduced = @dtIntroduced, "; strSQL += " cCost = @cCost, "; strSQL += " cPrice = @cPrice, "; strSQL += " bDiscontinued = @bDiscontinued "; strSQL += " WHERE iProduct_id = @iProduct_id "; cmd = DataLayer.CreateCommand(strSQL, mstrConnectString); FillInParameters(cmd); return DataLayer.ExecuteSQL(cmd, true); } VB.NET Public Function Update() As Integer Dim cmd As IDbCommand Dim strSQL As String ' Check Business Rules Validate() strSQL = "UPDATE tblProducts " strSQL &= "SET sProductName = @sProductName," strSQL &= " dtIntroduced = @dtIntroduced, " strSQL &= " cCost = @cCost, " strSQL &= " cPrice = @cPrice, " strSQL &= " bDiscontinued = @bDiscontinued " strSQL &= " WHERE iProduct_id = @iProduct_id " cmd = DataLayer.CreateCommand(strSQL, mstrConnectString) FillInParameters(cmd) Return DataLayer.ExecuteSQL(cmd, True) End Function

Delete Method The Delete method works the same way as the Insert and the Update methods. It builds SQL with the appropriate parameters. In this case, the Delete is specifically for deleting a single row using the primary key of the table.

4-18 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 111: Fundamentals of N Tier

Validate Method

C# public int Delete() { IDbCommand cmd; string strSQL; strSQL = "DELETE FROM tblProducts "; strSQL += " WHERE iProduct_id = @iProduct_id "; cmd = DataLayer.CreateCommand(strSQL, mstrConnectString); FillInParameters(cmd); return DataLayer.ExecuteSQL(cmd, true); } VB.NET Public Function Delete() As Integer Dim cmd As IDbCommand Dim strSQL As String strSQL = "DELETE FROM tblProducts " strSQL &= " WHERE iProduct_id = @iProduct_id " cmd = DataLayer.CreateCommand(strSQL, mstrConnectString) FillInParameters(cmd) Return DataLayer.ExecuteSQL(cmd, True) End Function

Validate Method Each table in your database most likely has some business rules. These are rules like a certain column value must be filled in, if a date field is attempted to be inserted/updated, then it must be a valid date, etc. To enforce these simple business rules a Validate method is called from the Insert and Update methods. The design pattern for the validate method is fairly simple. Create a string with a description of each business rule failure delimited by a NewLine character. The business rules are delimited so they can be easily displayed as a group. It is best to show the user all business rule failures at one time as opposed to showing them a single business rule, have them fix that one, then display another one. The validate method in the data class should be rules that match what is in the database table, or very simple rules that could ultimately be code generated. For any more complicated rules, it is best to place those into the business rule class. The business rule class that you will create in the next

Fundamentals of N-Tier 4-19 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 112: Fundamentals of N Tier

Data Classes

chapter will also have a validate method. The validate method in the business rule class will call this validate method, then add on any additional business rules.

4-20 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 113: Fundamentals of N Tier

Validate Method

C# public virtual void Validate() { string strMsg = string.Empty; if (mstrProductName.Trim() == string.Empty) { strMsg += "Product Name Must Be Filled In" + Environment.NewLine; } if (mstrIntroduced.Trim() != string.Empty) { if (!IsDate(mstrIntroduced)) { strMsg += "Date Introduced is not a valid Date" + Environment.NewLine; } } if (strMsg != string.Empty) { throw new BusinessRuleException(strMsg); } } protected bool IsDate(string Value) { DateTime dt; bool boolRet; try { dt = Convert.ToDateTime(Value); boolRet = true; } catch { boolRet = false; } return boolRet; } VB.NET Public Overridable Sub Validate() Dim strMsg As String = String.Empty If mstrProductName.Trim() = String.Empty Then strMsg &= "Product Name Must Be Filled In" & _ Environment.NewLine End If If mstrIntroduced.Trim() <> String.Empty Then If Not IsDate(mstrIntroduced) Then strMsg &= "Date Introduced is not a valid Date" & _ Environment.NewLine End If End If If strMsg <> String.Empty Then Throw New BusinessRuleException(strMsg) End If

Fundamentals of N-Tier 4-21 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 114: Fundamentals of N Tier

Data Classes

End Sub

BusinessRuleException Class In the Validate method you will throw an instance of a BusinessRuleException class if there are business rules that have failed. This is a good practice because you can now catch this exception and know that it is a set of business rules that you can report on, and not a database exception, or other type of exception. It is recommended that you do not just return a Boolean value from the Validate method as a return value can be ignored by the programmer using your business object, but an exception can not be ignored. Below is the definition for this BusinessRuleException class.

4-22 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 115: Fundamentals of N Tier

BusinessRuleException Class

C# [Serializable] public class BusinessRuleException : ApplicationException { public BusinessRuleException() : base() { } public BusinessRuleException(string msg) : base(msg) { } public BusinessRuleException(string msg, Exception ex) : base(msg, ex) { } public string MessageForWebDisplay { get { return base.Message.Replace( Environment.NewLine, "<br>"); } } } VB.NET <Serializable()> Public Class BusinessRuleException Inherits ApplicationException Public Sub New() End Sub Public Sub New(ByVal Msg As String) MyBase.New(Msg) End Sub Public Sub New(ByVal Msg As String, ByVal ex As Exception) MyBase.New(Msg, ex) End Sub Public ReadOnly Property MessageForWebDisplay() As String Get Return MyBase.Message.Replace( _ Environment.NewLine, "<br>") End Get End Property End Class

Notice that you will create a few different constructors to accommodate the most common ways to create an exception object. In addition you add on a MessageForWebDisplay property. The reason why is that the design pattern used to delimit all of the business rules is to add a CRLF at the end of each string. This will be fine if you display the business rules in a message box on a windows form, but will not do anything when displayed in a label on a web form. Instead you will need to translate all CRLF's into a <br> tag. You might as well do this in this class as opposed to leaving it up to the consumer of your class.

Fundamentals of N-Tier 4-23 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 116: Fundamentals of N Tier

Data Classes

Multiple Tables In almost every database application you create you will most likely have to deal with multi-table joins and multi-table updates. Our one-class/one-table approach looks like it could restrict us from dealing with these situations. However, you will see that they will actually help us!

Multi-Table Joins In the case of a multi-table join, you simply need to add additional methods to your data class to perform dynamic SQL, call a view, or invoke a stored procedure that does the multi-table join. This method can then return a DataSet of the result of this join to the front end application to consume. You might also create specific classes just to return the data from a view or stored procedure. So dealing with multi-table joins is very easy in this model.

Multi-Table Updates Dealing with a multi-table update is also just as simple. In fact since you have each table wrapped up with all the code to perform insert, update and delete statements, all you need to do is use each of the different classes that you need in your multi-table update. A good way to handle this type of multi-table update is to use class aggregation. This means you create a class (called a Business Operation Class) that has a method into which you pass the appropriate data classes to perform the operation that you want. In Figure 4.2 the AddProductsSupplierCategory method would accept three parameters; a Products object, a Suppliers object, and a Categories object. This method would then be responsible for using each of the classes to perform its insert method. You could optionally wrap the calls within a transaction as well.

Figure 4.2. Use Class Aggregation to perform a multi-table modification.

4-24 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 117: Fundamentals of N Tier

Multiple Tables

The advantage to using this approach is you will not duplicate any SQL statements since each class knows how to insert, update and delete into its corresponding table. The class simply uses the appropriate method where the SQL is written to perform the operation on that table. No duplicate SQL! Within this same business operation class you could have another method that accepts just a Supplier and a Category object, and maybe performs an Update on each of those tables. Again, you are not duplicating any code you are using services that already exist as a part of the data classes.

Fundamentals of N-Tier 4-25 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 118: Fundamentals of N Tier

Data Classes

4-26 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Summary Creating a Data Class gives you many advantages; the code in your your front-end UI layer is more readable. Using the IntelliSense features of VS.NET means you do not have to remember column names. Column properties are strongly typed so you don't have to worry about passing in the wrong type of data. These classes can be reused from any front-end layer regardless of whether it is a web or windows forms application.

Page 119: Fundamentals of N Tier

Chapter 5

Business Classes The business class is where you put business rules to enforce that the data entered into the class is correct prior to sending it down to the database. To follow along and see the final example of the business class, load the solution file: NTierSample2CS\NTierSample2CS.sln or NTierSample2VB\NTierSample2VB.sln

Creating a Business Class There are a few different types of business classes that you might create. The first one will simply inherit from the data classes that you just learned how to build. Another type might be one that combines various business classes together to solve a particular business problem. Another type of business class might simply be one with a lot of methods to call other business classes's methods and perform some sort of data transformation on the data returned. There is no end to the types of business classes you might create. In this chapter you will learn how to create a business class that inherits from the data class. Where you named the data class ProductsDC, this class will simply be Products. This is the class that your users will instantiate an instance of. The data class should be one that can NOT be instantiated. You should mark your data classes as abstract or MustInherit.

Page 120: Fundamentals of N Tier

Business Classes

C# public class Products : ProductsDC { #region "Constructors" public Products() { base.ConnectString = AppConfig.ConnectString; } public Products(string ConnectString) { base.ConnectString = ConnectString; } #endregion } VB.NET Public Class Products Inherits ProductsDC #Region "Constructors" Public Sub New() ' Set the default connect string MyBase.ConnectString = AppConfig.ConnectString() End Sub Public Sub New(ByVal ConnectString As String) MyBase.ConnectString = ConnectString End Sub #End Region End Class

You will notice that there are a couple of different constructors for this business class. The default constructor assigns the ConnectString property of the data class to the connection string that is retrieved from the AppConfig class. The second constructor allows you to pass in a connection string. This can come in handy when you need to change the database from which you will be retrieving the Product information.

Validating Data The Validate method in the business class extends the business rules in the data class. The reason for the separation is you want the data class to be those rules that can be inferred from the schema of the table. This will help if you design, or use, a code generator to generate those data classes and the business rules. The Validate method in the business rule class will check data that can not be inferred from the schema. For example, maybe you need to ensure that a numeric field must be between a certain range, or that a date field must be

5-2 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 121: Fundamentals of N Tier

Validating Data

Fundamentals of N-Tier 5-3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

after or before a certain date. Below is an example of a validate method you might add to the Products class.

Page 122: Fundamentals of N Tier

Business Classes

C# public override void Validate() { string strMsg = string.Empty; try { // Check data class business rules base.Validate(); } catch (BusinessRuleException ex) { // Get Business Rule Messages strMsg = ex.Message; } //******************************************************* //* CHECK YOUR BUSINESS RULES HERE if (base.Cost < 0 || base.Cost > 999) { strMsg += "Cost must be greater than $0.00 and less than $1,000.00" + Environment.NewLine; } if (base.Price < 0 || base.Price > 999) { strMsg += "Price must be greater than $0.00 and less than $1,000.00" + Environment.NewLine; } if (base.Cost > base.Price) { strMsg += "Price must be greater than the Cost of the Product" + Environment.NewLine; } if (strMsg != String.Empty) { throw new BusinessRuleException(strMsg); } } VB.NET Public Overrides Sub Validate() Dim strMsg As String = String.Empty Try ' Check data class business rules MyBase.Validate() Catch ex As BusinessRuleException ' Get Business Rule Messages strMsg = ex.Message End Try '******************************************************* '* CHECK YOUR BUSINESS RULES HERE If MyBase.Cost < 0 Or MyBase.Cost > 999 Then strMsg &= "Cost must be greater than $0.00 and less than $1,000.00" & Environment.NewLine End If If MyBase.Price < 0 Or MyBase.Price > 999 Then strMsg &= "Price must be greater than $0.00 and less

5-4 Fundamentals of N-Tier than $1,000.00" & Environment.NewLine

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 123: Fundamentals of N Tier

Validating Data

Fundamentals of N-Tier 5-5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

End If If MyBase.Cost > MyBase.Price Then strMsg &= "Price must be greater than the Cost of the Product" & Environment.NewLine End If If strMsg <> String.Empty Then Throw New BusinessRuleException(strMsg) End If End Sub

Notice how this Validate method works. It first calls the Validate method in the data class. If this Validate method raises an exception you will trap it so you can gather the business rule messages and add them onto any business rules that might fail here in this method. This method will also create a new exception if any rules fail, and throw that exception back to the caller.

Page 124: Fundamentals of N Tier

Business Classes

5-6 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Summary In this chapter you learned to create business classes that inherit from data classes. The business rule class implements specific business rules that are in addition to the business rules implemented by the data classes.

Page 125: Fundamentals of N Tier

Chapter 6

Alternate N-Tier Implementation

Some programmers prefer to keep their data completely separate from their methods. An example of this can be found in the solution file: NTierSample4CS\NTierSample4CS.sln or NTierSample4VB\NTierSample4VB.sln. In this chapter you will see an approach to doing this by creating an additional class for just the properties of the Products class. This class will be called ProductsState. One of the benefits to this approach is the ProductsState class is the only class that needs to be passed back and forth between a client an server when you are doing .NET Remoting or some other physical separation of your N-Tier services.

ProductsState Class The idea here is to take all the properties and the Structure that you had created in the ProductsDC class before and move these all off to a separate class.

Page 126: Fundamentals of N Tier

Alternate N-Tier Implementation

C# [Serializable] public class ProductsState { public SchemaStruct Schema; #region "Constructors" public ProductsState() { Schema = new SchemaStruct(); Schema.ObjectName = "tblProducts"; Schema.ProductId = "iProduct_id"; Schema.ProductName = "sProductName"; Schema.Discontinued = "bDiscontinued"; Schema.Introduced = "dtIntroduced"; Schema.Price = "cPrice"; Schema.Cost = "cCost"; } #endregion #region "Private fields" private int mintProductId; private string mstrProductName; private string mstrIntroduced; private decimal mdecCost; private decimal mdecPrice; private bool mboolDiscontinued; #endregion #region "Column Properties" public int ProductId { get { return mintProductId; } set { mintProductId = value; } } public string ProductName { get { return mstrProductName; } set { mstrProductName = value; } } public string Introduced { get { return mstrIntroduced; } set { mstrIntroduced = value; } } public decimal Cost { get { return mdecCost; } set { mdecCost = value; } } public decimal Price { get { return mdecPrice; } set { mdecPrice = value; } } public bool Discontinued {

6-2 Fundamentals of N-Tier get { return mboolDiscontinued; }

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 127: Fundamentals of N Tier

ProductsState Class

set { mboolDiscontinued = value; } } #endregion #region "Schema Structure to return Object and Column Names" [Serializable] public struct SchemaStruct { public string ObjectName; public string ProductId; public string ProductName; public string Introduced; public string Cost; public string Price; public string Discontinued; } #endregion } VB.NET <Serializable()> Public Class ProductsState Public Schema As SchemaStruct #Region "Constructors" Public Sub New() Schema = New SchemaStruct() Schema.ObjectName = "tblProducts" Schema.ProductId = "iProduct_id" Schema.ProductName = "sProductName" Schema.Discontinued = "bDiscontinued" Schema.Introduced = "dtIntroduced" Schema.Price = "cPrice" Schema.Cost = "cCost" End Sub #End Region #Region "Private Member Variables" Private mintProductId As Integer Private mstrProductName As String Private mstrIntroduced As String Private mdecCost As Decimal Private mdecPrice As Decimal Private mboolDiscontinued As Boolean #End Region #Region "Column Properties" Property ProductId() As Integer Get Return mintProductId End Get Set(ByVal Value As Integer) mintProductId = Value End Set End Property Property ProductName() As String Get Return mstrProductName End Get Set(ByVal Value As String)

Fundamentals of N-Tier 6-3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 128: Fundamentals of N Tier

Alternate N-Tier Implementation

mstrProductName = Value End Set End Property Property Introduced() As String Get Return mstrIntroduced End Get Set(ByVal Value As String) mstrIntroduced = Value End Set End Property Property Cost() As Decimal Get Return mdecCost End Get Set(ByVal Value As Decimal) mdecCost = Value End Set End Property Property Price() As Decimal Get Return mdecPrice End Get Set(ByVal Value As Decimal) mdecPrice = Value End Set End Property Property Discontinued() As Boolean Get Return mboolDiscontinued End Get Set(ByVal Value As Boolean) mboolDiscontinued = Value End Set End Property #End Region #Region "Schema Structure to return Object and Column Names" <Serializable()> Public Structure SchemaStruct Public ObjectName As String Public ProductId As String Public ProductName As String Public Introduced As String Public Cost As String Public Price As String Public Discontinued As String End Structure #End Region End Class

6-4 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 129: Fundamentals of N Tier

ProductsDC Class

ProductsDC Class In the ProductsDC class you will now need to change the signatures of some of the methods. For example, the GetProduct method, the Load method, Validate, Insert, Update, and Delete methods all now have to pass in an instance of the ProductsState class. Below is an example of a couple of these methods.

Fundamentals of N-Tier 6-5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 130: Fundamentals of N Tier

Alternate N-Tier Implementation

C# public DataSet GetProduct(ProductsState prod) { DataSet ds = null; string strSQL; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + prod.ProductId.ToString(); ds = DataLayer.GetDataSet(strSQL, mstrConnectString); return ds; } public int Update(ProductsState prod) { IDbCommand cmd; string strSQL; // Check Business Rules Validate(prod); strSQL = "UPDATE tblProducts "; strSQL += "SET sProductName = @sProductName,"; strSQL += " dtIntroduced = @dtIntroduced, "; strSQL += " cCost = @cCost, "; strSQL += " cPrice = @cPrice, "; strSQL += " bDiscontinued = @bDiscontinued "; strSQL += " WHERE iProduct_id = @iProduct_id "; cmd = DataLayer.CreateCommand(strSQL, mstrConnectString); FillInParameters(prod, cmd); return DataLayer.ExecuteSQL(cmd, true); } VB.NET Public Function GetProduct(ByVal prod As ProductsState) As DataSet Dim ds As DataSet = Nothing Dim strSQL As String = String.Empty strSQL = "SELECT * FROM tblProducts" strSQL &= " WHERE iProduct_id = " + prod.ProductId.ToString() ' Retrieve DataTable from Data Layer ds = DataLayer.GetDataSet(strSQL, mstrConnectString) Return ds End Function Public Function Update(ByVal prod As ProductsState) As Integer Dim cmd As IDbCommand Dim strSQL As String ' Check Business Rules Validate(prod) strSQL = "UPDATE tblProducts " strSQL &= "SET sProductName = @sProductName,"

6-6 Fundamentals of N-Tier strSQL &= " dtIntroduced = @dtIntroduced, "

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 131: Fundamentals of N Tier

Products Class

strSQL &= " cCost = @cCost, " strSQL &= " cPrice = @cPrice, " strSQL &= " bDiscontinued = @bDiscontinued " strSQL &= " WHERE iProduct_id = @iProduct_id " cmd = DataLayer.CreateCommand(strSQL, mstrConnectString) FillInParameters(prod, cmd) Return DataLayer.ExecuteSQL(cmd, True) End Function

Look into the sample application for the complete code in the ProductsDC class.

Products Class There are very few changes you will need to make to the business class, other than in the Validate method you will need to pass in an instance of a ProductsState class. Other than that, this class can stay pretty much the same.

Fundamentals of N-Tier 6-7 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 132: Fundamentals of N Tier

Alternate N-Tier Implementation

C# public class Products : ProductsDC { public override void Validate(ProductsState prod) { string strMsg = string.Empty; try { // Check data class business rules base.Validate(prod); } catch (BusinessRuleException ex) { // Get Business Rule Messages strMsg = ex.Message; } //******************************************************* //* CHECK YOUR BUSINESS RULES HERE if (prod.Cost < 0 || prod.Cost > 999) { strMsg += "Cost must be greater than $0.00 and less than $1,000.00" + Environment.NewLine; } if (prod.Price < 0 || prod.Price > 999) { strMsg += "Price must be greater than $0.00 and less than $1,000.00" + Environment.NewLine; } if (prod.Cost > prod.Price) { strMsg += "Price must be greater than the Cost of the Product" + Environment.NewLine; } if (strMsg != String.Empty) { throw new BusinessRuleException(strMsg); } } } VB.NET Public Class Products Inherits ProductsDC Public Overrides Sub Validate(ByVal prod As ProductsState) Dim strMsg As String = String.Empty Try ' Check data class business rules MyBase.Validate(prod) Catch ex As BusinessRuleException ' Get Business Rule Messages strMsg = ex.Message End Try '******************************************************* '* CHECK YOUR BUSINESS RULES HERE

6-8 Fundamentals of N-Tier If prod.Cost < 0 Or prod.Cost > 999 Then

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 133: Fundamentals of N Tier

Products Class

strMsg &= "Cost must be greater than $0.00 and less than $1,000.00" & Environment.NewLine End If If prod.Price < 0 Or prod.Price > 999 Then strMsg &= "Price must be greater than $0.00 and less than $1,000.00" & Environment.NewLine End If If prod.Cost > prod.Price Then strMsg &= "Price must be greater than the Cost of the Product" & Environment.NewLine End If If strMsg <> String.Empty Then Throw New BusinessRuleException(strMsg) End If End Sub End Class

Fundamentals of N-Tier 6-9 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 134: Fundamentals of N Tier

Alternate N-Tier Implementation

6-10 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Summary In this chapter you learned an alternate method of creating N-Tier data and business classes. Feel free to experiment with different methods of N-Tier architecture.

Page 135: Fundamentals of N Tier

Chapter 7

N-Tier and Web Services

Web services can be used to expose our N-Tier classes to other applications such as a Windows Forms application. The solution file for the Windows Forms application is in NTierSample3CS\NTierSample3CS.sln or NTierSample3VB\NTierSample3VB.sln.

NOTE: You will need to build the Web Service from the classes provided in the NTierSampleWSCS\ or NTierSampleWSVB\ folders.

Build the Web Service To use the samples in this chapter you will need to perform the following steps. Open up VS.NET 2005. Click on File > New Web Site… Choose the ASP.NET Web Service template. Choose HTTP for the Location. Choose either Visual C# or Visual Basic for your language

Page 136: Fundamentals of N Tier

N-Tier and Web Services

For the name of the web service type in either http://Localhost/NTierSampleWSCS or http://Localhost/NTierSampleWSVB depending on your language.

Figure 7.1. Creating a new web service.

Click the OK button to create the web service. Delete the Service.asmx file. Delete the Service.cs or Service.vb file from the App_Code folder. Go to the location where you installed the eBook and locate the folder NTierSampleWSCS or NTierSampleWSVB. Copy in the Web.Config, the wsProducts.asmx and the wsProducts.cs/wsProducts.vb files into your new Web Service project. Move the source code file under the App_Code folder. Select Website > Add Reference… from the menu. Click on the Browse tab and browse to the location where you installed the eBook and locate the folder called Assemblies. Add all the files located in this folder.

7-2 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 137: Fundamentals of N Tier

Build the Web Service

Figure 7.2. Add all these DLLs to your web service.

Select Build > Build Web Site from the menu. Now you can open the NTierSample3CS\NTierSample3CS.sln or NTierSample3VB\NTierSample3VB.sln solution file. Open the App.Config file and make sure the location of the web service is the same location that you chose when you created the web service.

Figure 7.3. Make sure you are pointing to the correct location of the web service.

Now you should be able to run this sample. Continue on with the rest of this chapter to see how this was created.

Fundamentals of N-Tier 7-3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 138: Fundamentals of N Tier

N-Tier and Web Services

wsProducts Web Service When you design a web service that will return a business object, you need to ensure that your classes are all marked as Serializable. In addition you should create a method that either returns or accepts a business object. This ensures that when the WSDL is generated for the web service this class will be included in the definition. Below is a list of the various methods in the wsProducts.asmx file that is included in the NTierSampleWS solution. The things to notice in this web service is that the Connection String is fed to the Products class from a class in the web service. This class retrieves the connection string from the Web.Config file for this web service. Other than this, this web service uses the business class just like from any normal application that you have seen in this eBook.

7-4 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 139: Fundamentals of N Tier

wsProducts Web Service

C# [WebService(Namespace = "http://www.pdsa.com/eBooks/NTier/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class wsProducts : System.Web.Services.WebService { [WebMethod] public Products GetProductObject() { Products prod = new Products(); return prod; } [WebMethod] public DataSet GetProducts() { Products prod = new Products(); return prod.GetProducts(); } [WebMethod] public Products GetProduct(int ProductId) { Products prod = new Products(); if (!prod.Load(ProductId)) { prod = null; } return prod; } [WebMethod] public int Insert(Products prod) { int intRet = 0; prod.ConnectString = AppConfig.ConnectString; try { intRet = prod.Insert(); } catch { throw; } return intRet; } [WebMethod] public int Update(Products prod) { int intRet = 0; prod.ConnectString = AppConfig.ConnectString; try { intRet = prod.Update(); }

Fundamentals of N-Tier 7-5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 140: Fundamentals of N Tier

N-Tier and Web Services

catch { throw; } return intRet; } [WebMethod] public int Delete(Products prod) { int intRet = 0; prod.ConnectString = AppConfig.ConnectString; try { intRet = prod.Delete(); } catch { throw; } return intRet; } } VB.NET <WebService(Namespace:="http://tempuri.org/")> _ <WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _ <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ Public Class wsProducts Inherits System.Web.Services.WebService <WebMethod()> _ Public Function GetProductObject() As Products Dim prod As Products = New Products() Return prod End Function <WebMethod()> _ Public Function GetProducts() As DataSet Dim prod As Products = New Products() Return prod.GetProducts() End Function <WebMethod()> _ Public Function GetProduct(ByVal ProductId As Integer) _ As Products Dim prod As Products = New Products() If prod.Load(ProductId) = False Then prod = Nothing End If Return prod End Function <WebMethod()> _ Public Function Insert(ByVal prod As Products) As Integer

7-6 Fundamentals of N-Tier Dim intRet As Integer = 0

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 141: Fundamentals of N Tier

wsProducts Web Service

prod.ConnectString = AppConfig.ConnectString Try intRet = prod.Insert() Catch Throw End Try Return intRet End Function <WebMethod()> _ Public Function Update(ByVal prod As Products) As Integer Dim intRet As Integer = 0 prod.ConnectString = AppConfig.ConnectString Try intRet = prod.Update() Catch Throw End Try Return intRet End Function <WebMethod()> _ Public Function Delete(ByVal prod As Products) As Integer Dim intRet As Integer = 0 prod.ConnectString = AppConfig.ConnectString Try intRet = prod.Delete() Catch Throw End Try Return intRet End Function End Class

Fundamentals of N-Tier 7-7 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 142: Fundamentals of N Tier

N-Tier and Web Services

Consuming the Web Service If you look at the frmProduct form in the Windows Forms application that consumes the web service, you will see that not only will you define a Products class, but you will also need an instance of your web service. In C# you will need a separate field in the class, however, in VB.NET you can use the "My" namespace, and thus no separate variable is required for the Web Service.

C# Products mprod; wsProducts mws = new wsProducts(); VB.NET Private mprod As New Products

Loading the Products List Box If you look at the code in the frmProducts windows form, you can follow the logic starting with the Load event procedure. In this procedure, you must first instantiate an instance of the Products object. The web service is responsible for creating this instance. Next, you call the ProductsLoad() method in this windows form. This method will load all the Products into the list box on the form by calling the GetProducts() method on the web service.

7-8 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 143: Fundamentals of N Tier

Consuming the Web Service

C# private void frmProducts_Load(object sender, EventArgs e) { mprod = mws.GetProductObject(); ProductsLoad(); this.SetNormalButtonState(); } private void ProductsLoad() { try { // Use Schema class to return column names lstProducts.ValueMember = mprod.Schema.ProductId; lstProducts.DisplayMember roductName; = mprod.Schema.P lstProducts.DataSource = mws.GetProducts().Tables[0]; } catch (Exception ex) { AppException.Publish(ex); } } VB.NET Private Sub frmProducts_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load mprod = My.WebServices.wsProducts.GetProductObject() ProductsLoad() Me.SetNormalButtonState() End Sub Private Sub ProductsLoad() Try ' Use Schema class to return column names lstProducts.ValueMember = mprod.Schema.ProductId lstProducts.DisplayMember = mprod.Schema.ProductName lstProducts.DataSource = _ My.WebServices.wsProducts.GetProducts().Tables(0) Catch ex As Exception AppException.Publish(ex) End Try End Sub

Showing the Form Data When the user clicks on the list box full of products, you will grab the Product ID that is stored in the ValueMember property and make a call to the web service to grab the full Product information. You will make a call to the GetProduct() method passing in the Product ID as an integer.

Fundamentals of N-Tier 7-9 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 144: Fundamentals of N Tier

N-Tier and Web Services

C# private void lstProducts_SelectedIndexChanged(object sender, EventArgs e) { FormShow(); this.SetNormalButtonState(); } protected override void FormShow() { try { // Get Products Class mprod = mws.GetProduct( Convert.ToInt32(lstProducts.SelectedValue)); if (mprod != null) { lblProductID.Text = mprod.ProductId.ToString(); txtProductName.Text = mprod.ProductName; dtpDateIntroduced.Value = Convert.ToDateTime(mprod.Introduced); txtCost.Text = mprod.Cost.ToString(); txtPrice.Text = mprod.Price.ToString(); chkDiscontinued.Checked = mprod.Discontinued; } else { this.ClearFormControls(); } } catch (Exception ex) { AppException.Publish(ex); } } VB.NET Private Sub lstProducts_SelectedIndexChanged( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles lstProducts.SelectedIndexChanged FormShow() Me.SetNormalButtonState() End Sub Protected Overrides Sub FormShow() Try ' Get product object mprod = My.WebServices.wsProducts. _ GetProduct(Convert.ToInt32(lstProducts.SelectedValue)) If mprod IsNot Nothing Then lblProductID.Text = mprod.ProductId.ToString() txtProductName.Text = mprod.ProductName dtpDateIntroduced.Value = _ Convert.ToDateTime(mprod.Introduced) txtCost.Text = mprod.Cost.ToString() txtPrice.Text = mprod.Price.ToString() chkDiscontinued.Checked = mprod.Discontinued Else Me.ClearFormControls()

7-10 Fundamentals of N-Tier End If

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 145: Fundamentals of N Tier

Consuming the Web Service

Catch ex As Exception AppException.Publish(ex) End Try End Sub

Inserting Data To insert a new product (or to update or delete a product), you will call the Insert, Update or Delete methods on the web service and pass in the instance of the Product object filled with the appropriate data.

Fundamentals of N-Tier 7-11 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 146: Fundamentals of N Tier

N-Tier and Web Services

C# private bool DataAdd() { bool boolRet = false; string strMsg = string.Empty; FormMoveToDataClass(); try { if (mws.Insert(mprod) > 0) { boolRet = true; strMsg = "Successful Insert"; } else { boolRet = false; strMsg = "INSERT DID NOT SUCCEED"; } MessageBox.Show(strMsg); } catch (Exception ex) { AppException.Publish(ex); } return boolRet; } private void FormMoveToDataClass() { if (lblProductID.Text.Trim() != "") mprod.ProductId = Convert.ToInt32(lblProductID.Text); mprod.ProductName = txtProductName.Text; mprod.Introduced = dtpDateIntroduced.Value.ToString(); mprod.Cost = Convert.ToDecimal(txtCost.Text); mprod.Price = Convert.ToDecimal(txtPrice.Text); mprod.Discontinued = chkDiscontinued.Checked; } VB.NET Private Function DataAdd() As Boolean Dim boolRet As Boolean = False Dim strMsg As String = String.Empty FormMoveToDataClass() Try If My.WebServices.wsProducts.Insert(mprod) > 0 Then boolRet = True strMsg = "Successful Insert" Else boolRet = False strMsg = "INSERT DID NOT SUCCEED" End If

7-12 Fundamentals of N-Tier MessageBox.Show(strMsg)

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 147: Fundamentals of N Tier

Consuming the Web Service

Catch ex As Exception AppException.Publish(ex) End Try Return boolRet End Function Private Sub FormMoveToDataClass() If lblProductID.Text.Trim() <> "" Then mprod.ProductId = Convert.ToInt32(lblProductID.Text) End If mprod.ProductName = txtProductName.Text mprod.Introduced = dtpDateIntroduced.Value.ToString() mprod.Cost = Convert.ToDecimal(txtCost.Text) mprod.Price = Convert.ToDecimal(txtPrice.Text) mprod.Discontinued = chkDiscontinued.Checked End Sub

Fundamentals of N-Tier 7-13 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 148: Fundamentals of N Tier

N-Tier and Web Services

7-14 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Summary In this chapter you learned to put your N-Tier classes into a Web Service and then just use the Web Service from a front-end client application. This is one example of physically separating the tiers of an application. There are other methods that you can employ as well, but the theory and practice is very similar.

Page 149: Fundamentals of N Tier

Chapter 8

N-Tier and Transactions

If you are using the N-Tier data classes presented in this eBook and you now wish to put a bunch of them together into a transaction, you will need to modify these classes a little bit. The reason for a transaction is so either all of the inserts/updates/deletes succeed or they all fail. This chapter will outline the changes you need to make to the data classes presented thus far in this eBook as well as some additional classes you need to build. The samples that go with this chapter are \NTierSample2CS-Trans\NTierSample2CS-Trans.sln and \NTierSample2VB-Trans\NTierSample2VB-Trans.sln.

Create a "DC" Base Class The biggest change you will make to the ProductsDC class and any data class that you create in the future is you need to make it inherit from a "base class". This base class will provide some of the common functionality for each data class implementation as well as provide a common interface that can be used when it comes to implementing transactions.

Create a Transaction Enumeration To help support our generic Transaction class that will be created in this chapter, you first need an enumeration called DCTransType that will list the

Page 150: Fundamentals of N Tier

N-Tier and Transactions

operation that you wish to perform on one of your data classes. Here is the enumeration that is implemented in the DCBase class in the sample project.

C# public enum DCTransType { NA, Insert, Update, Delete } VB.NET Public Enum DCTransType NA Insert Update Delete End Enum

You will need to create some properties that each of your data classes can take advantage of; namely SQL, ConnectString, CommandObject and TransType. The SQL property is the last SQL statement that the data class submitted to the data layer. The ConnectString property is the connection string that the data classes uses. The CommandObject is the actual command object that was used to submit the SQL and the TransType is of the type DCTransType enumeration and will be used to tell the transaction class what operation to perform on this data class. Below is the code for this base class.

8-2 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 151: Fundamentals of N Tier

Create a "DC" Base Class

C# [Serializable] Public abstract class DCBase { private string mstrSQL; private string mstrConnectString; private IDbCommand mcmd; private DCTransType mTransType; public IDbCommand CommandObject { get { return mcmd; } set { mcmd = value; } } public DCTransType TransType { get { return mTransType; } set { mTransType = value; } } public string SQL { get { return mstrSQL; } set { mstrSQL = value; } } public string ConnectString { get { return mstrConnectString; } set { mstrConnectString = value; } } public virtual void PrepareForTransaction() { mcmd = DataLayer.CreateCommand(mstrSQL, mstrConnectString); } public abstract void Validate(); public abstract int Insert(); public abstract int Update(); public abstract int Delete(); } VB.NET <Serializable()> Public MustInherit Class DCBase Private mstrSQL As String = String.Empty Private mcmd As IDbCommand Private mTransType As DCTransType Private mstrConnectString As String Public Property ConnectString() As String Get Return mstrConnectString End Get Set(ByVal value As String) mstrConnectString = value End Set End Property Public Property SQL() As String

Fundamentals of N-Tier 8-3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 152: Fundamentals of N Tier

N-Tier and Transactions

Get Return mstrSQL End Get Set(ByVal value As String) mstrSQL = value End Set End Property Public Property TransType() As DCTransType Get Return mTransType End Get Set(ByVal value As DCTransType) mTransType = value End Set End Property Public Property CommandObject() As IDbCommand Get Return mcmd End Get Set(ByVal value As IDbCommand) mcmd = value End Set End Property Public Overridable Sub PrepareForTransaction() mcmd = DataLayer.CreateCommand(mstrSQL, mstrConnectString) End Sub Public MustOverride Sub Validate() Public MustOverride Function Insert() As Integer Public MustOverride Function Update() As Integer Public MustOverride Function Delete() As Integer End Class

Notice that the base class is marked as abstract/MustInherit. This means that you can NOT create an instance of this class. You can only inherit from it. The reason is because the Validate, Insert, Update and Delete methods are all marked as abstract/MustOverride. Each of these methods can not be implemented in the base class since they are each particular to the individual data class that inherits from this class. There is no code that can be written in the base class that makes sense for each of these methods. However, you need to define these method calls here because we need to know the "signature" of these methods in the Transaction class.

Create a Transaction Class The DCTrans class that you will find in the Sample application for this chapter is actually a Generic List class implementation. The use of Generics is very appropriate in this case since we have a group of classes that will all inherit

8-4 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 153: Fundamentals of N Tier

Create a Transaction Class

from the DCBase class. So it makes sense to create a list of generic DCBase classes. You can then use this list to add items to the list and iterate over this list of DCBase class objects to perform each data class's Insert, Update or Delete method.

Fundamentals of N-Tier 8-5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 154: Fundamentals of N Tier

N-Tier and Transactions

C# public class DCTrans : List<DCBase> { private string mstrConnectString; public string ConnectString { get { return mstrConnectString; } set { mstrConnectString = value; } } public DCTrans(string ConnectString) { mstrConnectString = ConnectString; } public void Execute() { IDbTransaction trn = null; IDbConnection cnn = null; try { cnn = DataLayer.CreateConnection(mstrConnectString); cnn.Open(); trn = cnn.BeginTransaction(); foreach (DCBase dc in this) { // Prepare Command Object for Transaction dc.ConnectString = mstrConnectString; dc.PrepareForTransaction(); // Set Transaction/Connection on Command Object dc.CommandObject.Connection = cnn; dc.CommandObject.Transaction = trn; switch (dc.TransType) { case DCTransType.Insert: dc.Insert(); break; case DCTransType.Update: dc.Update(); break; case DCTransType.Delete: dc.Delete(); break; default: break; } } trn.Commit(); } catch (Exception ex) { if (trn != null) { trn.Rollback(); }

8-6 Fundamentals of N-Tier

Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 155: Fundamentals of N Tier

Create a Transaction Class

throw ex; } finally { if (cnn != null) { cnn.Close(); cnn.Dispose(); } if (trn != null) { trn.Dispose(); } } } } VB.NET Public Class DCTrans Inherits System.Collections.Generic.List(Of DCBase) Private mstrConnectString As String Public Sub New(ByVal ConnectString As String) mstrConnectString = ConnectString End Sub Public Sub Execute() Dim trn As IDbTransaction = Nothing Dim cnn As IDbConnection = Nothing Try cnn = DataLayer.CreateConnection(mstrConnectString) cnn.Open() trn = cnn.BeginTransaction() For Each dc As DCBase In Me ' Prepare Command Object for Transaction dc.ConnectString = mstrConnectString dc.PrepareForTransaction() ' Set Transaction/Connection on Command Object dc.CommandObject.Connection = cnn dc.CommandObject.Transaction = trn Select Case dc.TransType Case DCTransType.Insert dc.Insert() Case DCTransType.Update dc.Update() Case DCTransType.Delete dc.Delete() End Select Next trn.Commit() Catch ex As Exception If trn IsNot Nothing Then trn.Rollback()

Fundamentals of N-Tier 8-7 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 156: Fundamentals of N Tier

N-Tier and Transactions

End If Throw ex Finally If cnn IsNot Nothing Then cnn.Close() cnn.Dispose() End If If trn IsNot Nothing Then trn.Dispose() End If End Try End Sub End Class

You can now see the reason for the DCBase class that you created before. This Generic List class is an implementation of the DCBase class. So the only types of classes that this list can accept are of the type DCBase. Notice in the Execute method that you can now use the Insert, Update and Delete methods of the DCBase class, but in reality it will call the Insert, Update or Delete method on the actual class that implements these methods.

Modify the ProductsDC Class You will now need to make a few changes to the ProductsDC class to work with the new DCBase class and the DCTransaction class that you have created. First off, the ProductsDC class needs to inherit from the DCBase class. Change your code to look like the following:

C# public abstract class ProductsDC : DCBase VB.NET Public MustInherit Class ProductsDC Inherits DCBase

Make the following changes to the signatures of the following methods.

8-8 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 157: Fundamentals of N Tier

Modify the ProductsDC Class

C# public override void Validate() public override int Insert() public override int Update() public override int Delete() VB.NET Public Overrides Sub Validate() Public Overrides Function Insert() As Integer Public Overrides Function Update() As Integer Public Overrides Function Delete() As Integer

You now need to modify the Insert, Update and Delete methods to use the CommandObject and ConnectString properties from the base class. You also need to check if you are in a transaction or not. If you are in a transaction, then you do not create a command object, instead you use the existing command object from the base class that has been initialized by the PrepareTransaction method. Below is each of these methods with the changes highlighted in bold.

Insert Method Changes Below are the changes, highlighted, for the Insert method.

Fundamentals of N-Tier 8-9 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 158: Fundamentals of N Tier

N-Tier and Transactions

C# public override int Insert() { // Check Business Rules Validate(); base.SQL = "INSERT INTO tblProducts(sProductName, "; base.SQL += " dtIntroduced, cCost, cPrice, bDiscontinued) "; base.SQL += "VALUES(@sProductName, @dtIntroduced, "; base.SQL += " @cCost, @cPrice, @bDiscontinued) "; if (base.TransType == DCTransType.NA) { base.CommandObject = DataLayer.CreateCommand(base.SQL, base.ConnectString); } else { base.CommandObject.CommandText = base.SQL; } FillInParameters(base.CommandObject); return DataLayer.ExecuteSQL(base.CommandObject, (base.TransType == DCTransType.NA)); } VB.NET Public Overrides Function Insert() As Integer ' Check Business Rules Validate() MyBase.SQL = "INSERT INTO tblProducts(sProductName, " MyBase.SQL &= " dtIntroduced, cCost, cPrice, bDiscontinued) " MyBase.SQL &= "VALUES(@sProductName, @dtIntroduced, " MyBase.SQL &= " @cCost, @cPrice, @bDiscontinued) " If MyBase.TransType = DCTransType.NA Then MyBase.CommandObject = DataLayer.CreateCommand(MyBase.SQL, _ MyBase.ConnectString) Else MyBase.CommandObject.CommandText = MyBase.SQL End If FillInParameters(MyBase.CommandObject) Return DataLayer.ExecuteSQL(MyBase.CommandObject, _ (MyBase.TransType = DCTransType.NA)) End Function

Update Method Changes Below are the changes, highlighted, for the Update method.

8-10 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 159: Fundamentals of N Tier

Modify the ProductsDC Class

C# public override int Update() { // Check Business Rules Validate(); base.SQL = "UPDATE tblProducts "; base.SQL += "SET sProductName = @sProductName,"; base.SQL += " dtIntroduced = @dtIntroduced, "; base.SQL += " cCost = @cCost, "; base.SQL += " cPrice = @cPrice, "; base.SQL += " bDiscontinued = @bDiscontinued "; base.SQL += " WHERE iProduct_id = @iProduct_id "; if (base.TransType == DCTransType.NA) { base.CommandObject = DataLayer.CreateCommand(base.SQL, base.ConnectString); } else { base.CommandObject.CommandText = base.SQL; } FillInParameters(base.CommandObject); return DataLayer.ExecuteSQL(base.CommandObject, (base.TransType == DCTransType.NA)); } VB.NET Public Overrides Function Update() As Integer ' Check Business Rules Validate() MyBase.SQL = "UPDATE tblProducts " MyBase.SQL &= "SET sProductName = @sProductName," MyBase.SQL &= " dtIntroduced = @dtIntroduced, " MyBase.SQL &= " cCost = @cCost, " MyBase.SQL &= " cPrice = @cPrice, " MyBase.SQL &= " bDiscontinued = @bDiscontinued " MyBase.SQL &= " WHERE iProduct_id = @iProduct_id " If MyBase.TransType = DCTransType.NA Then MyBase.CommandObject = DataLayer.CreateCommand(MyBase.SQL, _ MyBase.ConnectString) Else MyBase.CommandObject.CommandText = MyBase.SQL End If FillInParameters(MyBase.CommandObject) Return DataLayer.ExecuteSQL(MyBase.CommandObject, _ (MyBase.TransType = DCTransType.NA)) End Function

Delete Method Changes Below are the changes, highlighted, for the Delete method.

Fundamentals of N-Tier 8-11 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 160: Fundamentals of N Tier

N-Tier and Transactions

C# public override int Delete() { base.SQL = "DELETE FROM tblProducts "; base.SQL += " WHERE iProduct_id = @iProduct_id "; if (base.TransType == DCTransType.NA) { base.CommandObject = DataLayer.CreateCommand(base.SQL, base.ConnectString); } else { base.CommandObject.CommandText = base.SQL; } FillInParameters(base.CommandObject); return DataLayer.ExecuteSQL(base.CommandObject, (base.TransType == DCTransType.NA)); } VB.NET Public Overrides Function Delete() As Integer MyBase.SQL = "DELETE FROM tblProducts " MyBase.SQL &= " WHERE iProduct_id = @iProduct_id " If MyBase.TransType = DCTransType.NA Then MyBase.CommandObject = DataLayer.CreateCommand(MyBase.SQL, _ MyBase.ConnectString) Else MyBase.CommandObject.CommandText = MyBase.SQL End If FillInParameters(MyBase.CommandObject) Return DataLayer.ExecuteSQL(MyBase.CommandObject, _ (MyBase.TransType = DCTransType.NA)) End Function

The UI Layer The only change you need to make to the UI layer to be able to now use your ProductsDC class is to add a Reference to DataCommonVB/CS to UI Layer. The reason for this is the base class DLL must be referenced wherever it is used.

8-12 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 161: Fundamentals of N Tier

Test the Transaction

Test the Transaction To now test and see how everything works, you need to create a couple of instances of the Products class. Fill those instances with some data, and pass these instances into the DCTrans object. Below is the code in the frmMain form in the sample application that will create these two instances and fill them with data.

Fundamentals of N-Tier 8-13 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 162: Fundamentals of N Tier

N-Tier and Transactions

C# private void TransSample() { ProductTransSample pt = new ProductTransSample(); Products boProd1 = new Products(); Products boProd2 = new Products(); try { boProd1.Cost = 100; boProd1.Discontinued = false; boProd1.Introduced = DateTime.Now.ToString(); boProd1.Price = 200; boProd1.ProductName = "A New Product #1"; boProd2.Cost = 10; boProd2.Discontinued = false; boProd2.Introduced = DateTime.Now.ToString(); boProd2.Price = 20; boProd2.ProductName = "A New Product #2"; pt.InsertTwoProducts(boProd1, boProd2); MessageBox.Show("Transaction Completed Successfully"); } catch (Exception ex) { MessageBox.Show(ex.Message); } } VB.NET Private Sub TransSample() Dim pt As New ProductTransSample Dim boProd1 As New Products Dim boProd2 As New Products Try boProd1.Cost = 100 boProd1.Discontinued = False boProd1.Introduced = Now.ToString() boProd1.Price = 200 boProd1.ProductName = "A New Product #1" boProd2.Cost = 10 boProd2.Discontinued = False boProd2.Introduced = Now.ToString() boProd2.Price = 20 boProd2.ProductName = "A New Product #2" pt.InsertTwoProducts(boProd1, boProd2) MessageBox.Show("Transaction Completed Successfully") Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub

8-14 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 163: Fundamentals of N Tier

Test the Transaction

Once they are filled, they are then passed to another object called ProductTransSample that is in the NTierData project.

ProductTransSample Class This class will be responsible for creating an instance of the DCTrans class, setting the appropriate TransType on each object and then calling the Execute method on the DCTrans class.

C# public class ProductTransSample { public void InsertTwoProducts(Products boProd1, Products boProd2) { DCTrans trn; try { trn = new DCTrans(AppConfig.ConnectString); boProd1.TransType = DCTransType.Insert; trn.Add(boProd1); boProd2.TransType = DCTransType.Insert; trn.Add(boProd2); trn.Execute(); } catch (Exception ex) { throw ex; } } } VB.NET Public Class ProductTransSample Public Sub InsertTwoProducts(ByVal boProd1 As Products, _ ByVal boProd2 As Products) Dim trn As DCTrans Try trn = New DCTrans(AppConfig.ConnectString) boProd1.TransType = DCTransType.Insert trn.Add(boProd1) boProd2.TransType = DCTransType.Insert trn.Add(boProd2) trn.Execute() Catch ex As Exception Throw ex End Try End Sub End Class

Fundamentals of N-Tier 8-15 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 164: Fundamentals of N Tier

N-Tier and Transactions

8-16 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Summary In this chapter you learned to put your N-Tier classes into a transaction using ADO.NET. Creating a Generic List object helps you build this transaction class in such a way that you no longer need to write transaction code in your applications. This is a huge benefit in that it ensures that all your coders use the same approach to transactions. In addition you allow the classes to do all the work of creating, rolling back or committing the transaction.

Page 165: Fundamentals of N Tier

Index ADO.NET Interfaces ....... 3-4 Advantages of the Business Rules Server .............1-12

Alternate N‐Tier Implementation ........ 6-1

App.Config File .............2-13 AppConfig Class ...........2-13 Benefits of Data Classes 1-13 Build the Web Service .... 7-1 Business Classes ..... 1-4, 5-1 Creating .................... 5-1 Validating Data .......... 5-2

Business Rules Component ...............................1-12

BusinessRuleException Class ...............................4-22

ConfigCommon Assembly 2-22

Configuration Management ................................ 1-3

Consuming the Web Service ................................ 7-8

Create a Transaction Class 8-5

Create Command Method 3-7

CreateCommand Method 3-3

CreateConnection Method 3-3, 3-6

CreateDataAdapter Method ......................... 3-3, 3-8

CreateParameter Method 3-3, 3-7

Creating Data Classes .... 4-1 Creating N‐Tier Services . 2-1 Creating Reusable Components ............2-19

Data Access Service ....... 1-4

Data Classes ........ 1-11, 4-1 Creating .................... 4-1 Examples of Usage ..... 4-4 Methods ................... 4-3 Schema Structure ...... 4-9 Standard Properties ... 4-2

Data Common Assembly .. 2-22

Data Layer .................... 1-4 Data Layer Class .......... 2-16 Data Layer Component 1-11 Data Modification Methods

.............................. 4-15 Data Retrieval Methods 4-11 Database Service ........... 1-4 DataLayer Class Methods ................... 3-2 Overview .................. 3-2

DataLayer Component ... 3-1 DataLayer.CreateCommand Method .................... 3-9

DataLayer.CreateConnection Method .................... 3-8

DataLayer.CreateDataAdapter Method .............. 3-15

DataLayer.CreateParameter Method .................. 3-12

DataProvider Class ........ 3-6 Delete Method ........... 4-18 Disadvantages of the Business Rules Server 1-13

Exception Management . 1-3 ExecuteScalar ............. 3-24 ExecuteScalar Method ... 3-2 ExecuteSQL .......... 3-3, 3-27 FillInParameters Method . 4-

16 GetDataReader ........... 3-20 GetDataReader Method . 3-2

GetDataSet Method .. 3-2, 3-16

GetDataTable Method .. 3-2, 3-18

Goals of Business Classes . 1-13

Goals of Data Classes ... 1-13 Insert Method ............. 4-15 Load Method .............. 4-13 Log Management .......... 1-4 Map a Database Object to a Class ....................... 1-10

Match Columns to Properties ................. 4-6

Methods of the Data Classes ................................ 4-3

Methods of the DataLayer Class ......................... 3-2

Multiple Tables ........... 4-24 Multi‐Table Joins ......... 4-24 Multi‐Table Updates .... 4-24 N‐Tier ........................... 1-1 Advantages ............... 1-7 Approaches to development ......... 1-5

Disadvantages ........... 1-7 How to do it .............. 1-9 Implemenation .......... 1-5 What is it? ................. 1-1

N‐Tier and Transactions . 8-1 N‐Tier and Web Services 7-1 N‐Tier architecture ........ 1-1 NTierData Assembly .... 2-23 NTierSample1a Project 2-23 Products Class ............... 6-7 Products Class (Version 2) 2-

14 Products Class (Version 3) 2-

18

Page 166: Fundamentals of N Tier

Fundamentals of N-Tier

2 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

ProductsDC Class .......... 6-5 ProductsState Class ....... 6-1 ProductTransSample Class 8-

15 Returning all Rows .......4-11 Returning One Row by Primary Key .............4-12

Reusable Components ... 1-8 Sample 3 – Product Data Class ......................... 2-9

Schema Structure .......... 4-9 Security Management ... 1-3 Services ........................ 1-2

Standard Properties of the Data Classes .............. 4-2

Test the Transaction .... 8-13 The Business Rules Component ............. 1-12

The Data Classes ......... 1-11 The Data Layer Component

.............................. 1-11 The Product Class (Version 1) ........................... 2-11

Transaction Class........... 8-5 Transaction Enumeration 8-2 Transactions and N‐Tier . 8-1 Two‐Tier Sample ........... 2-2

typical N‐Tier structure .. 1-9 Update Method .......... 4-17 User Interface ............... 1-3 User Tracking ................ 1-3 Validate Method ......... 4-19 Web Service Consuming ................ 7-8

Web Services ................ 7-1 Why N‐Tier is a good choice

................................ 1-5 Why use Data Access Classes

................................ 1-6 Working with Multiple Tables ..................... 4-24

wsProducts Web Service 7-4

Page 167: Fundamentals of N Tier

Index

Fundamentals of N-Tier 3 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.

Page 168: Fundamentals of N Tier

Fundamentals of N-Tier

4 Fundamentals of N-Tier Copyright © 2006-2009 by PDSA, Inc.

All rights reserved. Reproduction is strictly prohibited.

Page 169: Fundamentals of N Tier

Index

Fundamentals of N-Tier 5 Copyright © 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.