Fundamentals of N Tier
Fundamentals of N Tier
Fundamentals of N-Tier
By: Paul D. Sheriff
4th Edition October 2009 Published By: PDSA, Inc. ISBN: 978-0-9793748-1-4
Introduction Written By: Technical Editors: Paul D. Sheriff 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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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.
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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
11
12
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Chapter 1
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 lets define what it is, then talk about why you want to implement it, and finally show an example of an N-Tier application.
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.
What is N-Tier?
Service User Interface Description 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. 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. 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. 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. 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. 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. 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. This is the database you choose to use such as SQL Server, Oracle, Sybase, Informix or Access.
Exception Management
User Tracking
Business Classes
Data Layer
Database Service
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
1-3
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Implementation of N-Tier
them on a remote computer and connect to them using .NET Remoting. Fourth, you could call them via Web Services.
1-5
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.
1-6
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Reusable Components
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
1-7
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
1-9
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.
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.
1-11
Overview of N-Tier Architecture correctly, this type of design can actually increase the scalability of an application.
1-12
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
1-13
1-14
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Chapter 2
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.
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-3
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.
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.
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-5
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 Private Function GetConnectString() As String
2-6
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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.
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-7
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 Private Function GetAProduct(ByVal ProductID As Integer) _
2-8
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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.
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-9
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
2-10
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-11
public DataSet GetProduct(int ProductID) { string strSQL; strSQL = "SELECT * FROM tblProducts"; strSQL += " WHERE iProduct_id = " + ProductID.ToString(); } return GetProducts(strSQL);
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" strSQL &= " WHERE iProduct_id = " & ProductID.ToString()
2-12
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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.
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
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-13
To use the ConfigurationManager class you need to add a reference to the System.Configuration.dll to your project.
2-14
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-15
2-16
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-17
2-18
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
2-19
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.
Figure 2.2. The C# solution with all the classes in separate DLLs.
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-21
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.
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
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
2-23
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.
2-24
Fundamentals of N-Tier
Copyright 2006-2009 PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Chapter 3
To follow along and see how the DataLayer class was built, you should load the Solution File: NTierDataTesterCS\NTierDataTesterCS.sln or NTierDataTesterVB\NTierDataTesterVB.sln
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.
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) GetDataTable(SQL, ConnectString) GetDataReader(SQL, ConnectString) ExecuteScalar(SQL, ConnectString) Return a DataSet given a SQL string and a Connection String. Return a DataTable given a SQL string and a Connection String. Return a DataReader given a SQL string and a Connection String. 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.
3-2
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
ExecuteSQL(cmd)
ExecuteSQL(SQL, ConnectString)
CreateCommand(SQL)
CreateCommand(SQL, ConnectString)
CreateDataAdapter(SQL, ConnectString)
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-3
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-5
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-7
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-9
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
3-10
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-11
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-13
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
3-14
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-15
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-17
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-19
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-21
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.
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"]); }
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-23
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-25
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-27
} 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 Throw
3-28
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-29
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-31
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() cnn.Dispose() End If
3-32
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
ExecuteSQL Method
If cmd IsNot Nothing Then cmd.Dispose() End If End Try End Sub
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-33
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)")) cmd.Parameters.Add(DataLayer.CreateParameter( _
3-34
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-35
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-37
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
3-39
3-40
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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 multitable 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.
Data Classes
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
ConnectString
<Column Properties>
Get[TableName](<PK>)
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-3
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. 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. 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. 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. 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.
Insert
Update
Delete
FillInParameters
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-5
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-7
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.
Schema Structure
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-9
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
4-10
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-11
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.
4-12
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-13
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-15
Data Classes
C# public int Insert() { IDbCommand cmd; string strSQL; // Check Business Rules Validate(); strSQL strSQL strSQL strSQL = "INSERT INTO tblProducts(sProductName, "; += " dtIntroduced, cCost, cPrice, bDiscontinued) "; += "VALUES(@sProductName, @dtIntroduced, "; += " @cCost, @cPrice, @bDiscontinued) ";
VB.NET Public Function Insert() As Integer Dim cmd As IDbCommand Dim strSQL As String ' Check Business Rules Validate() strSQL strSQL strSQL strSQL = "INSERT INTO tblProducts(sProductName, " &= " dtIntroduced, cCost, cPrice, bDiscontinued) " &= "VALUES(@sProductName, @dtIntroduced, " &= " @cCost, @cPrice, @bDiscontinued) "
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-17
Data Classes
C# public int Update() { IDbCommand cmd; string strSQL; // Check Business Rules Validate(); strSQL strSQL strSQL strSQL strSQL strSQL strSQL = "UPDATE tblProducts "; += "SET sProductName = @sProductName,"; += " dtIntroduced = @dtIntroduced, "; += " cCost = @cCost, "; += " cPrice = @cPrice, "; += " bDiscontinued = @bDiscontinued "; += " WHERE iProduct_id = @iProduct_id ";
VB.NET Public Function Update() As Integer Dim cmd As IDbCommand Dim strSQL As String ' Check Business Rules Validate() strSQL strSQL strSQL strSQL strSQL strSQL strSQL = "UPDATE tblProducts " &= "SET sProductName = @sProductName," &= " dtIntroduced = @dtIntroduced, " &= " cCost = @cCost, " &= " cPrice = @cPrice, " &= " bDiscontinued = @bDiscontinued " &= " WHERE iProduct_id = @iProduct_id "
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-19
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-21
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-23
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.
4-24
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
4-25
Data Classes
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.
4-26
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
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.
Validating Data
after or before a certain date. Below is an example of a validate method you might add to the Products class.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
5-3
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 than $1,000.00" & Environment.NewLine
5-4
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Validating Data
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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
5-5
Business Classes
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.
5-6
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Chapter 6
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.
} #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 { get { return mboolDiscontinued; }
6-2
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
6-3
6-4
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
6-5
public int Update(ProductsState prod) { IDbCommand cmd; string strSQL; // Check Business Rules Validate(prod); strSQL strSQL strSQL strSQL strSQL strSQL strSQL = "UPDATE tblProducts "; += "SET sProductName = @sProductName,"; += " dtIntroduced = @dtIntroduced, "; += " cCost = @cCost, "; += " cPrice = @cPrice, "; += " bDiscontinued = @bDiscontinued "; += " WHERE iProduct_id = @iProduct_id ";
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," strSQL &= " dtIntroduced = @dtIntroduced, "
6-6
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Products Class
strSQL strSQL strSQL strSQL &= &= &= &= " " " " cCost = @cCost, " cPrice = @cPrice, " bDiscontinued = @bDiscontinued " 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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
6-7
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 If prod.Cost < 0 Or prod.Cost > 999 Then
6-8
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
6-9
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.
6-10
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Chapter 7
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.
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
7-3
7-4
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
[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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
7-5
[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.DesignerGenerat ed()> _ 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 Dim intRet As Integer = 0
7-6
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
7-7
7-8
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
private void ProductsLoad() { try { // Use Schema class to return column names lstProducts.ValueMember = mprod.Schema.ProductId; lstProducts.DisplayMember = mprod.Schema.ProductName; 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
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
7-9
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(); }
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() End If
7-10
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
7-11
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 MessageBox.Show(strMsg)
7-12
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
7-13
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.
7-14
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Chapter 8
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
8-3
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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
8-5
8-6
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
8-7
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.
8-8
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
void Validate() int Insert() int Update() int Delete() Sub Validate() Function Insert() As Integer Function Update() As Integer 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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
8-9
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
"
8-10
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
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 MyBase.SQL MyBase.SQL MyBase.SQL MyBase.SQL MyBase.SQL MyBase.SQL = "UPDATE tblProducts " &= "SET sProductName = @sProductName," &= " dtIntroduced = @dtIntroduced, " &= " cCost = @cCost, " &= " cPrice = @cPrice, " &= " bDiscontinued = @bDiscontinued " &= " 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
8-11
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.
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
8-13
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.
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
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
8-15
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.
8-16
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Index
ADO.NETInterfaces ....... 3-4 AdvantagesoftheBusiness RulesServer .............1-12 AlternateNTier Implementation ........ 6-1 App.ConfigFile.............2-13 AppConfigClass ...........2-13 BenefitsofDataClasses 1-13 BuildtheWebService.... 7-1 BusinessClasses ..... 1-4, 5-1 Creating.................... 5-1 ValidatingData .......... 5-2 BusinessRulesComponent ...............................1-12 BusinessRuleExceptionClass ...............................4-22 ConfigCommonAssembly 222 ConfigurationManagement ................................ 1-3 ConsumingtheWebService ................................ 7-8 CreateaTransactionClass 85 CreateCommandMethod 37 CreateCommandMethod 33 CreateConnectionMethod 33, 3-6 CreateDataAdapterMethod ......................... 3-3, 3-8 CreateParameterMethod 33, 3-7 CreatingDataClasses .... 4-1 CreatingNTierServices . 2-1 CreatingReusable Components ............2-19 DataAccessService ....... 1-4 DataClasses ........ 1-11, 4-1 Creating .................... 4-1 ExamplesofUsage ..... 4-4 Methods ................... 4-3 SchemaStructure ...... 4-9 StandardProperties ... 4-2 DataCommonAssembly .. 222 DataLayer .................... 1-4 DataLayerClass .......... 2-16 DataLayerComponent 1-11 DataModificationMethods .............................. 4-15 DataRetrievalMethods 4-11 DatabaseService ........... 1-4 DataLayerClass Methods ................... 3-2 Overview .................. 3-2 DataLayerComponent ... 3-1 DataLayer.CreateCommand Method .................... 3-9 DataLayer.CreateConnection Method .................... 3-8 DataLayer.CreateDataAdapt erMethod .............. 3-15 DataLayer.CreateParameter Method .................. 3-12 DataProviderClass ........ 3-6 DeleteMethod ........... 4-18 Disadvantagesofthe BusinessRulesServer1-13 ExceptionManagement . 1-3 ExecuteScalar ............. 3-24 ExecuteScalarMethod ... 3-2 ExecuteSQL.......... 3-3, 3-27 FillInParametersMethod . 416 GetDataReader ........... 3-20 GetDataReaderMethod . 3-2 GetDataSetMethod.. 3-2, 316 GetDataTableMethod .. 3-2, 3-18 GoalsofBusinessClasses . 113 GoalsofDataClasses ... 1-13 InsertMethod ............. 4-15 LoadMethod .............. 4-13 LogManagement .......... 1-4 MapaDatabaseObjecttoa Class ....................... 1-10 MatchColumnsto Properties ................. 4-6 MethodsoftheDataClasses ................................ 4-3 MethodsoftheDataLayer Class ......................... 3-2 MultipleTables ........... 4-24 MultiTableJoins ......... 4-24 MultiTableUpdates .... 4-24 NTier ........................... 1-1 Advantages ............... 1-7 Approachesto development ......... 1-5 Disadvantages ........... 1-7 Howtodoit .............. 1-9 Implemenation .......... 1-5 Whatisit? ................. 1-1 NTierandTransactions . 8-1 NTierandWebServices 7-1 NTierarchitecture ........ 1-1 NTierDataAssembly .... 2-23 NTierSample1aProject 2-23 ProductsClass ............... 6-7 ProductsClass(Version2) 214 ProductsClass(Version3) 218
Fundamentals of N-Tier ProductsDCClass .......... 6-5 ProductsStateClass ....... 6-1 ProductTransSampleClass 815 ReturningallRows .......4-11 ReturningOneRowby PrimaryKey .............4-12 ReusableComponents ... 1-8 Sample3ProductData Class......................... 2-9 SchemaStructure .......... 4-9 SecurityManagement ... 1-3 Services........................ 1-2 StandardPropertiesofthe DataClasses .............. 4-2 TesttheTransaction .... 8-13 TheBusinessRules Component ............. 1-12 TheDataClasses ......... 1-11 TheDataLayerComponent .............................. 1-11 TheProductClass(Version 1) ........................... 2-11 TransactionClass........... 8-5 TransactionEnumeration 8-2 TransactionsandNTier . 8-1 TwoTierSample ........... 2-2 typicalNTierstructure .. 1-9 UpdateMethod .......... 4-17 UserInterface ............... 1-3 UserTracking ................ 1-3 ValidateMethod ......... 4-19 WebService Consuming ................ 7-8 WebServices ................ 7-1 WhyNTierisagoodchoice ................................ 1-5 WhyuseDataAccessClasses ................................ 1-6 WorkingwithMultiple Tables ..................... 4-24 wsProductsWebService 7-4
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Index
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Fundamentals of N-Tier
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.
Index
Fundamentals of N-Tier
Copyright 2006-2009 by PDSA, Inc. All rights reserved. Reproduction is strictly prohibited.