.NET Data Provider

download .NET Data Provider

of 21

Transcript of .NET Data Provider

  • 8/8/2019 .NET Data Provider

    1/21

    With the popularity of Microsoft's .NET Framework, many developers are hungry for information about the bestmeans of integrating .NET applications with Oraclenot only in terms of basic connectivity, but also in relationship toeffective 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 Oracledatabase, 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 threepractice labs, ranging in difficulty from therelatively simple to the more complex. The article's screenshots are taken from Visual Studio 2008, but theexperience 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 theOracle Database". (Also, see the OTN.NET Developer Centerfor technical articles covering a range of Oracle.NETapplication lifecycle issues.)

    Note that the free Oracle Developer Tools for Visual Studio, available fordownloadfrom OTN, provides a Visual Studio

    add-in that makes the development of .NET apps on Oracle much easier and more intuitive. That subject is beyondour scope here, however.

    .NET Data Provider

    In addition to basic Oracle client connectivity software, .NET applications require the use of what is known as amanaged data provider(where "managed" refers to code managed by the .NET framework). The data provider is thelayer between the .NET application code and the Oracle client connectivity software. In almost every case, the bestperformance is achieved by using a provider optimized for a specific database platform instead of the generic .NETOLE 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 inthe framework, but it still requires Oracle client software installation.) In this article, we will use of the Oracle DataProvider 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 XMLDB, 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'sa good idea to confirm client connectivity before starting development. If you can connect to Oracle using Oracleclient software such as SQL*Plus on the same machine as Visual Studio, then you know that your Oracle client-sidesoftware is properly installed and configured.

    If you are new to Oracle, see the section "Installing .NET Products" in theOracle Database 2 Day Developer's Guidefor background information regarding installing and configuring ODP.NET specifically, or to theOracle 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 performerror 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 shownbelow or click the New Project button located directly underFile.

    http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab1http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab1http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab1http://www.oracle.com/technetwork/articles/dotnet/cook-masteringdotnet-090821.htmlhttp://www.oracle.com/technetwork/articles/dotnet/cook-masteringdotnet-090821.htmlhttp://www.oracle.com/technetwork/articles/dotnet/cook-masteringdotnet-090821.htmlhttp://www.oracle.com/technetwork/topics/dotnet/whatsnew/index.htmlhttp://www.oracle.com/technetwork/topics/dotnet/whatsnew/index.htmlhttp://www.oracle.com/technetwork/topics/dotnet/whatsnew/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/overview/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/overview/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/overview/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/downloads/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/downloads/index.htmlhttp://download.oracle.com/docs/cd/B28359_01/appdev.111/b28843/toc.htmhttp://download.oracle.com/docs/cd/B28359_01/appdev.111/b28843/toc.htmhttp://www.oracle.com/technetwork/database/enterprise-edition/documentation/index.htmlhttp://www.oracle.com/technetwork/database/enterprise-edition/documentation/index.htmlhttp://www.oracle.com/technetwork/database/enterprise-edition/documentation/index.htmlhttp://www.oracle.com/technetwork/articles/dotnet/cook-masteringdotnet-090821.htmlhttp://www.oracle.com/technetwork/articles/dotnet/cook-masteringdotnet-090821.htmlhttp://www.oracle.com/technetwork/topics/dotnet/whatsnew/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/overview/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/overview/index.htmlhttp://www.oracle.com/technetwork/developer-tools/visual-studio/downloads/index.htmlhttp://download.oracle.com/docs/cd/B28359_01/appdev.111/b28843/toc.htmhttp://www.oracle.com/technetwork/database/enterprise-edition/documentation/index.htmlhttp://www.oracle.com/technetwork/database/enterprise-edition/documentation/index.htmlhttp://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab1
  • 8/8/2019 .NET Data Provider

    2/21

    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 underProject Types, select the programminglanguage of your choice. In our example, "Visual Basic" was chosen. On the right side underVisual Studio installedtemplates, choose a project template. To keep things simple, a "Windows Forms Application" is selected.

  • 8/8/2019 .NET Data Provider

    3/21

    Figure 2Using the New Projectdialog

    You'll want to specify meaningful names for the project name (we used OraWinApp) and the solution name (we usedOraWinApp). A solution contains one or more projects. When a solution contains only one project, many people usethe 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 DLLcontaining the data provider of our choice. Within the Solution Explorer, select the project name, right click and selectAdd Reference. Alternatively, you can go to the menu bar and select Project and then select Add Reference.

  • 8/8/2019 .NET Data Provider

    4/21

  • 8/8/2019 .NET Data Provider

    5/21

    Figure 4Selecting the ODP.NET Managed Data Provider

    ODP.NET is found under the Oracle.DataAccess component name. Select Oracle.DataAccess from the list, thenclick 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 usinglengthy, 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 managedprovider

    using Oracle.DataAccess.Client; // C# ODP.NET Oracle managed providerIf you added the reference, Intellisense will help you complete the addition of an Imports or using statement as shownin Figure 5.

  • 8/8/2019 .NET Data Provider

    6/21

    Figure 5Adding 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 ofOraDb 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 definedin 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#

  • 8/8/2019 .NET Data Provider

    7/21

  • 8/8/2019 .NET Data Provider

    8/21

    Using different overloads, the syntax can be structured slightly differently. The Command object has methods forexecuting the command text, which will be seen in the next section. Different methods are appropriate for differenttypes of SQL commands.

    Retrieving a Scalar Value

    Retrieving data from the database can be accomplished by instantiating an OracleDataReader object and using theExecuteReader method, which returns an OracleDataReader object. Returned data is accessible by passing eitherthe column name or zero-based column ordinal to the OracleDataReader.

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

    Label1.Text = dr.Item("dname") ' retrieve by column nameLabel1.Text = dr.Item(0) ' retrieve the first column in the select listLabel1.Text = dr.GetString(0) ' return a .NET data typeLabel1.Text = dr.GetOracleString(0) ' return an Oracle data typeThere are typed accessors for returning .NET native data types and others for returning native Oracle data types, allof 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 namelabel1.Text = dr.GetString(0).ToString(); // return a .NET data typelabel1.Text = dr.GetOracleString(0).ToString(); // return an Oracle data typeIn this simplified example, the returned value of DNAME is a string and is used to set the value of the label control'stext property, which is also a string. But if DEPTNO, which is not a string, had been retrieved instead, there would bea data type mismatch. The .NET runtime attempts to implicitly convert from one data type to another when the sourceand 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 implicitdata 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 findyourself 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 thedatabase. The Dispose method calls the Close method.

    conn.Close() ' Visual Basicconn.Dispose() ' Visual Basic

    conn.Close(); // C#conn.Dispose(); // C#You don't have to explicitly call Close orDispose if you use VB's Using keyword or C#'s using keyword.

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

  • 8/8/2019 .NET Data Provider

    9/21

    {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 applicationperformance, which is especially important under high load conditions. You can experiment with some of theconcepts we've learned here inLab 1 (Retrieving Data from the Database) andLab 2 (Adding Interactivity).

    Error Handling

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

    ' Visual BasicTry

    conn.Open()

    Dim cmd As New OracleCommandcmd.Connection = conncmd.CommandText = "select dname from dept where deptno = " + TextBox1.Textcmd.CommandType = CommandType.Text

    If dr.Read() ThenLabel1.Text = dr.Item("dname") ' or use dr.Item(0)

    End IfCatch 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();

    http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab1http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab1http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab2http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab2http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab1http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab2
  • 8/8/2019 .NET Data Provider

    10/21

    // 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 userfriendly. For example, look at the following message displayed when the database is unavailable:

    Figure 6An 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 toadd 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.NumberCase 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 errorMessageBox.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;

    }}

  • 8/8/2019 .NET Data Provider

    11/21

    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 to catch, 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 7A 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 orusing, 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 formultiple columns and multiple rows. First consider a multiple column, single row query:

    select deptno, dname, loc from dept where deptno = 10To 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) ordr.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 deptTo 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#{

  • 8/8/2019 .NET Data Provider

    12/21

    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 x64When 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 8Change fromAny CPUto 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 abovethe controls to allow additions that will be made in Lab 2.

    http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab3http://www.oracle.com/technetwork/articles/cook-vs08-088541.html#lab3
  • 8/8/2019 .NET Data Provider

    13/21

    Figure 9Lab 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 aclick event handler for the button. The easiest way to get started with this task is to double click the buttonbecause it will create a stub for the event handler.

  • 8/8/2019 .NET Data Provider

    14/21

    Figure 10Click event handler stub

    3. Add Visual Basic Imports statements before the Public Class declaration or C# using statements before thenamespace declaration.

    4. Imports Oracle.DataAccess.Client ' Visual Basic, ODP.NET Oracle managedprovider

    5.6. using Oracle.DataAccess.Client; // C#, ODP.NET Oracle managed provider7. 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 Basic14. conn.Open()15.16. Dim cmd As New OracleCommand17. cmd.Connection = conn

  • 8/8/2019 .NET Data Provider

    15/21

  • 8/8/2019 .NET Data Provider

    16/21

    Figure 11Data 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 theapplication. 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 ofTextBox1 isn't set toanything.

  • 8/8/2019 .NET Data Provider

    17/21

    Figure 12Lab 2 form with button and label controls

    2. Modify the code that defines the select string:

    cmd.CommandText = "select dname from dept where deptno = " +TextBox1.Text 'VBcmd.CommandText = "select dname from dept where deptno = " +textBox1.Text; // C#>

    3. Run the application. Test the application by entering 10 for the DEPTNO. Retest the application by enteringan invalid DEPTNO (e.g., 50). The application will abort.

  • 8/8/2019 .NET Data Provider

    18/21

    Figure 13An unhandled exception

    4. Modify your code to prevent an error when an invalid DEPTNO is entered. Recall that the ExecuteReadermethod actually returns an object. Replace the line containing dr.Read with all of the following statements.

    If dr.Read() Then ' Visual BasicLabel1.Text = dr.Item("dname").ToString()ElseLabel1.Text = "deptno not found"End If

    if (dr.Read()) // C#

    {label1.Text = dr["dname"].ToString();;}else{label1.Text = "deptno not found";}

    Test the application by entering a DEPTNO that does not exist. Now the application no longer aborts. Enterthe letter A instead of a number and click the button. The application aborts. Clearly, our application needs a

  • 8/8/2019 .NET Data Provider

    19/21

    better approach to handling errors.

    Although it could be argued that the application should not allow the user to make invalid inputs that wouldcause an error, ultimately the application must have robust error handling added. Not all errors arepreventable, 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 anOracleDataReader. 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 14Form with ListBox added

  • 8/8/2019 .NET Data Provider

    20/21

    2. Remove the where clause from the query and add the additional columns:

    cmd.CommandText = "select deptno, dname, loc from dept" ' Visual Basiccmd.CommandText = "select deptno, dname, loc from dept"; // C#

    3. The query results will be read in a while loop and will populate the ListBox control. Modify your VisualBasic code to look like this:

    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;"

    Dim conn As New OracleConnection(oradb) ' Visual Basicconn.Open()

    Dim cmd As New OracleCommandcmd.Connection = conn

    cmd.CommandText = "select deptno, dname, loc from dept"cmd.CommandType = CommandType.Text

    Dim dr As OracleDataReader = cmd.ExecuteReader()While dr.Read()

    ListBox1.Items.Add("The " + dr.Item("dname") + _" department is in " + dr.Item("loc"))

    End While

    dr.Dispose()cmd.Dispose()conn.Dispose()Modify your C# code to look like this: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 deptno, dname, loc from dept";cmd.CommandType = CommandType.Text;

    OracleDataReader dr = cmd.ExecuteReader();

    while (dr.Read()){

    listBox1.Items.Add("The " + dr["dname"].ToString() +" department is in " + dr["loc"].ToString());

    }

    dr.Dispose();cmd.Dispose();conn.Dispose();

  • 8/8/2019 .NET Data Provider

    21/21

    4. Run the application. The ListBox should be populated with all of the department names and locations fromthe DEPT table. The code downloads have error handling implemented.

    John Paul Cook [[email protected]] is a database and .NET consultant based in Houston. He is the author of

    numerous articles on .NET, Oracle, and virtualization and has been developing relational database applications since1986. His current interests include Visual Studio 2008 and Oracle Database 11g. He is an Oracle certified DBA and a

    Microsoft Most Valuable Professional.

    mailto:[email protected]:[email protected]:[email protected]:[email protected]