Implementing a .NET Managed Provider for DB2 Universal Database

If you are interested in working with open source implementations of the .NET framework and like the challenge of creating your own data provider, this article gives you the basics of implementing such a data provider using the DB2® Call Level Interface.

Victor Vatamanescu, Independent consultant

Victor Vatamanescu is an independent consultant for a local company implementing an e-government solution. He enjoys working in open source projects related to the .NET framework, PostgreSQL and fltk (mono, npgsql, fltk.net).



08 May 2003

Introduction

.NET is a modern object-oriented, component-based paradigm for writing software that is championed by Microsoft®. It is backed up by the .NET Framework, the environment that has two functions:

  • Managing the execution of the .NET code
  • Providing a set of libraries that ease many programming tasks you may face in your projects

The .NET technology has proven to be solid and mature (even if it has some glitches). The framework comes free on Windows®. There are also a number of open source implementations of the .NET Framework. One of the most important is MONO (http://www.go-mono.com) which includes a cross-platform C# compiler and a nearly complete implementation of the library.

The set of libraries that compose the framework are organized logically on a functional basis. The System.Data library deals with data related programming tasks. It proposes a disconnected architecture of working with data, based on XML.

Currently, Microsoft ships direct data providers only for its SQL Server database system. It also ships OLE DB and ODBC data providers that can access data in any data source that has an OLE DB or ODBC driver. However, the best performance is achieved when using direct data providers.

The .NET data provider from IBM® for its DB2 Universal Database™ (UDB) is now available in DB2 UDB 8.1.2 and later versions of DB2 UDB.

The purpose of this article is to familiarize you with the set of concepts and libraries of a data provider and to show you how to write such a provider. For the official word on writing your own data provider, see the .NET Framework Developer's Guide.


Requirements for a .NET managed data provider for DB2 UDB

When writing a .NET managed data provider for DB2 UDB, we expose the DB2 UDB Call Level Interface API (CLI API) through a set of classes and interfaces that conform to the ADO.NET data provider architecture - a set of coding conventions specific to the .NET framework that must be followed by every managed provider implementation.

A managed data provider must implement the following interfaces:

Table 1. Managed data provider interfaces

InterfaceDescription
IDbConnectionRepresents a unique session with a data source. In the case of a client/server database system, the session may be equivalent to a network connection to the server.
IDbTransactionRepresents a local transaction.
IDbCommandRepresents a query or command that is used when connected to a data source.
IDataParameterAllows a user to implement a parameter to a command and its mapping to DataSet columns.
IDataParameterCollectionAllows a user to implement a parameter to a command and its mapping to DataSet columns.
IDataReaderProvides a method of reading a forward-only read-only stream of data from your data source.
IDataAdapterAllows a user to implement a DataAdapter for populating a DataSet and resolving changes in the DataSet back to the data source.
IDbDataAdapterAllows a user to implement a DataAdapter geared towards relational databases. Represents a set of commands and a database connection that are used to fill the DataSet and reconcile changes made to the DataSet with the source database.

The .NET Framework also includes the DbDataAdapter class, which provides a nearly complete implementation of the IDataAdapter and IDbDataAdapter classes. The DbDataAdapter class can be used by any provider that implements the complete set of .NET data provider interfaces. Suggestions for common constructors that must be implemented are found in .NET Developer's Guide.

We use the following classes to implement our provider. The naming convention is conformant with the naming rules that must be followed by a native managed data provider:

  • Db2Connection
  • Db2Transaction
  • Db2Command
  • Db2DataReader
  • Db2Parameter
  • Db2ParameterCollection
  • Db2DataAdapter
  • Db2Exception

We'll concentrate on the Db2Connection and Db2Command classes because those classes are the most important ones in the interaction with the DB2 UDB server.


Interacting with the DB2 Call Level Interface (CLI)

We will implement our managed data provider using the C# language. This section describes how we interact with the CLI:

  • Accessing the DB2 CLI
  • Using CLI handles
  • CLI diagnostic requirements

Accessing the DB2 CLI

The first step is to provide access to the database API - the DB2UDB CLI API. This API is exposed as a dynamic link library named "db2cli". DB2 Call Level Interface (DB2 CLI) is IBM's callable SQL interface to the DB2 family of database servers. It is a C and C++ application programming interface for relational database access, and it uses function calls to pass dynamic SQL statements as function arguments.

The functionality provided by the CLI is accessed as a set of internal enums, structs and classes included in CLIInvoke.cs, an internal class that acts as a wrapper between the higher level classes and the low-level API. A code snippet is shown here:

using System.Data; 
using System.Data.Common; 
<b>using System.Runtime.InteropServices;</b> 
 
	internal enum CliHandleType : short 
	{ 
		SQL_HANDLE_ENV = 1, 
		SQL_HANDLE_DBC = 2, 
		SQL_HANDLE_STMT = 3, 
		SQL_HANDLE_DESC = 4 
	}; 
 
... 
 
internal class CLIInvoke 
{ 
	internal static Db2ParameterDirection 
	ConvertParameterDirection (ParameterDirection dir) 
	{ 
		switch (dir) 
		{ 
			case ParameterDirection.Input: 
				return Db2ParameterDirection.Input; 
			case ParameterDirection.InputOutput: 
				return Db2ParameterDirection.InputOutput; 
			case ParameterDirection.Output: 
				return Db2ParameterDirection.Output; 
			case ParameterDirection.ReturnValue: 
				return Db2ParameterDirection.ReturnValue; 
			default: 
				return Db2ParameterDirection.Input; 
		} 
	} 
 
		[DllImport("db2cli")] 
		internal static extern CliReturn SQLAllocHandle 
	 (CliHandleType HandleType, IntPtr InputHandle, ref IntPtr 
	 OutputHandlePtr); 
 
		[DllImport("db2cli")] 
		internal static extern CliReturn SQLSetEnvAttr (IntPtr 
	EnvHandle, CliEnv Attribute, IntPtr Value, int StringLength);

Note the [DllImport("db2cli")] attribute (in bold above), which indicates that the particular function it associated with it is in the "db2cli" dynamic link library. This attribute is accompanied by the extern keyword to indicate that the method is implemented externally.

The managed provider accesses the unmanaged code in the CLI library by means of the interoperability services - a set of functionality provided by the .NET Framework. Note the line using System.Runtime.InteropServices;, which imports the namespace, so that we don't have to qualify its classes.

Conceptually, we can divide the operations of an application using DB2 CLI into two categories:

  • Initialization and termination - establishing and terminating communications with the database server
  • Transaction processing - the common operations on the database server specific to data manipulation and definition language

Those levels of functionality will be roughly mapped to the Db2Connection class (initialization and termination) and to the Db2Command class (transaction processing).

Using CLI handles

When working with DB2 CLI to access the database server functionality, the programming paradigm requires using handles. You must obtain handles that are passed as parameters to the functions in the CLI API. A handle is a variable that refers to a data object controlled by DB2 CLI. Using handles means your application doesn't have to do as much management of global variables or data structures.

There are four types of handles, as described in IBM DB2 CLI documentation:

  • Environment handle - The environment handle refers to the data object that contains information regarding the global state of the application, such as attributes and connections. An environment handle must be allocated before a connection handle can be allocated.
  • Connection handle - A connection handle refers to a data object that contains information associated with a connection to a particular data source (database). This includes connection attributes, general status information, transaction status, and diagnostic information.
  • Statement handle(s) - A statement handle refers to the data object that is used to track the execution of a single SQL statement. This includes information such as statement attributes, SQL statement text, dynamic parameters, cursor information, bindings for dynamic arguments and columns, result values and status information (these are discussed later). Each statement handle is associated with a connection handle. A statement handle must be allocated before a statement can be executed.
  • Descriptor handle(s) - A descriptor handle refers to a data object that contains information about: columns in a result set and dynamic parameters in an SQL statement.

The first two types of handles are specific to the initialization and termination operations and will be treated in the Db2Connection class. The later two handles are specific to the transaction processing operations and will be treated mostly in the Db2Command class.

CLI diagnostic requirements

Another rule imposed by the DB2 CLI programming paradigm is that each DB2 CLI function returns the function return code as a basic diagnostic. The return code values are covered in the CliReturn enumeration:

	internal enum CliReturn : short 
	{ 
		SQL_ERROR = -1, 
		SQL_INVALID_HANDLE = -2, 
		SQL_SUCCESS = 0, 
		SQL_SUCCESS_WITH_INFO = 1, 
		SQL_STILL_EXECUTING = 2, 
		SQL_NEED_DATA = 99, 
		SQL_NO_DATA = 100 
	}

Each time we will make a call to a DB2 CLI function through the CLIInvoke's class methods, we will check the return value and take appropriate action.


Working with connections: The Db2Connection class

The first class to be implemented by the managed data provider is the connection class - in our case the Db2Connection class.

Overview

The Connection object represents a database connection.

According to the .NET Developer's Guide, we must ensure that a Connection is created and opened before Commands can be executed. There are also other considerations to be observed. Please see the documentation for more information.

The IDbConnection interface

The most important requirement for the implementation of the Db2Connection class is that it must implement the IDbConnection interface. Here are the properties and methods defined in the IDbConnection interface:

Table 2. IDbConnection properties

PropertyDescription
ConnectionStringGets or sets the string used to open a database.
ConnectionTimeoutGets the time to wait while trying to establish a connection before terminating the attempt and generating an error.
DatabaseGets the name of the current database or the database to be used once a connection is open.
StateGets the current state of the connection.

Table 3. IDbConnection methods

MethodDescription
BeginTransactionOverloaded. Begins a database transaction.
ChangeDatabaseChanges the current database for an open Connection object.
CloseCloses the connection to the database.
CreateCommandCreates and returns a Command object associated with the connection.
OpenOpens a database connection with the settings specified by the ConnectionString property of the provider-specific Connection object.

Implementing the Db2Connection class

The following code snippet is the implementation of the Db2Connection class. It is contained in the Db2Connection.cs file.

The following sections describe more about the following topics:

  • The DB2 CLI environment and connection handles
  • The Db2Connection constructors
  • The implementation of the IDbConnection interface
  • Obtaining the handles
  • Opening a connection
  • Closing a connection
  • Freeing the handles

The Db2Connection constructors

Db2Connection class implements two constructors, as recommended in the documentation. This is their signature:

public Db2Connection() 
public Db2Connection(string connectionString)

The default constructor implementation contains code that allocates the environment handle and sets the environment attributes.

public Db2Connection() 
{ 
	_connectionTimeout = 15; 
	_connectionString = null; 
	_database = null; 
 
	CliReturn ret; 
	ret = CLIInvoke.SQLAllocHandle(CliHandleType.SQL_HANDLE_ENV, 
		IntPtr.Zero, ref _hEnvironment); 
	if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
		CliReturn.SQL_SUCCESS_WITH_INFO)) 
		throw new Db2Exception("Cannot allocate environment 
			handle"); 
	ret = CLIInvoke.SQLSetEnvAttr(_hEnvironment, CliEnv.CliVersion, 
		(IntPtr) 3 , 0); 
	if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
		CliReturn.SQL_SUCCESS_WITH_INFO)) 
		throw new Db2Exception("Cannot set the environment's 
			attributes"); 
} 
 
 
public Db2Connection(string connectionString): this () 
{ 
	this.ConnectionString = connectionString; 
}

The implementation of the IDbConnection interface

Each member of the IDbConnection interface has an implementation in the Db2Connection class. Note the following method:

public void Dispose()

This method is not a direct member of the IDbConnection interface. It is inherited by the IDbConnection interface from the IDisposable interface and must be implemented as well.

You should also note here the setter of the ConnectionString property. It parses the connection string and fills the appropriate private fields. Its implementation was limited to a minimum for the sake of simplicity.

Obtaining the handles

This task is part of initializing the work with the DB2 CLI.

The DB2 CLI environment and connection handles

The first thing to notice in the implementation of the Db2Connection class is that, apart from the private fields that are directly mapped to the public properties of the class, we also have two DB2 CLI handles:

  • _hEnvironment: the environment handle
  • _hConnection: the connection handle

Both are of IntPtr type, a platform-specific type that is used to represent a pointer or a handle. The _hEnvironment and _hConnection variables are initialized in the default constructor. We will use those handles to interact with the DB2 CLI functions. Note that the _hConnection handle is exposed as the hConnection internal property. This way it can be accessed only by the types internal to the managed data provider assembly.

This is the code that allocates the environment handle and sets the environment attributes:

ret = CLIInvoke.SQLAllocHandle(CliHandleType.SQL_HANDLE_ENV, 
IntPtr.Zero, ref _hEnvironment); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Cannot allocate EnvHandle..."); 
ret = CLIInvoke.SQLSetEnvAttr(_hEnvironment, CliEnv.CliVersion, 
(IntPtr) 3 , 0); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Cannot set the environment's 
attributes...");

This code is located in the default constructor of the Db2Connection class.

  1. The first step is to allocate the environment handle. Basically, we call the SQLAllocHandle DB2 CLI function through the CLIInvoke class. Then we test the return code and in case of failure, we throw an exception.

    The SQLAllocHandle function is used to obtain any of the four types of handles. In this case we use the CliHandleType enumeration to instruct the function that we want an environment function and pass the _hEnvironment private field by reference to the function to obtain the handle.

  2. The second step is to set the environment variables. We call the SQLSetEnvAttr DB2 CLI function using the CLIInvoke wrapper class. Note that the first parameter of this function is the environment handle we obtained earlier. We also test the return code of the function.
  3. The connection handle is obtained in a similar manner. The difference is that we will obtain the handle in the Open method.
    ret = CLIInvoke.SQLAllocHandle(CliHandleType.SQL_HANDLE_DBC, 
     _hEnvironment, ref _hConnection); 
    if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
    CliReturn.SQL_SUCCESS_WITH_INFO)) 
    	throw new Db2Exception("Cannot allocate database handle");

    Note that we used the same DB2 CLI SQLAllocHandle, only this time we asked for a connection handle and we provided the environment handle as input parameter. We test the return code to see if we need to throw any errors.

Handling return codes: We treat return codes from the DB2 CLI using the CliReturn enumeration. Each time we invoke a DB2 CLI function we read its return code and if the function returned an error, we throw a Db2Error error.

Opening a connection

public void Open() 
{ 
	CliReturn ret; 
	if (State == ConnectionState.Open) 
		throw new Db2Exception("Connection already open"); 
	ret = CLIInvoke.SQLAllocHandle(CliHandleType.SQL_HANDLE_DBC, 
		_hEnvironment, ref _hConnection); 
	if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
		CliReturn.SQL_SUCCESS_WITH_INFO)) 
		throw new Db2Exception("Cannot allocate database handle"); 
	ret = CLIInvoke.SQLConnect(_hConnection, _server, (short) 
		_server.Length, _user, (short) _user.Length, _password, 
		(short) _password.Length); 
	if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
		CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Exception("Cannot connect to the database"); 
}

The DB2 CLI function used to open the connection is SQLConnect and is accessed through the corresponding CLIInvoke wrapper class. It takes the connection handle as a parameter, as well as login credentials. After calling the function we test the return value. There are other functions we can use to open a connection and they are described in the DB2 CLI documentation.

Another issue correlated to the database connection is that you can implement a connection pulling mechanism. This topic can be correlated to using multiple threads when programming the provider, but these topics are outside the scope of this article.

Closing a connection

At this point we have the environment and the connection handles. From now on the initializing part of working with the DB2 CLI is over. This is half of the responsibilities the Db2Connection class has. The other half is related to terminating the work session we initialized so far.

To close a connection in ADO.NET, call the Close method of the Db2Connection class. This method will call the SQLDisconnect DB2 CLI function through the CLIInvoke wrapper class.

public void Close () 
{ 
	if (this.State == ConnectionState.Open) 
	{ 
		CliReturn ret; 
		ret = CLIInvoke.SQLDisconnect(_hConnection); 
		if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
			CliReturn.SQL_SUCCESS_WITH_INFO)) 
		{ 
			throw new Db2Exception("Error when trying to dispose 
				the environment handle"); 
		} 
	} 
	else 
	{ 
		throw new Db2Exception("InvalidOperationException"); 
	} 
}

As usual, we check the return code and throw an exception in case an error occurred. You should also note (see the whole method body) that we check first the state of the connection.

Freeing the handles

All we have to do in order to terminate our working session is to free the handles we have used so far. We will use the DB2 CLI SQLFreeHandle function that we access through the CLIInvoke wrapper class. We check the return values and act accordingly.

ret = CLIInvoke.SQLFreeHandle((ushort) CliHandleType.SQL_HANDLE_DBC, 
 _hConnection); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Error when trying to dispose the 
connection handle"); 
ret = CLIInvoke.SQLFreeHandle((ushort) CliHandleType.SQL_HANDLE_ENV, 
 _hEnvironment); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Error when trying to dispose the 
environment handle");

Note that we free the handles in the Dispose method. This is the place used to do this kind of clean-up in the .NET framework, because we do not have a deterministic destructor - the garbage collector will trigger the destructor, but we don't know when. By placing this kind of code in the Dispose method of the IDisposable interface we provide the opportunity to call this method from other places and free valuable resources.


Transaction processing - The Db2Command class

From the ADO.NET point of view, the Command object is responsible for formulating a request and passing it on to the data source. If results are returned, the Command object is responsible for returning results as a DataReader, a scalar value, or as parameters. To implement a Command object, create a class that implements IDbCommand. Implement the ExecuteReader method to return a resultset (or multiple resultsets) as a DataReader, the ExecuteScalar method to return a result as a scalar value, and the ExecuteNonQuery method to process a command at the data source that does not return a result set but may return parameters.

The IDbCommand interface

The IDbCommand interface has the following set of properties and methods that must be implemented:

Table 4. IDbCommand properties

PropertyDescription
CommandTextGets or sets the text command to run against the data source.
CommandTimeoutGets or sets the wait time before terminating the attempt to execute a command and generating an error.
CommandTypeIndicates or specifies how the CommandText property is interpreted.
ConnectionGets or sets the IDbConnection used by this instance of the IDbCommand.
ParametersGets the IDataParameterCollection.
TransactionGets or sets the transaction in which the Command object of an ADO.NET data provider executes.
UpdatedRowSourceGets or sets how command results are applied to the DataRow when used by the Update method of a DbDataAdapter.

Table 5. IDbCommand methods

MethodDescription
CancelAttempts to cancels the execution of an IDbCommand.
CreateParameterCreates a new instance of an IDataParameter object.
ExecuteNonQueryExecutes a SQL statement against the Connection object of an ADO.NET data provider, and returns the number of rows affected.
ExecuteReaderOverloaded. Executes the CommandText against the Connection and builds an IDataReader.
ExecuteScalarExecutes the query, and returns the first column of the first row in the resultset returned by the query. Extra columns or rows are ignored.
PrepareCreates a prepared (or compiled) version of the command on the data source.

Implementing the Db2Command class

The Db2Command class implements the IDbCommand interface and access to the DB2 CLI is done through the CLIInvoke wrapper class.Take a closer look at the Prepare method.

public void Prepare() 
{ 
	throw new NotSupportedException(); 
}

Although a full DB2 UDB managed data provider will not provide such an implementation, the point here is that when the underlying data source does not provide a specific functionality, the managed data provider should throw a NotSupportedException exception.

Transaction processing tasks in the Db2Command class

We also use handles to access the DB2 CLI functionality, only that in this case we will use statement and descriptor handles. Conceptually, the transaction processing has the following steps:

  1. Allocating statement handle(s)
  2. Preparation and execution of SQL statements
  3. Processing results
  4. Commit or Rollback
  5. Optionally, freeing statement handle(s) if the statement is unlikely to be executed again

We will concentrate on the most basic tasks:

  • Allocating statement handles
  • Executing a statement
  • Freeing the statement handles

Allocating statement handles
After we declared the statement handle as a private IntPtr pointer (_hPointer), we must allocate it in order to use it. This is done by the Connection property setter in the following lines:

CliReturn ret; 
IntPtr myConnectionHandle = ((Db2Connection) value).hConnection; 
ret = CLIInvoke.SQLAllocHandle(CliHandleType.SQL_HANDLE_STMT, 
myConnectionHandle, ref _hStatement); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Cannot allocate statement handle");

We use the SQLAllocHandle DB2 CLI function through the CLIInvoke wrapper class. Note that we instruct the function that we want a statement handle. We must provide the connection handle as a parameter. The connection handle is obtained from the _connection private field - this is why we allocate the statement handle in the Connection property setter.

As usual, we test the return code.

Executing a statement
We will focus on the ExecuteNonQuery statement to demonstrate how a data manipulation language statement will be executed in our managed data provider.

int result = 0; 
CliReturn ret; 
if ((_connection == null) || (_connection.State != 
ConnectionState.Open)) 
	throw new InvalidOperationException(); 
ret = CLIInvoke.SQLExecDirect(_hStatement, _commandText, 
_commandText.Length); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Cannot execute the command"); 
ret = CLIInvoke.SQLRowCount(_hStatement, ref result); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Cannot retrieve the number of 
affected rows"); 
 
return result;

The ExecuteNonQuery method must return an integer value (the number of rows affected by the statement), so the implementation has two steps:

  • Calling the SQLExecDirect DB2 CLI function to execute the statement,
  • Calling the SQLRowCount function to get the number of rows affected by the statement.

We also check the return codes of those functions.

Freeing the statement handles
After we executed the statement, we must free the resources (in our case, the handles) used by the provider. As with the Db2Connection class, we will free the statement handle in the Dispose method of the Db2Command class.

ret = CLIInvoke.SQLFreeHandle((ushort) CliHandleType.SQL_HANDLE_STMT, 
_hStatement); 
if ((ret != CliReturn.SQL_SUCCESS) && (ret != 
CliReturn.SQL_SUCCESS_WITH_INFO)) 
	throw new Db2Exception("Error when trying to dispose the 
statement handle");

We use the same SQLFreeHandle DB2 CLI function through the CLIInvoke helper class, and then we check the return code.

Testing the provider

The provider can be compiled and tested on both Linux and Windows. It can be compiled with the Microsoft C# compiler using Visual Studio .NET or from the command line:

csc /r:System.Data.dll /target:library /out:System.Db2Client.dll *.cs

Compiling with mono is almost the same except that the name of the compiler is mcs.


Conclusion

As you can see, the task of implementing a managed data provider for the DB2 UDB system can be handled in a straightforward manner. I have shown you the basics. Of course, the code I provided is not a complete implementation of a DB2 UDB managed data provider, and I have sacrificed mandatory programming tasks such as error handling, resource pooling, threading for the sake of simplicity and brevity. The article was not meant to cover such subjects.

I hope this article will prove of help to you, whether you are writing your own managed data provider, or if you are just scratching the surface of the System.Data .NET library. The DB2 UDB managed data provider will eanble .NET applications (on Windows and other operating systems - thanks to the open source efforts) to the strength and stability of the DB2 UDB system.


Download

DescriptionNameSize
Code samplecode.zip  ( HTTP | FTP )33KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Information management on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management
ArticleID=13253
ArticleTitle=Implementing a .NET Managed Provider for DB2 Universal Database
publish-date=05082003