0% found this document useful (0 votes)
73 views20 pages

Developer Oracle

Download as doc, pdf, or txt
Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1/ 20

Oracle Database for

Developer: .NET Windows


ODP.NET
Build a .NET Application on the Sample code

Oracle Database with Visual Studio


2005 or 2008
Learn the basic yet essential processes involved in building a .NET
application that uses an Oracle database.

By John Paul Cook

Published September 2008

Read the Visual Studio .NET 2003 version of this guide

With the popularity of Microsoft's .NET Framework, many developers are hungry for information about the
best means of integrating .NET applications with Oracle—not only in terms of basic connectivity, but also in
relationship to effective and efficient application development using Visual Studio 2005 or 2008.

In this article, I'll explain the basic yet essential processes involved in building a .NET application that uses an
Oracle database, including:

 How to add project references to support Oracle class libraries in your .NET project
 How to create Oracle Database connection strings
 How to work with Connection, Command, and DataReader objects

You will have the opportunity to apply what you have learned in three practice labs, ranging in difficulty from
the relatively simple to the more complex. The article's screenshots are taken from Visual Studio 2008, but the
experience is very similar in Visual Studio 2005.

For information and labs about how to secure your application, see my article "Securing a .NET Application on
the Oracle Database". (Also, see the OTN .NET Developer Center for technical articles covering a range of
Oracle.NET application lifecycle issues.)

Note that the free Oracle Developer Tools for Visual Studio, available for download from OTN, provides a
Visual Studio add-in that makes the development of .NET apps on Oracle much easier and more intuitive. That
subject is beyond our scope here, however.

.NET Data Provider


In addition to basic Oracle client connectivity software, .NET applications require the use of what is known as a
managed data provider (where "managed" refers to code managed by the .NET framework). The data provider
is the layer between the .NET application code and the Oracle client connectivity software. In almost every
case, the best performance is achieved by using a provider optimized for a specific database platform instead of
the generic .NET OLE DB data provider.
Oracle, Microsoft, and third-party vendors all offer .NET data providers optimized for Oracle. Oracle and
Microsoft make their Oracle data providers available for free. (Microsoft's provider for the .NET Framework
2.0 is included in the framework, but it still requires Oracle client software installation.) In this article, we will
use of the Oracle Data Provider for .NET (ODP.NET), which is included with the Oracle Database or as a
separate download.

ODP.NET provides standard ADO.NET data access, while exposing Oracle database-specific features, such as
XML DB, data access performance optimizations, and Real Application Clusters connection pooling.

When ODP.NET and Oracle client software are installed, application development using Visual Studio can
begin. It's a good idea to confirm client connectivity before starting development. If you can connect to Oracle
using Oracle client software such as SQL*Plus on the same machine as Visual Studio, then you know that your
Oracle client-side software is properly installed and configured.

If you are new to Oracle, see the section "Installing .NET Products" in the Oracle Database 2 Day Developer's
Guide for background information regarding installing and configuring ODP.NET specifically, or to the Oracle
Database Documentation Library for general information about Oracle Database.

Creating a Project in Visual Studio 2005 or 2008


Let's create an ODP.NET application that retrieves data from an Oracle database. Later, we'll see how to
perform error handling with ODP.NET and handle an additional data retrieval scenario.

After starting Visual Studio, the first task is to create a project. You can either select File | New | Project as
shown below or click the New Project button located directly under File.
Figure 1 Creating a new project in Visual Studio 2008 Service Pack 1

A New Project dialog box appears. On the left side of the dialog box under Project Types, select the
programming language of your choice. In our example, "Visual Basic" was chosen. On the right side under
Visual Studio installed templates, choose a project template. To keep things simple, a "Windows Forms
Application" is selected.
Figure 2 Using the New Project dialog

You'll want to specify meaningful names for the project name (we used OraWinApp) and the solution name (we
used OraWinApp). A solution contains one or more projects. When a solution contains only one project, many
people use the same name for both.

Adding a Reference
Because our project must connect to an Oracle database, it is necessary to add a reference to the ODP.NET DLL
containing the data provider of our choice. Within the Solution Explorer, select the project name, right click and
select Add Reference. Alternatively, you can go to the menu bar and select Project and then select Add
Reference.
Figure 3 Adding a reference

The Add Reference dialog box appears.


Figure 4 Selecting the ODP.NET Managed Data Provider

ODP.NET is found under the Oracle.DataAccess component name. Select Oracle.DataAccess from the list,
then click OK to make the ODP.NET data provider known to your project.

Visual Basic/C# Statements


After adding references, it is standard practice to add Visual Basic Imports statements or C# using statements.
Technically, these statements are not required but they do allow you to refer to database objects without using
lengthy, fully qualified names.

By convention, these statements appear at or near the top of a code file, before the namespace or class
declaration.

Imports Oracle.DataAccess.Client ' Visual Basic ODP.NET Oracle managed provider

using Oracle.DataAccess.Client; // C# ODP.NET Oracle managed provider


If you added the reference, Intellisense will help you complete the addition of an Imports or using statement as
shown in Figure 5.
Figure 5 Adding an Imports statement in Visual Basic

Connection Strings and Objects


An Oracle connection string is inseparable from Oracle names resolution. Suppose you had a database alias of
OraDb defined in a tnsnames.ora file as follows:

OraDb=
(DESCRIPTION=
(ADDRESS_LIST=
(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521))
)
(CONNECT_DATA=
(SERVER=DEDICATED)
(SERVICE_NAME=ORCL)
)
)
The OraDb alias defines the database address connection information for the client. To use the OraDb alias
defined in the tnsnames.ora file shown above, you would use the following syntax:
Dim oradb As String = "Data Source=OraDb;User Id=scott;Password=tiger;" ' Visual Basic

string oradb = "Data Source=OraDb;User Id=scott;Password=tiger;"; // C#


You can modify the connection string to obviate the need for the tnsnames.ora file, however. Simply replace the
name of the alias with how it would be defined in a tnsnames.ora file.
' Visual Basic
Dim oradb As String = "Data Source=(DESCRIPTION=" _
+ "(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521)))" _
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));" _
+ "User Id=scott;Password=tiger;"

// C#
string oradb = "Data Source=(DESCRIPTION="
+ "(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521)))"
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));"
+ "User Id=scott;Password=tiger;";
As you can see above, the username and password are embedded in the connection string in clear text. This is
the simplest approach to creating a connection string. However, the clear text approach is undesirable from a
security perspective. In particular, you must understand that compiled .NET application code is only marginally
more secure than the clear text source code files. It is very easy to decompile .NET DLL and EXE files and
view the original clear text contents. (Encryption is in fact the appropriate solution, but that subject would be a
quite lengthy digression from our discussion here.)

Next, you must instantiate a connection object from the connection class. The connection string must be
associated with the connection object.

Dim conn As New OracleConnection(oradb) ' Visual Basic

OracleConnection conn = new OracleConnection(oradb); // C#


Notice that the connection string is associated with the connection object by being passed through the object's
constructor, which is overloaded. The constructor's other overload allows the following alternative syntax:
Dim conn As New OracleConnection() ' Visual Basic
conn.ConnectionString = oradb

OracleConnection conn = new OracleConnection(); // C#


conn.ConnectionString = oradb;
After associating a connection string with a connection object, use the Open method to make the actual
connection.
conn.Open() ' Visual Basic

conn.Open(); // C#
We'll cover error handling later.

Command Object
The Command object is used to specify the SQL command text that is executed, either a SQL string or a stored
procedure. Similar to the Connection object, it must be instantiated from its class and it has an overloaded
constructor. In this sample, ODP.NET will perform a SQL query to return the department name (DNAME)
from the departments table (DEPT) where the department number (DEPTNO) is 10.

Dim sql As String = "select dname from dept where deptno = 10" ' Visual Basic
Dim cmd As New OracleCommand(sql, conn)
cmd.CommandType = CommandType.Text

string sql = "select dname from dept where deptno = 10"; // C#


OracleCommand cmd = new OracleCommand(sql, conn);
cmd.CommandType = CommandType.Text;
Using different overloads, the syntax can be structured slightly differently. The Command object has methods
for executing the command text, which will be seen in the next section. Different methods are appropriate for
different types of SQL commands.
Retrieving a Scalar Value
Retrieving data from the database can be accomplished by instantiating an OracleDataReader object and using
the ExecuteReader method, which returns an OracleDataReader object. Returned data is accessible by passing
either the column name or zero-based column ordinal to the OracleDataReader.

Dim dr As OracleDataReader = cmd.ExecuteReader() ' Visual Basic


dr.Read()

Label1.Text = dr.Item("dname") ' retrieve by column name


Label1.Text = dr.Item(0) ' retrieve the first column in the select list
Label1.Text = dr.GetString(0) ' return a .NET data type
Label1.Text = dr.GetOracleString(0) ' return an Oracle data type
There are typed accessors for returning .NET native data types and others for returning native Oracle data types,
all of which are available in C#, Visual Basic, or any other .NET language. Zero-based ordinals are passed to
the accessors to specify which column to return.
OracleDataReader dr = cmd.ExecuteReader(); // C#
dr.Read();

label1.Text = dr["dname"].ToString(); // C# retrieve by column name


label1.Text = dr.GetString(0).ToString(); // return a .NET data type
label1.Text = dr.GetOracleString(0).ToString(); // return an Oracle data type
In this simplified example, the returned value of DNAME is a string and is used to set the value of the label
control's text property, which is also a string. But if DEPTNO, which is not a string, had been retrieved instead,
there would be a data type mismatch. The .NET runtime attempts to implicitly convert from one data type to
another when the source and destination data types don't match. Sometimes the data types are incompatible and
the implicit conversion fails, throwing an exception. But even when it works, it's still better to use explicit data
type conversions instead of implicit data type conversion.

An explicit cast to integer is shown below:

Label1.Text = CStr(dr.Item("deptno")) ' Visual Basic integer to string cast

C# is not as forgiving as Visual Basic on implicit conversions. You'll find yourself doing
explicit conversions:

label1.Text = dr.GetInt16("deptno").ToString(); // C#
You can explicitly cast scalar values as well as arrays.

Close and Dispose


Either the connection object's Close or the Dispose method should be called to close the connection to the
database. The Dispose method calls the Close method.

conn.Close() ' Visual Basic


conn.Dispose() ' Visual Basic

conn.Close(); // C#
conn.Dispose(); // C#
You don't have to explicitly call Close or Dispose if you use VB's Using keyword or C#'s using keyword.
using (OracleConnection conn = new OracleConnection(oradb)) // C#
{
conn.Open();
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "select dname from dept where deptno = 10";
cmd.CommandType = CommandType.Text;

OracleDataReader dr = cmd.ExecuteReader();
dr.Read();

label1.Text = dr.GetString(0);
}
In addition, OracleCommand includes a Dispose method; OracleDataReader includes a Close and Dispose
method. Closing and disposing .NET objects free up system resources, ensuring more efficient application
performance, which is especially important under high load conditions. You can experiment with some of the
concepts we've learned here in Lab 1 (Retrieving Data from the Database) and Lab 2 (Adding Interactivity).

Error Handling
When an error occurs, .NET applications should gracefully handle the error and inform the user with a
meaningful message. Try-Catch-Finally structured error handling is a part of .NET languages; here is a
relatively minimalist example of using the Try-Catch-Finally syntax:

' Visual Basic


Try
conn.Open()

Dim cmd As New OracleCommand


cmd.Connection = conn
cmd.CommandText = "select dname from dept where deptno = " + TextBox1.Text
cmd.CommandType = CommandType.Text

If dr.Read() Then
Label1.Text = dr.Item("dname") ' or use dr.Item(0)
End If
Catch ex As Exception ' catches any error
MessageBox.Show(ex.Message.ToString())
Finally
' In a real application, put cleanup code here.
End Try

// C#
try
{
conn.Open();

OracleCommand cmd = new OracleCommand();


cmd.Connection = conn;
cmd.CommandText = "select dname from dept where deptno = " + textBox1.Text;
cmd.CommandType = CommandType.Text;

if (dr.Read()) // C#
{
label1.Text = dr["dname"].ToString();
// or use dr.GetOracleString(0).ToString()
}
}
catch (Exception ex) // catches any error
{
MessageBox.Show(ex.Message.ToString());
}
finally
{
// In a real application, put cleanup code here.
}
Although this approach will gracefully capture any errors in attempting to get data from the database, it is not
user friendly. For example, look at the following message displayed when the database is unavailable:

Figure 6 An ORA-12545 error caught and displayed to the user

An ORA-12545 is quite meaningful for an Oracle DBA or developer, but not for an end user. A better solution
is to add an additional Catch statement to trap for the most common database errors and provide user-friendly
messages.
Catch ex As OracleException ' catches only Oracle errors
Select Case ex.Number
Case 1
MessageBox.Show("Error attempting to insert duplicate data.")
Case 12545
MessageBox.Show("The database is unavailable.")
Case Else
MessageBox.Show("Database error: " + ex.Message.ToString())
End Select
Catch ex As Exception ' catches any error
MessageBox.Show(ex.Message.ToString())

catch (OracleException ex) // catches only Oracle errors


{
switch (ex.Number)
{
case 1:
MessageBox.Show("Error attempting to insert duplicate data.");
break;
case 12545:
MessageBox.Show("The database is unavailable.");
break;
default:
MessageBox.Show("Database error: " + ex.Message.ToString());
break;
}
}
catch (Exception ex) // catches any error not previously caught
{
MessageBox.Show(ex.Message.ToString());
}
Notice the two Catch statements in the code sample above. If there aren't any Oracle errors tocatch, the first
statement branch is skipped, leaving any other non-Oracle error to be caught by the second statement. Catch
statements must be ordered in the code from most specific to most general. After implementing the user-
friendly exception handling code, the ORA-12545 error message appears as follows:

Figure 7 A user-friendly error message for an ORA-12545 error

The Finally code block is always executed regardless of whether or not an error occurred. It is where cleanup
code belongs. If you don't use Using or using, you should dispose your connection and other objects in the
Finally code block.

Retrieving Multiple Values Using a DataReader


So far our examples have only showed how to retrieve a single value. An OracleDataReader can retrieve values
for multiple columns and multiple rows. First consider a multiple column, single row query:

select deptno, dname, loc from dept where deptno = 10


To obtain the values of the columns, either zero-based ordinals or column names can be used. Ordinals are
relative to the order in the query. Thus, the LOC column's value can be retrieved in Visual Basic by using either
dr.Item(2) or dr.Item("loc").

Here is a code snippet that concatenates the DNAME and LOC columns from the previous query:

Label1.Text = "The " + dr.Item("dname") + " department is in " + dr.Item("loc") ' VB

label1.Text = "The " + dr["dname"].ToString() + " department is in " +


dr["loc"].ToString(); // C#
Now consider a query that returns multiple rows:
select deptno, dname, loc from dept
To process multiple rows returned from an OracleDataReader, some type of looping construct is needed.
Furthermore, a control that can display multiple rows is desirable. An OracleDataReader is a forward-only,
read-only cursor, so it can't be bound to an updateable or fully scrollable control such as a Windows Forms
DataGrid control. An OracleDataReader is compatible with a ListBox control, as the following code snippet
illustrates:
While dr.Read() ' Visual Basic
ListBox1.Items.Add("The " + dr.Item("dname") + " department is in " + dr.Item("loc"))
End While

while (dr.Read()) // C#
{
listBox1.Items.Add("The " + dr["dname"].ToString() + " department is in " +
dr["loc"].ToString());
}
Lab 3 (Retrieve Multiple Columns and Rows with an OracleDataReader) highlights some of these concepts.
Building and Running on x64
When running Visual Studio 2008 on an x64 operating system, the Active solution platform defaults to Any
CPU. Change that to x86 before building your project.

Figure 8 Change from Any CPU to x86 when building on a 64-bit platform

Conclusion
This article has introduced you to the process of accessing Oracle databases using .NET programming
languages. You should now have the capability to connect to the database and retrieve multiple columns and
rows.

Lab 1: Retrieving Data from the Database

We begin with the requirement that you've created a project and added a reference as shown previously in this
article.

1. Continue by adding a button control and a label control to the Windows form. Be sure to leave room
above the controls to allow additions that will be made in Lab 2.
Figure 9 Lab 1 form with button and label controls

2. Add code to retrieve data from the Oracle database and display the results on the form. Put the code in
a click event handler for the button. The easiest way to get started with this task is to double click the
button because it will create a stub for the event handler.
Figure 10 Click event handler stub

3. Add Visual Basic Imports statements before the Public Class declaration or C# using statements before
the namespace declaration.
4. Imports Oracle.DataAccess.Client ' Visual Basic, ODP.NET Oracle managed provider
5.
6. using Oracle.DataAccess.Client; // C#, ODP.NET Oracle managed provider
7. Add the Visual Basic version of the click event handler code between the Private Sub and End Sub
statements (be sure to replace ORASRVR with your server's host name):
8. Dim oradb As String = "Data Source=(DESCRIPTION=(ADDRESS_LIST=" _
9. + "(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521)))" _
10. + "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));" _
11. + "User Id=scott;Password=tiger;"
12.
13. Dim conn As New OracleConnection(oradb) ' Visual Basic
14. conn.Open()
15.
16. Dim cmd As New OracleCommand
17. cmd.Connection = conn
18. cmd.CommandText = "select dname from dept where deptno = 10"
19. cmd.CommandType = CommandType.Text
20.
21. Dim dr As OracleDataReader = cmd.ExecuteReader()
22. dr.Read() ' replace this statement in next lab
23. Label1.Text = dr.Item("dname") ' or dr.Item(0), remove in next lab
24.
25. dr.Dispose()
26. cmd.Dispose()
27. conn.Dispose()

Add the following C# code to the click event handler between the { and } curly braces for the button's
click event handler (be sure to replace ORASRVR with your server's host name):

string oradb = "Data Source=(DESCRIPTION=(ADDRESS_LIST="


+ "(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521)))"
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));"
+ "User Id=scott;Password=tiger;";

OracleConnection conn = new OracleConnection(oradb); // C#


conn.Open();

OracleCommand cmd = new OracleCommand();


cmd.Connection = conn;
cmd.CommandText = "select dname from dept where deptno = 10";
cmd.CommandType = CommandType.Text;

OracleDataReader dr = cmd.ExecuteReader();
dr.Read(); // replace this statement in next lab
label1.Text = dr["dname"].ToString(); // remove in next lab

dr.Dispose();
cmd.Dispose();
conn.Dispose();
28. Run the application. Click the button. You should see the following:

Figure 11 Data successfully retrieved.

Lab 2: Adding Interactivity

Now that the basics of database access are implemented in the code, the next step is to add interactivity to the
application. Instead of running the hard coded query, a textbox control can be added to accept a user input for
the department number (i.e., DEPTNO).

1. Add a textbox control and another label control to the form as shown below. Set the text property of the
Label2 control to Enter Deptno: and make sure that the Text property of TextBox1 isn't set to
anything.

Figure 12 Lab 2 form with button and label controls

2. Modify the code that defines the select string:


3. cmd.CommandText = "select dname from dept where deptno = " + TextBox1.Text 'VB
4.
cmd.CommandText = "select dname from dept where deptno = " + textBox1.Text; // C#>
5. Run the application. Test the application by entering 10 for the DEPTNO. Retest the application by
entering an invalid DEPTNO (e.g., 50). The application will abort.
Figure 13 An unhandled exception

6. Modify your code to prevent an error when an invalid DEPTNO is entered. Recall that the
ExecuteReader method actually returns an object. Replace the line containing dr.Read with all of the
following statements.
7. If dr.Read() Then ' Visual Basic
8. Label1.Text = dr.Item("dname").ToString()
9. Else
10. Label1.Text = "deptno not found"
11. End If
12.
13. if (dr.Read()) // C#
14. {
15. label1.Text = dr["dname"].ToString();;
16. }
17. else
18. {
19. label1.Text = "deptno not found";
20. }

Test the application by entering a DEPTNO that does not exist. Now the application no longer aborts.
Enter the letter A instead of a number and click the button. The application aborts. Clearly, our
application needs a better approach to handling errors.
Although it could be argued that the application should not allow the user to make invalid inputs that
would cause an error, ultimately the application must have robust error handling added. Not all errors
are preventable, so error handling must be implemented.

Lab 3: Retrieve Multiple Columns and Rows with an OracleDataReader

Now that a single value has been retrieved, the next step is to retrieve multiple columns and rows with an
OracleDataReader. A ListBox control is added to the form to display the results.

1. Add a ListBox control to the form. Resize the control to fill most of the width of the form as shown
below.

Figure 14 Form with ListBox added

2. Remove the where clause from the query and add the additional columns:
3. cmd.CommandText = "select deptno, dname, loc from dept" ' Visual Basic
4.
cmd.CommandText = "select deptno, dname, loc from dept"; // C#
5. The query results will be read in a while loop and will populate the ListBox control. Modify your
Visual Basic code to look like this:
6. Dim oradb As String = "Data Source=(DESCRIPTION=(ADDRESS_LIST=" _
7. + "(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521)))" _
8. + "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));" _
9. + "User Id=scott;Password=tiger;"
10.
11. Dim conn As New OracleConnection(oradb) ' Visual Basic
12. conn.Open()
13.
14. Dim cmd As New OracleCommand
15. cmd.Connection = conn
16. cmd.CommandText = "select deptno, dname, loc from dept"
17. cmd.CommandType = CommandType.Text
18.
19. Dim dr As OracleDataReader = cmd.ExecuteReader()
20. While dr.Read()
21. ListBox1.Items.Add("The " + dr.Item("dname") + _
22. " department is in " + dr.Item("loc"))
23. End While
24.
25. dr.Dispose()
26. cmd.Dispose()
27. conn.Dispose()
28. Modify your C# code to look like this:
29. string oradb = "Data Source=(DESCRIPTION=(ADDRESS_LIST="
30. + "(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521)))"
31. + "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));"
32. + "User Id=scott;Password=tiger;";
33.
34. OracleConnection conn = new OracleConnection(oradb); // C#
35. conn.Open();
36.
37. OracleCommand cmd = new OracleCommand();
38. cmd.Connection = conn;
39. cmd.CommandText = "select deptno, dname, loc from dept";
40. cmd.CommandType = CommandType.Text;
41.
42. OracleDataReader dr = cmd.ExecuteReader();
43. while (dr.Read())
44. {
45. listBox1.Items.Add("The " + dr["dname"].ToString() +
46. " department is in " + dr["loc"].ToString());
47. }
48.
49. dr.Dispose();
50. cmd.Dispose();
conn.Dispose();
51. Run the application. The ListBox should be populated with all of the department names and locations
from the DEPT table. The code downloads have error handling implemented.

You might also like