Microsoft's .NET Framework is gaining acceptance among solution
developers who traditionally rely on Microsoft development tools. Some of these
developers want to take advantage of the robust transaction support, tight
security, reliable messaging, and workload and system management that are unique
strengths of iSeries servers. It seems that the combination of .NET Framework
and a powerful database server, such as DB2 UDB for iSeries, can result in a
scalable software solution that's very appealing to the development
community.
Typically, .NET applications use ADO.NET classes to access and
manipulate data stored in databases. ADO.NET is an evolutionary improvement to
the Microsoft ActiveX Data Objects (ADO) programming interface. This article
discusses how to efficiently implement the ADO.NET programming model to access
the iSeries database. I'll also illustrate using DB2 add-ins for Visual Studio
.NET to significantly speed up the development process.
The .NET
applications rely on services of .NET providers for communication with back-end
database servers. A .NET data provider is used for connecting to a database,
executing commands, and retrieving results. Currently, four providers can be
used to access DB2 UDB from .NET applications:
- V5R3 iSeries Access .NET provider implemented by IBM Rochester
- The DB2 for Linux, UNIX, and Windows (LUW) V8.2 (formerly Stinger) .NET provider implemented by IBM software group
- ODBC .NET Data Provider, the Microsoft-supplied ODBC bridge provider using the iSeries Access for Windows ODBC driver for underlying database connectivity
- OLE DB .NET Data Provider, the Microsoft-supplied OLE DB bridge provider using the iSeries Access OLE DB driver for underlying database connectivity
The OLE DB .NET provider and the ODBC .NET provider are called "unmanaged" providers, meaning that the code has been compiled directly into a binary executable, so it runs outside of the .NET framework. The unmanaged providers usually deliver suboptimal performance because they involve jumping in and out of the .NET Framework environment for every interface call. On the other hand, the iSeries Access .NET provider and the DB2 .NET provider are called "managed" providers. Managed providers are compiled into .NET assemblies that can be executed in the context of the .NET Framework. In this case, the application/provider interaction does not require API calls that cross the .NET Framework boundary.
The iSeries Access .NET Provider
The iSeries Access .NET provider is sometimes called
"native iSeries .NET provider." It uses a highly optimized, proprietary protocol
to communicate with the iSeries database server job. The database server jobs
are called QZDASOINIT, and they typically run in the QUSRWRK subsystem. A
database server job runs the SQL requests on behalf of the .NET provider. More
precisely, when a .NET application submits an SQL statement, the provider passes
the statement to a server job that, in turn, calls the DB2 runtime to execute
the statement. The results are then reformatted and returned to the caller.
The provider supports a full set of .NET data types and provides rich
SQL functionality to enable applications to easily access and process data
stored on your iSeries. These features are currently implemented in the iSeries
Access .NET provider:
- SQL (INSERT, UPDATE, DELETE, SELECT)
- Commitment control
- Connection pooling
- SQL naming
- Unicode
- Threads
- IASPs (multiple databases)
- Stored procedure support
Two additional features have been recently shipped in the iSeries Access SI15176 Service Pack:
- System naming ( / )
- Large Objects (LOBs)
The following functionality is not yet available through the provider:
- SQL package support (extended dynamic SQL)
- Data links
- User-defined types
- Record-level access
- CMD/PGM call
- Data queues
Prerequisites
The iSeries Access .NET provider ships with iSeries Access V5R3. It supports the iSeries servers running i5/OS V5R3 and OS/400 V5R2. Note that some functionality, such as 63-digit decimal precision, is available only on V5R3 servers. For the best code stability, you should always load the latest database group PTF on the iSeries system (SF99503 for V5R3 or SF99502 for V5R2) as well as the most recent V5R3 iSeries Access Service Pack. As with any other managed provider, the .NET Framework needs to be already installed on your PC.
Sample Application
This section covers implementation details for a
sample Visual Basic (VB) .NET application called DB2i5Access (download here).
The program uses the iSeries Access .NET provider to obtain a database
connection and to manipulate database rows in a DB2 table called STAFF. The
table is part of the sample DB2 schema, and it can be created with this stored
procedure call:
The procedure has been shipped with i5/OS and OS/400 since
V5R1.
The application works in the disconnected mode. In the disconnected
mode, a DataAdapter object retrieves data from the database and stores it in a
DataSet object that serves as a local data cache. A .NET application can use the
data cached in a DataSet without requiring multiple trips to the database. A
DataAdapter can also implicitly update the central database with changes made to
the DataSet. We used Microsoft Visual Studio .NET 2003 to develop the sample
application. The main application dialog is shown in Figure 1.
Figure 1: Here's your sample DB2i5Access application.
Overview
After a new project is created, a reference to the
assembly that contains the iSeries Access .NET provider needs to be added to the
solution. You can accomplish this task by right-clicking the References icon in
Solutions Explorer and selecting Add Reference. In the Add Reference dialog,
find the IBM DB2 for iSeries .NET provider component and click Select and then
OK. It is also very useful to add the iSeries Access provider classes to the
Visual Studio Toolbox. To do so, select Tools > Add/Remove Toolbox items, and
on the Customize Toolbox dialog, check the iSeries provider-specific classes
(iDB2Command, iDB2CommandBuilder, iDB2Connection, and iDB2DataAdapter). Now,
drag and drop the iSeries provider classes onto the design pane. Currently, the
iSeries .NET provider does not provide wizards to allow you to visually
configure the classes required to access and modify DB2 data, so most of the
tasks need to be accomplished programmatically. As mentioned, in the
disconnected mode, there are several ADO.NET objects that cooperate to provide
the database access mechanism. The required objects and their relationships are
illustrated in Figure 2.
Figure 2: In the disconnected mode, ADO.NET objects provide the database
access mechanism.
Let's have a closer look at the most critical source
code fragments that need to be manually implemented by a database developer.
iDB2Connection
An ADO.NET connection object establishes a connection
to a specific data source. For instance, the iDB2Connection object connects to
DB2 UDB for iSeries. To successfully open an iSeries connection, security
information such as user ID and password must be passed to the database.
Typically, a ConnectionString property provides this information. The following
code shows how to use the iDB2Connection object:
Me.conMyiSeries = New iDB2Connection
Me.conMyiSeries.ConnectionString = "DataSource=PWD1;Connection
Timeout=30;UserID=db2user;Password=db2pwd"
Note the syntax of the keywords on the connection string. The keywords are provider-specific and usually differ for different databases. In addition to the authentication info, the iSeries connection string may also specify other connection parameters, such as Default Collection (schema), Default Isolation Level, Pooling, etc. Consult the DB2 UDB for iSeries .NET provider Technical Reference for a complete list of supported parameters. The above code sample, for illustration purposes, has the user ID and password hard-coded in the application program. In fact, our sample application uses TextBox controls to allow the user to specify valid credentials at runtime.
iDB2Command
An iDB2Command object executes action statements and submits queries. It has several properties that specify the execution context:
- Connection--A reference to a connection object used for communication with the database
- CommandType--Set to one of the following values: Text, StoredProcedure, TableDirect
- CommandText--Contains the text of an SQL statement or the name of the stored procedure to be executed
- Parameters--Values required by parameterized SQL statement or stored procedure
In our scenario, we instantiate four iDB2Command
objects that the iDB2DataAdapter needs. They represent the four fundamental SQL
statements: SELECT, INSERT, UPDATE, and DELETE. The following code illustrates
how to create and set up iDB2Command objects:
Me.cmdSelect = New iDB2Command
Me.cmdUpdate = New iDB2Command
Me.cmdDelete = New iDB2Command
Me.cmdInsert = New iDB2Command
'cmdSelect
Me.cmdSelect.CommandText = "SELECT * FROM STAFF" [1]
Me.cmdSelect.Connection = Me.conMyiSeries
'cmdDelete
Me.cmdDelete.CommandText = "DELETE FROM DB2USER.STAFF WHERE ID = ?" [2]
Me.cmdDelete.Connection = Me.conMyiSeries
Me.cmdDelete.Parameters.Add(New
iDB2Parameter("ID",
_
iDB2DbType.iDB2SmallInt,
5, System.Data.ParameterDirection.Input,
_
False,
CType(0,
Byte),
CType(0,
Byte),
"ID", _
System.Data.DataRowVersion.Original, Nothing))
For clarity, UPDATE and INSERT statements are not listed here. At [1],
the SELECT command is set up to retrieve all rows from the STAFF table over the
conMyiSeries connection. At [2], a parameterized DELETE statement is specified.
The statement uses a parameter marker (?) as a placeholder for the ID value that
will be provided at runtime. The parameter markers foster a more efficient
processing of SQL statements because a parameterized statement can be prepared
once and executed many times. The DELETE statement requires an iDB2Parameter
object that is bound to the parameter marker. Note that the original value of
the ID column is used to locate the staff member row in DB2. The statement
unconditionally deletes the corresponding row in the database. In other words,
it does not take into account whether the original row image has been modified
by other users. This is probably not the best business practice. In a more
realistic scenario, we would probably use the so-called "optimistic locking
approach," where we would check if the local image still matched the image on
the database server before deleting a row. This could be accomplished with a
DELETE statement modified as shown
below:
DELETE FROM DB2USER.STAFF
WHERE ( (ID = ?)
AND
((CAST( ? AS VARCHAR(9)) IS NULL AND CAST(NAME AS VARCHAR(9)) IS NULL)
OR (CAST(NAME AS
VARCHAR(9)) = ?))
AND
((CAST( ? AS SMALLINT) IS NULL AND CAST(DEPT AS SMALLINT) IS NULL)
OR (CAST(DEPT AS SMALLINT)
= ?))
AND ((CAST( ? AS
CHAR(5)) IS NULL AND CAST(JOB AS CHAR(5)) IS NULL)
OR (CAST(JOB AS CHAR(5)) =
?)) AND ((CAST( ? AS SMALLINT) IS NULL
AND CAST(YEARS AS
SMALLINT) IS NULL) OR (CAST(YEARS AS SMALLINT) = ?))
AND ((CAST( ? AS DEC(7,2))
IS NULL AND CAST(SALARY AS DEC(7,2)) IS NULL)
OR (CAST(SALARY AS
DEC(7,2)) = ?))
AND
((CAST( ? AS DEC(7,2)) IS NULL AND CAST(COMM AS DEC(7,2)) IS NULL)
OR (CAST(COMM AS DEC(7,2))
= ?)) )
The same approach could also be applied to the UPDATE
statement.
iDB2DataAdapter
An iDB2DataAdapter is a bridge that links an iSeries
database and a local data cache represented by a DataSet object. The
iDB2DataAdapter encapsulates a database connection and a set of SQL commands
that are used to retrieve the data from the database and update the database
with the changes made to the DataSet. Once the connection and the command's
objects have been defined, the iDB2DataAdapter configuration is pretty
straightforward. See the following code:
Me.daStaff.InsertCommand = Me.cmdInsert
Me.daStaff.SelectCommand = Me.cmdSelect
Me.daStaff.UpdateCommand = Me.cmdUpdate
Error Handling
Exception handling in ADO.NET complies with the
Common Language Runtime (CLR) specification and is consistent across all
supported .NET programming languages. In CLR, the control structure is the
Try-Catch-Finally statement. An error that occurs in the Try block is handled in
the Catch block. The iSeries Access .NET provider implements the iDB2Exception
object that can be used to monitor for the provider-specific exceptions. The
provider throws an iDB2Exception whenever it encounters an error generated from
the iSeries host server or from the underlying iSeries Access APIs .
The
iDB2Exception contains a collection of one or more iDB2Error objects. The
DB2i5Access application uses the iDB2Exception to intercept database errors that
can occur at the time the rows are loaded into the DataSet or the updates are
submitted to the database. For example, the btnLoad_Click method monitors for
iSeries-specific errors and shows an appropriate error message box when
unexpected conditions are encountered. See the following code fragment for
implementation details:
Private Sub btnLoad_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btnLoad.Click
Try
dsStaff.Clear()
daStaff.Fill(dsStaff.STAFF)
Catch Xcp As iDB2Exception
Dim sqle As iDB2Error
For Each sqle In Xcp.Errors
MessageBox.Show(Xcp.ToString(), "DB2 Server Error", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
Next
End Try
End Sub
Troubleshooting
Troubleshooting a multi-tier application is usually not trivial because the developer needs to deal with software components that reside in different operating system environments. Two tools can be particularly useful for problem resolution:
- The job log messages for a database server job usually provide enough details to isolate DB2 UDB for iSeries runtime issues.
- The iSeries Access .NET provider trace utility provides granular information about the data flow between the .NET application and the iSeries server job.
Job Log
The iSeries Access .NET provider communicates with a
corresponding iSeries server job. This server job runs the SQL requests on
behalf of the .NET client. The iSeries database server jobs are called
QZDASOINIT, and they run in the QUSRWRK subsystem. At any given time, many
database server jobs may be active on the system, so the first step is to
identify the job that serves the particular client connection, which is easily
done by running the following CL command:
Here, DB2USER is the user profile that is used to connect to the iSeries
server. This approach works even in the disconnected mode. Thanks to connection
pooling, a corresponding job is held even after the iDB2DataAdapter has closed
the database connection.
Sometimes, it is also useful to include debug
messages in the job log. The debug messages are informational messages written
to the job log about the implementation of a query. They describe query
implementation methods, such as use of indexes, join order, open data path (ODP)
implementation (reusable versus non-reusable), and so on. The easiest way to
instruct the iSeries to include the debug messages is to set the Trace property
on the connection string of the iDB2Connection object:
Me.conMyiSeries.ConnectionString =
"DataSource=TPLX;UserID=db2user;Password=db2pwd;Trace=StartDebug"
A sample of the job log messages with debug messages enabled is shown
below:
**** Starting optimizer debug message
for query .
Unable to retrieve query
options file.
All access paths were
considered for file STAFF.
Arrival
sequence access was used for file
STAFF.
**** Ending debug message for
query .
ODP
created.
Blocking used for
query.
Cursor C000001
opened.
DESCRIBE of prepared
statement S000001 completed.
35 rows
fetched from cursor C000001.
Cursor
C000001 was closed.
Provider Trace Utility
The iSeries Access .NET provider ships with a
tracing utility, which can be used to collect detailed client-side traces. The
.NET provider tracing is turned off by default. It can be enabled by calling the
CWBMPTRC program on Windows. Here's an example of how to switch on
tracing:
The trace file by default is named IDB2TRACE.TXT and is stored in the C:Documents and SettingsAll UsersDocumentsIBMClient Access directory.
DB2 UDB V8.2 .NET Provider
The recently announced IBM DB2 UDB for Linux, UNIX, Windows (LUW) V8.2 (formerly "Stinger") provides a high level of integration with Visual Studio .NET, improved performance for .NET applications, and significant enhancements for stored procedure and user-defined function developers. This new version also introduced .NET connectivity and simplified programming facilities for building applications that work with DB2 UDB for iSeries servers. The iSeries support relies on services provided by DB2 Connect. DB2 Connect is offered as separate Windows licensed products:
- Enterprise Edition
- Personal Edition
- DB2 Connect Unlimited Edition
- Application Server Edition
The DB2 Connect Enterprise
Edition functionality is also contained in DB2 Enterprise Server Edition for
Windows.
DB2 Connect implements the Distributed Relational Database
Architecture (DRDA) to access data stored in DB2 UDB for iSeries and other
DRDA-compliant database servers. A .NET client that uses DB2 .NET provider
connects to a DRDA server job on iSeries. The iSeries DRDA server jobs are
called QRWTSRVR and they run in the QUSRWRK subsystem. You can use the DB2
Configuration Assistant (db2ca) on the Windows client to configure the required
DRDA connection to the iSeries.
Once the connection has been configured,
you can take advantage of the DB2 Visual Studio add-in. The DB2 add-in provides
a rich collection of functions that support DB2 servers directly in Visual
Studio's IDE. These are the most important features:
- DB2 .NET Managed Provider
- Solution Explorer--create and manage DB2 projects
- IBM Explorer--view DB2 server catalog information and client side ADO.NET code generation
- SQL Editor--edit DB2 scripts (includes syntax colorization and statement auto-completion)
- DB2 Tools Toolbar
In addition, the DB2 add-in provides
wizards to visually configure DB2 commands, adapters, etc. For example, the DB2
Data Adapter Configuration wizard allows you to define SQL statements and stored
procedure calls. It also allows you to define the structure of the result set
and map SQL command parameters to columns in the result set.
To
illustrate how a wizard simplifies the developer's work, assume that we decided
to use the DB2DataAdapter in our DB2i5Access sample application. In this case,
instead of programmatically defining the database connection, required SQL
statements, and statements parameters, we drag a DB2DataAdapter object from the
Visual Studio Toolbox and drop it on the form designer. This launches the
configuration wizard. First, we provide the iSeries database connection
information. Remember, the DRDA connection to the iSeries needs to exist at this
time (use db2ca to configure it). Then, we specify what SQL commands are
required by the adapter. In addition to the default SELECT, we choose INSERT,
UPDATE, and DELETE. Next, we specify the SELECT statement as shown in Figure
3.
Figure 3: Define SQL statements with the DB2 Data Adapter Configuration
Wizard.
At this point, the wizard also allows you to validate the
statement on the back-end iSeries database as well as provide the table to
dataset mapping. Similarly, we define other SQL statements. We use the wizard's
Generate function to automatically generate the statement text along with all
required parameters. For example, the Generate function used on the UPDATE
statement dialog generates the UPDATE statement that implements the optimistic
locking. This is shown in Figure 4.
Figure 4: Generate an Update statement.
At the end of the
configuration process, the wizard creates two objects associated with the VB
Form class: db2Connection1 and DB2DataAdapter1. The wizard also generates the
necessary code in the "Windows Form Designer generated code" region. The
developer should not modify this code. The wizard should be used to make any
changes in the DB2DataAdapter configuration.
Hopefully, this short
walkthrough has given you a taste of the truly unique capabilities of the DB2
add-in. See DB2
Development Add-In Technical Preview for more information.
What Provider Is Right for You?
Choosing the appropriate .NET data provider for your
application depends on your environment. If you plan to access only the iSeries
database, the DB2 UDB for iSeries .NET provider should be your choice. This
managed provider will provide better performance than using the
System.Data.OleDb provider to bridge to the iSeries Access OLE DB provider or
using the Microsoft.Data.Odbc provider to bridge to the iSeries Access ODBC
driver.
Consider the DB2 UDB (LUW) provider if you need to access
different DB2 UDB platforms, such as DB2 UDB for Windows or DB2 UDB for z/OS, in
addition to iSeries or if you wish to take advantage of the GUI add-Ins
discussed in the previous section.
Jarek
Miszczyk is the Senior Software Engineer, PartnerWorld for Developers, IBM
Rochester. He can be reached by email at
LATEST COMMENTS
MC Press Online