Using PL - SQL From
Using PL - SQL From
CHAPTER 5
■■■
I n a sense, this chapter represents a slight shift in focus from the previous chapters; here, you
step out of the .NET environment, to a degree, and delve more deeply into the database itself.
The capabilities afforded by Oracle PL/SQL are often overlooked, especially when you’re using
a development environment outside of the database. This chapter isn’t about becoming a
PL/SQL expert or dissecting advanced PL/SQL techniques; it gives you enough information to
enable you to start using PL/SQL with your .NET programs. The primary focus isn’t on creating
PL/SQL, but on using it. It isn’t possible to provide a comprehensive examination of everything
PL/SQL has to offer in a single chapter—entire books are devoted to that subject. Therefore,
your goal in this chapter is to understand how to effectively use PL/SQL to perform common
tasks from a .NET program. Specifically, you examine the following topics:
Why use PL/SQL?: You aren’t required to use PL/SQL per se, though there are benefits to
using it. In this section, you learn why you may find using PL/SQL beneficial.
PL/SQL Packages: Packages and package bodies are an important aspect of PL/SQL.
You get to explore how to use packages and package bodies when creating stored
PL/SQL code.
Anonymous PL/SQL Blocks: It’s possible to use PL/SQL code that isn’t stored in the data-
base. Using anonymous blocks is the mechanism used to accomplish this.
Parameters and Return Values: As with plain SQL statements, you can pass parameters
to as well as receive return values from PL/SQL. You’ll work through a sample using the
OracleParameter class that illustrates how to do this.
The Ref Cursor PL/SQL Data Type: You use a Ref Cursor to pass a pointer to a result set
on the server to a client. In this section, you develop a familiarity with what Ref Cursors
are and how to declare them.
Returning Result Sets from PL/SQL: In this section, I use the Ref Cursor PL/SQL data
type and the OracleRefCursor data provider class to illustrate how to pass a pointer that
points to a server-based result set to a .NET client program.
Using PL/SQL Associative Arrays: PL/SQL Associative Arrays are arrays PL/SQL can use.
In this section, you create a sample application that uses PL/SQL Associative Arrays
rather than the host language arrays you used in Chapter 3.
203
4258_Ch05_CMP3 10/5/04 7:54 AM Page 204
■NOTE A number of PL/SQL components are part of a database installation and are available for general
use. These components are published in the “PL/SQL Packages and Types” reference manual included in the
Oracle documentation set. In addition, the “PL/SQL User’s Guide and Reference” is valuable. If you need to
brush up on your PL/SQL skills, I recommend consulting these guides. In addition, Mastering Oracle PL/SQL:
Practical Solutions (Apress, 2004) is an excellent resource.
■NOTE Consult the PL/SQL manuals for information on the %type and %ROWtype attributes, which auto-
matically allow declaration of a PL/SQL variable with the correct data type based on the table structure.
When you use this method of declaring variables and the underlying length or type of the column changes,
this change is transparent to your PL/SQL code and requires no (or at least reduced) code rework.
To declare a variable of the same type and length as a table column, declare the variable of
type tablename.column and append %type. For example, say you have a table named EMPLOYEES
and that table has a column called LAST_NAME that holds an employee’s last name. To declare the
variable l_emp_last_name of this type, you simply declare it as l_emp_last_name EMPLOYEES.LAST_
NAME%type. In PL/SQL, the variable type follows the variable name rather than preceding it. If
you increase the length of the LAST_NAME column from 30 to 48 characters, this change can be
transparent to your PL/SQL code that uses this table. In this case, the l_emp_last_name variable
automatically increases in length to 48. You’ll see an example of this as you work through the
code in this chapter.
You can use PL/SQL as a security tool as well. It’s possible to create a PL/SQL procedure
that returns data from a database table to which the user has no direct access. Because the
user has no access to the table directly, they aren’t able to browse the table using an external
tool. Of course, the user needs the appropriate permissions to execute the PL/SQL procedure
for this scenario to work correctly, but this is easily accomplished by the administrator of the
database.
As you saw when we discussed the FetchSize property in Chapter 2, working with data in
sets rather than as individual rows has some performance benefits. PL/SQL offers an alterna-
tive way to work with data in batches rather than as discrete rows known as associative arrays.
Using this functionality, you can perform array-based operations with PL/SQL instead of host
language array operations. You learn more about this later in the chapter.
4258_Ch05_CMP3 10/5/04 7:54 AM Page 205
Although there are many reasons to use PL/SQL, as a .NET programmer, you may find
that it feels somewhat unnatural to move code out of the .NET environment and into the
database. However, PL/SQL was created for the purpose of working with data in an Oracle
database, and it does a very good job. Recent releases of the database include enhancements
to the PL/SQL compiler and optimizer, which make it even more appealing from a pure per-
formance perspective. The following sections illustrate how using PL/SQL from the .NET
environment is made simple by the data provider.
■TIP When working with PL/SQL procedures, functions, packages, and so forth, you should coordinate
appropriate object permissions with your administrator. Although this applies in general, of course, it is par-
ticularly important when you’re working with PL/SQL code. Database roles, by default, are not active when a
stored procedure is executing; this can be very confusing for you to troubleshoot if the object privileges are
granted to a database role rather than directly to a user. This is fully documented in the “PL/SQL User’s
Guide and Reference” in the supplied documentation.
PL/SQL Packages
A package is a construct that PL/SQL inherited from Ada—the language upon which PL/SQL
is based. In a nutshell, a PL/SQL package is a mechanism for storing or bundling related items
together as a single logical entity. A package is composed of two distinct pieces:
The package specification: Defines what is contained in the package and is akin to a
header file in a language such as C++. Items defined in the specification are public. That
is, code outside of the specification and body can see these items. The specification is the
published interface to a package.
The package body: Contains the code for the procedures and functions defined in the
specification. The body may also contain code not declared in the specification; in this
case, this code is private. Other code in the same body can see and invoke this code, but
code outside of the body can’t.
These two pieces are stored as separate objects in the data dictionary, and they are visible
in the user_source view among others. The specification is stored as the PACKAGE type, and the
body is stored as the PACKAGE BODY type. You see an example of this shortly.
It’s possible to have a specification with no body. It isn’t possible, however, to have a body
with no specification. For example, you can use a specification with no body to declare a set
of public constants; because a set of constants doesn’t need an implementation, a body isn’t
necessary.
When you create a package specification, the optional or replace clause indicates to Ora-
cle that it should replace an existing package specification if one exists. This is a shorthand
method that avoids having to first drop a package specification when you’re recreating it. You
must specify one of the is or as clauses, but not both. I tend to use the as clause when I’m cre-
ating package specifications and bodies and the is clause when I’m creating the procedures
and functions that reside in the body. I do this solely because it makes the text flow better
when I’m reading it (you’ll see an example of this when you create a package body). The
package specification is terminated with the end keyword, and optionally, the package
specification name may follow it.
When you’re creating the package body, the syntax is virtually identical to that of the
package specification. The only difference in the syntax between the two is the addition of
the body keyword to the create clause. Here is the syntax for creating a package body:
Of course, an empty specification and body aren’t of much use. Therefore, I’ll briefly
examine the syntax you’d use to create a procedure or function within the package body.
As you may imagine, the syntax to accomplish this is fairly similar to that of the specification
and body:
function <function name> ([<function parameters>]) return <return type> [is | as]
<variable declarations>
begin
<function body>
[exception]
<exception block if exception keywork used>
end [function name];
You may notice that the exception block is an optional component of a procedure or
function. The PL/SQL code you develop in this book doesn’t make use of this block. Instead
you’ll let any exceptions raised inside a PL/SQL block of code propagate back to your .NET
programs. For additional information on this block, see the Oracle-supplied documentation
or Mastering Oracle PL/SQL: Practical Solutions, which I mentioned previously.
Now that you’ve seen the syntax you need to create a package specification, a package
body, and a procedure or function, you can create a simple specification and body. In addi-
tion, you can query the data dictionary view user_source to see some information related to
the specification and body. This simple package contains a public variable and a procedure
that displays the value of that variable in the SQL*Plus command window. Listing 5-1 contains
the code to create the package specification.
4258_Ch05_CMP3 10/5/04 7:54 AM Page 207
C:\>sqlplus oranetuser@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, Oracle Label Security, OLAP and Data Mining options
Package created.
As you can see in Listing 5-1, this code creates a simple package specification that con-
tains one variable and declares one procedure that will be in the package body. The code in
Listing 5-2 is used to create the package body.
As you can see, Listing 5-2 ends with the package body being successfully created. This
procedure does nothing more than invoke an Oracle-supplied PL/SQL procedure called
put_line, which resides in the dbms_output package. The l_hello_text variable is passed as
a parameter to this procedure. This package is fully documented in the “PL/SQL Packages
and Types” manual in the Oracle documentation set.
Now that you’ve created the specification and body, you can invoke the procedure from
your SQL*Plus session, as illustrated in Listing 5-3. Here is the syntax you use to call a pack-
aged procedure: <owner>.<package>.<procedure or function name>. The <owner> is optional if
the user making the call owns the package, as is the case in Listing 5-3.
4258_Ch05_CMP3 10/5/04 7:54 AM Page 208
In Listing 5-3, you first enable output in your SQL*Plus session so that you can see the
output of the procedure. If you execute the procedure and only see the PL/SQL procedure
successfully completed. text, it is likely that serveroutput hasn’t been enabled in SQL*Plus.
After enabling output, invoke your procedure and see the results of displaying the value of the
l_hello_text variable.
You may recall that I mentioned earlier that the package specification is stored as type
PACKAGE in the data dictionary. Listing 5-5 is where you can clearly see this. If you issue a query
similar to that in Listing 5-5, you’ll display the code that makes up the package specification.
Listing 5-5. Displaying the Package Specification as Stored in the user_source View
TEXT
---------------------------------------------------------------------
package simple_pkg as
l_hello_text varchar2(40) := 'Hello from a simple PL/SQL package!';
procedure display_message;
end simple_pkg;
4 rows selected.
4258_Ch05_CMP3 10/5/04 7:54 AM Page 209
In order to see the contents of the package body, you simply need to change the type to
PACKAGE BODY as illustrated in Listing 5-6.
Listing 5-6. Displaying the Package Body as Stored in the user_source View
TEXT
---------------------------------------
package body simple_pkg as
procedure display_message is
begin
dbms_output.put_line(l_hello_text);
end display_message;
end simple_pkg;
6 rows selected.
As you can see, two procedures are declared in the specification, and both are named
tproc. Of course, both versions of the procedure need to be implemented in the package body.
When the PL/SQL run-time engine encounters a call to tproc with no parameters, the first
version of the procedure is invoked. If the PL/SQL run-time engine encounters a call to tproc
with a varchar2 parameter, the second version of the procedure is invoked. You’ll see over-
loading in action in the complete samples later in the chapter.
4258_Ch05_CMP3 10/5/04 7:54 AM Page 210
[declare]
<variable declarations>
begin
<block body>
end;
The declare keyword is optional, but you must specify it if you’re going to use variable
declarations. If the block uses no variables (as was the case in Listing 5-3) this keyword is
omitted. You can see an anonymous block by implementing the simple_pkg as an anonymous
block. Listing 5-7 illustrates this process and its output.
Although anonymous blocks are probably most frequently used to invoke other proce-
dures or functions, you can also use them to batch SQL statements as a single group. You can
see this in Listing 5-8.
Table created.
SQL> begin
2 insert into anon_test (name_id, name) values (1, 'John');
3 insert into anon_test (name_id, name) values (2, 'Paul');
4 insert into anon_test (name_id, name) values (3, 'George');
5 insert into anon_test (name_id, name) values (4, 'Ringo');
6 end;
7 /
SQL> commit;
Commit complete.
NAME_ID NAME
---------- ------------
1 John
2 Paul
3 George
4 Ringo
4 rows selected.
Here is a question that comes up frequently: “What would happen if one of the batched
statements violated the primary key?” As you discovered in Chapter 3, Oracle transactions
either successfully complete or fail as a whole. Therefore, if one of the batched statements
violates the primary key constraint, the entire anonymous block is rolled back. You can see
this in Listing 5-9.
Listing 5-9. A Single Statement Causes the Entire Block to Be Rolled Back
1 begin
2 insert into anon_test (name_id, name) values (5, 'Micky');
3 insert into anon_test (name_id, name) values (6, 'Michael');
4 insert into anon_test (name_id, name) values (7, 'Peter');
5 insert into anon_test (name_id, name) values (1, 'David');
6* end;
SQL> /
begin
*
ERROR at line 1:
4258_Ch05_CMP3 10/5/04 7:54 AM Page 212
NAME_ID NAME
---------- --------------------------------
1 John
2 Paul
3 George
4 Ringo
4 rows selected.
SQL>
Even though only one of the name_id values in the anonymous block violated the primary
key (the statement on line 5 in the anonymous block), the entire block is rolled back.
C:\>sqlplus oranetuser@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
Table created.
In order to illustrate how to use the different parameter directions and return a value
from a function, I’ll show you how to create a PL/SQL package, called league_test, which
contains the following three procedures and one function:
Insert_row: Allows you to populate the sample table using input parameters.
Listing 5-11 shows the package specification where you declare the procedures and func-
tion and the package body that contains them. I created this code in the same SQL*Plus session
as the table in Listing 5-10.
8 p_goals_for in number,
9 p_goals_against in number);
10
11 procedure retrieve_row(p_position in number,
12 p_team out varchar2,
13 p_played out number,
14 p_wins out number,
15 p_draws out number,
16 p_losses out number,
17 p_goals_for out number,
18 p_goals_against out number);
19
20 procedure calculate_points(p_inout in out number);
21
22 function get_team(p_position in number) return varchar2;
23 end league_test;
24 /
Package created.
29
30 procedure retrieve_row(p_position in number,
31 p_team out varchar2,
32 p_played out number,
33 p_wins out number,
34 p_draws out number,
35 p_losses out number,
36 p_goals_for out number,
37 p_goals_against out number) is
38 begin
39 -- this returns the columns for a given position in the table
40 select team,
41 played,
42 wins,
43 draws,
44 losses,
45 goals_for,
46 goals_against
47 into p_team,
48 p_played,
49 p_wins,
50 p_draws,
51 p_losses,
52 p_goals_for,
53 p_goals_against
54 from league_results
55 where position = p_position;
56 end retrieve_row;
57
58 procedure calculate_points(p_inout in out number) is
59 begin
60 -- this returns the number of points for a given position
61 -- in the table
62 -- points are calculated as:
63 -- 3 points for a win
64 -- 1 point for a draw
65 -- 0 points for a loss
66 select (wins * 3) + (draws)
67 into p_inout
68 from league_results
69 where position = p_inout;
70 end calculate_points;
71
72 function get_team(p_position in number) return varchar2 is
73 l_team league_results.team%type;
74 begin
75 -- simply get the team for a given position
76 select team
4258_Ch05_CMP3 10/5/04 7:54 AM Page 216
77 into l_team
78 from league_results
79 where position = p_position;
80
81 return l_team;
82 end get_team;
83 end league_test;
84 /
At this point, you’re ready to create your .NET code that instantiates the Main class and
invokes a series of methods to utilize the procedures and the function that you created. You’ll
create a console application to accomplish this, and, as with your other code, you’ll use some
helper methods to separate and modularize the code. This sample (Parameters) is available
in this chapter’s folder in the Downloads section of the Apress website (www.apress.com).
The Main procedure for your sample is presented in Listing 5-12. This procedure simply
creates a connection to the database and calls your helper methods.
oraConn.Open();
oraConn.Close();
oraConn.Dispose();
}
4258_Ch05_CMP3 10/5/04 7:54 AM Page 217
// assign values
p_position.Value = position;
p_team.Value = team;
p_played.Value = played;
p_wins.Value = wins;
p_draws.Value = draws;
p_losses.Value = losses;
p_goals_for.Value = goals_for;
p_goals_against.Value = goals_against;
cmd.Parameters.Add(p_played);
cmd.Parameters.Add(p_wins);
cmd.Parameters.Add(p_draws);
cmd.Parameters.Add(p_losses);
cmd.Parameters.Add(p_goals_for);
cmd.Parameters.Add(p_goals_against);
p_team.Direction = ParameterDirection.Output;
p_played.Direction = ParameterDirection.Output;
4258_Ch05_CMP3 10/5/04 7:54 AM Page 220
p_wins.Direction = ParameterDirection.Output;
p_draws.Direction = ParameterDirection.Output;
p_losses.Direction = ParameterDirection.Output;
p_goals_for.Direction = ParameterDirection.Output;
p_goals_against.Direction = ParameterDirection.Output;
p_team.Size = 32;
input/output parameter with a numeric value in Listing 5-16, you can also use them for text
transformation. For example, you can pass a text value to a procedure, and the procedure can
transform the text in some manner, such as by performing encryption or creating a checksum
value. A numeric accumulator is also something that you can easily implement using an
input/output parameter.
Console.WriteLine();
}
C:\My Projects\ProOraNet\Oracle\C#\Chapter05\Parameters\bin\Debug>
Parameters.exe
Executing input parameter sample...
Table successfully loaded.
Played: 38
Wins: 16
Draws: 12
Losses: 10
Goals For: 55
Goals Against: 37
C:\My Projects\ProOraNet\Oracle\C#\Chapter05\Parameters\bin\Debug>
In the output in Listing 5-19, you can clearly see the results of your procedures and func-
tion that return values from the database. However, you don’t see the full results of the input
parameter code that loads the table. In this listing, I’ve slightly reformatted the output to pre-
vent line wrapping.
20 rows selected.
SQL>
4258_Ch05_CMP3 10/5/04 7:54 AM Page 224
■NOTE The OracleRefCursor class is used with Ref Cursors in the Oracle data provider. You can
access the Ref Cursors via the OracleDataReader class in the Microsoft data provider.
The preceding code creates a weakly typed cursor type since no return type was defined.
In order to create a strongly typed cursor type, you’ll need to use code similar to the following:
In this code, a strongly typed cursor type is created. This Ref Cursor can only return a
result set that has the structure of the LEAGUE_RESULTS table. The primary advantage of
a weakly typed cursor is that you can use it to return any type of result set since no return
type was defined. On the other hand, the PL/SQL compiler can check that a strongly typed
cursor is associated only with queries that return the correct columns and column types.
■NOTE If you know the type that a cursor needs to return, use a strongly typed cursor. If you don’t, use
a weakly typed cursor. For example, if the cursor returns data from a table whose structure you know at
design time, you can use a strongly typed cursor. If the cursor returns data from a table that a user chooses
dynamically at run-time, use a weakly typed cursor.
To return the Ref Cursor from the database, you create a new package and package body
as illustrated in Listing 5-20. You can also return a Ref Cursor using an anonymous block. The
technique is the same as I illustrate here. The only difference is that the code resides in an
anonymous block instead of a package. Here, you’re using a get_table procedure and get_table
function to illustrate the two different means of returning your cursor. The get_table routine is
overloaded, and when the package code executes, the PL/SQL run-time determines (based on
the different signatures of the two routines) the appropriate routine to invoke. I discussed over-
loading earlier in the chapter.
C:\>sqlplus oranetuser@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, Oracle Label Security, OLAP and Data Mining options
Package created.
15 order by position;
16
17 return l_cursor;
18 end get_table;
19
20 procedure get_table (p_cursor out ref_cursor) is
21 begin
22 open p_cursor for
23 select position,
24 team,
25 played,
26 wins,
27 draws,
28 losses,
29 goals_for,
30 goals_against
31 from league_results
32 order by position;
33 end get_table;
34 end league_rc;
35 /
In the PL/SQL code, you can see that you’re declaring Ref_Cursor as a strongly typed Ref
Cursor type. This is how you return the cursor to your .NET client code. In each routine, the
cursor opens using a basic SELECT statement that retrieves and orders all of the rows in the
LEAGUE_RESULTS table. This is an area where PL/SQL excels. Due to the tight integration of
SQL and PL/SQL, you’re able to embed SQL statements in your PL/SQL code with no modifi-
cations.
The Main method for your console application, which is the RefCursor solution in this
chapter’s folder in the Downloads section of the Apress website (www.apress.com), is presented
in Listing 5-21. As you can see, it is similar to the previous code you’ve examined.
oraConn.Open();
oraConn.Close();
oraConn.Dispose();
}
cmd.Parameters.Add(p_refcursor);
while (reader.Read())
{
Console.Write(reader.GetDecimal(0).ToString() + ",");
Console.Write(reader.GetString(1) + ",");
Console.Write(reader.GetDecimal(2).ToString() + ",");
Console.Write(reader.GetDecimal(3).ToString() + ",");
4258_Ch05_CMP3 10/5/04 7:54 AM Page 229
Console.Write(reader.GetDecimal(4).ToString() + ",");
Console.Write(reader.GetDecimal(5).ToString() + ",");
Console.Write(reader.GetDecimal(6).ToString() + ",");
Console.WriteLine(reader.GetDecimal(7).ToString());
}
Console.WriteLine();
reader.Close();
reader.Dispose();
p_refcursor.Dispose();
cmd.Dispose();
}
cmd.Parameters.Add(p_refcursor);
while (reader.Read())
{
Console.Write(reader.GetDecimal(0).ToString() + ",");
Console.Write(reader.GetString(1) + ",");
Console.Write(reader.GetDecimal(2).ToString() + ",");
4258_Ch05_CMP3 10/5/04 7:54 AM Page 230
Console.Write(reader.GetDecimal(3).ToString() + ",");
Console.Write(reader.GetDecimal(4).ToString() + ",");
Console.Write(reader.GetDecimal(5).ToString() + ",");
Console.Write(reader.GetDecimal(6).ToString() + ",");
Console.WriteLine(reader.GetDecimal(7).ToString());
}
Console.WriteLine();
reader.Close();
reader.Dispose();
p_refcursor.Dispose();
cmd.Dispose();
}
C:\My Projects\ProOraNet\Oracle\C#\Chapter05\RefCursor\bin\Debug>RefCursor.exe
Invoking ref cursor function...
1,Arsenal,38,26,12,0,73,26
2,Chelsea,38,24,7,7,67,30
3,Manchester United,38,23,6,9,64,35
4,Liverpool,38,16,12,10,55,37
5,Newcastle United,38,13,17,8,52,40
6,Aston Villa,38,15,11,12,48,44
7,Charlton Athletic,38,14,11,13,51,51
8,Bolton Wanderers,38,14,11,12,48,56
9,Fulham,38,14,10,14,52,46
10,Birmingham City,38,12,14,12,43,48
11,Middlesbrough,38,13,9,16,44,52
12,Southampton,38,12,11,15,44,45
13,Portsmouth,38,12,9,17,47,54
14,Tottenham Hotspur,38,13,6,19,47,57
15,Blackburn Rovers,38,12,8,18,51,59
16,Manchester City,38,9,14,15,55,54
17,Everton,38,9,12,17,45,57
18,Leicester City,38,6,15,17,48,65
19,Leeds United,38,8,9,21,40,79
20,Wolverhampton Wanderers,38,7,12,19,38,77
4258_Ch05_CMP3 10/5/04 7:54 AM Page 231
C:\My Projects\ProOraNet\Oracle\C#\Chapter05\RefCursor\bin\Debug>
■NOTE The PL/SQL Associative Array feature isn’t available in the current version of the Microsoft
data provider.
This creates an in-memory structure that is a set of key and value pairs. Specifying the
index by binary_integer clause means that the key is an integer value. The value associated
with each integer key is of type <data type>, which you specified when you created the type.
For instance, to create an associative array that has integer key values and varchar2 data val-
ues with a maximum length of 32, you use a statement such as the following:
■NOTE In version 9i and later, you can also specify varchar2 as the index by instead of binary_inte-
ger. This is documented in the PL/SQL User’s Guide and Reference shipped with the Oracle documentation.
Rather than specifying specific data types, you can also use the %type keyword we’ve dis-
cussed previously. This is illustrated by the following:
The type created in here is of the same data type as the team column in the
LEAGUE_RESULTS table.
You can access individual key/value pairs in the array by using a subscript operator and
specifying the index value. For example, using the t_assoc_array type and an anonymous
block, you can create a variable of this type and access individual elements as illustrated in
Listing 5-25.
■NOTE Two dashes (--) are used to indicate a single line comment in PL/SQL. You can also use the C
language convention of /* and */ for multiline comments.
SQL> declare
2 -- create the type
3 type t_assoc_array is table of varchar2(32) index by binary_integer;
4 -- create a variable of that type
5 l_assoc_variable t_assoc_array;
6 -- another variable to assign the value
7 l_temp varchar2(32);
8 begin
9 -- add an element to the array
10 l_assoc_variable(1) := 'An associative array value';
11 -- get the element from the array
12 l_temp := l_assoc_variable(1);
13 -- display the value
14 dbms_output.put_line(l_temp);
15 end;
16 /
An associative array value
In order to determine the index value for the first or last element in an associative array,
you can use the first and last properties. These supply the equivalent of the lower and upper
bounds of a .NET array. Listing 5-26 illustrates accessing these properties.
Listing 5-26. Accessing the first and last Properties of an Associative Array
SQL> declare
2 -- create the type
3 type t_assoc_array is table of varchar2(32) index by binary_integer;
4258_Ch05_CMP3 10/5/04 7:54 AM Page 233
If you use the t_assoc_array and the l_assoc_variable from the previous section, an
insert operation using bulk binding into a table named t would resemble the following code
snippet:
forall x in l_assoc_variable.first..l_assoc_variable.last
insert into t values (l_assoc_variable(x));
Using bulk binding with a select operation works in a similar manner. Again if you use the
same associative array and variable, a bulk select would be similar to the following:
select column
bulk collect into l_assoc_variable
from t
order by t;
4258_Ch05_CMP3 10/5/04 7:54 AM Page 234
bulk_insert: This procedure performs a bulk insert (using an associative array) of data in
the LEAGUE_RESULTS table.
bulk_select: This procedure performs a bulk select (using an associative array) of data
from the LEAGUE_RESULTS table.
In the package declaration, you create a type for each column in the database table. This
serves as your array inside of the PL/SQL code. You marshal data between the database and
your .NET code using OracleParameter objects and these PL/SQL arrays. The PL/SQL proce-
dures themselves are quite simple. They simply insert all of the values in the passed-in array
into the LEAGUE_RESULTS table, or they return all of the data in the table to your client code.
C:\>sqlplus oranetuser@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, Oracle Label Security, OLAP and Data Mining options
10 index by binary_integer;
11 type t_draws is table of league_results.draws%type
12 index by binary_integer;
13 type t_losses is table of league_results.losses%type
14 index by binary_integer;
15 type t_goals_for is table of league_results.goals_for%type
16 index by binary_integer;
17 type t_goals_against is table of league_results.goals_against%type
18 index by binary_integer;
19
20 -- the procedures that will perform our work
21 procedure bulk_insert (p_position in t_position,
22 p_team in t_team,
23 p_played in t_played,
24 p_wins in t_wins,
25 p_draws in t_draws,
26 p_losses in t_losses,
27 p_goals_for in t_goals_for,
28 p_goals_against in t_goals_against);
29
30 procedure bulk_select (p_position out t_position,
31 p_team out t_team,
32 p_played out t_played,
33 p_wins out t_wins,
34 p_draws out t_draws,
35 p_losses out t_losses,
36 p_goals_for out t_goals_for,
37 p_goals_against out t_goals_against);
38 end league_associative;
39 /
Package created.
SQL>
SQL> create or replace package body league_associative as
2 procedure bulk_insert (p_position in t_position,
3 p_team in t_team,
4 p_played in t_played,
5 p_wins in t_wins,
6 p_draws in t_draws,
7 p_losses in t_losses,
8 p_goals_for in t_goals_for,
9 p_goals_against in t_goals_against) is
10 begin
11 forall i in p_position.first..p_position.last
12 insert into league_results (position,
13 team,
14 played,
4258_Ch05_CMP3 10/5/04 7:54 AM Page 236
15 wins,
16 draws,
17 losses,
18 goals_for,
19 goals_against)
20 values (p_position(i),
21 p_team(i),
22 p_played(i),
23 p_wins(i),
24 p_draws(i),
25 p_losses(i),
26 p_goals_for(i),
27 p_goals_against(i));
28 end bulk_insert;
29
30 procedure bulk_select (p_position out t_position,
31 p_team out t_team,
32 p_played out t_played,
33 p_wins out t_wins,
34 p_draws out t_draws,
35 p_losses out t_losses,
36 p_goals_for out t_goals_for,
37 p_goals_against out t_goals_against) is
38 begin
39 select position,
40 team,
41 played,
42 wins,
43 draws,
44 losses,
45 goals_for,
46 goals_against
47 bulk collect into p_position,
48 p_team,
49 p_played,
50 p_wins,
51 p_draws,
52 p_losses,
53 p_goals_for,
54 p_goals_against
55 from league_results
56 order by position;
57 end bulk_select;
58 end league_associative;
59 /
SQL>
4258_Ch05_CMP3 10/5/04 7:54 AM Page 237
Now that you have successfully created your PL/SQL code, you can create the familiar
looking Main method as illustrated in Listing 5-28. Here, you’ll use an addition to the source
code file that you haven’t seen before; you include using Oracle.DataAccess.Types; at the top
of the source code file. Although I’ve included this namespace in the template applications in
the code download, this is the first time you’ve expressly needed to use it. You need to deter-
mine what type of value your parameter represents in the code that displays the data in the
console window, and including the Types enumeration in your source file makes this easier.
oraConn.Open();
oraConn.Close();
oraConn.Dispose();
}
cmd.ExecuteNonQuery();
}
p_team.OracleDbType = OracleDbType.Varchar2;
p_played.OracleDbType = OracleDbType.Decimal;
p_wins.OracleDbType = OracleDbType.Decimal;
p_draws.OracleDbType = OracleDbType.Decimal;
p_losses.OracleDbType = OracleDbType.Decimal;
p_goals_for.OracleDbType = OracleDbType.Decimal;
p_goals_against.OracleDbType = OracleDbType.Decimal;
p_played.Value = new decimal[20]{38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
38, 38, 38, 38, 38, 38, 38, 38, 38, 38};
p_wins.Value = new decimal[20]{26, 24, 23, 16, 13, 15, 14, 14, 14, 12, 13,
12, 12, 13, 12, 9, 9, 6, 8, 7};
p_draws.Value = new decimal[20]{12, 7, 6, 12, 17, 11, 11, 11, 10, 14, 9,
11, 9, 6, 8, 14, 12, 15, 9, 12};
p_goals_for.Value = new decimal[20]{73, 67, 64, 55, 52, 48, 51, 48, 52, 43,
44, 44, 47, 47, 51, 55, 45, 48, 40, 38};
4258_Ch05_CMP3 10/5/04 7:54 AM Page 240
p_goals_against.Value = new decimal[20]{26, 30, 35, 37, 40, 44, 51, 56, 46, 48,
52, 45, 54, 57, 59, 54, 57, 65, 79,
77};
needs to determine what type of value a parameter object represents so that it may be output
to the console window correctly. By including the namespace, your code is slightly more com-
pact. The loop determines the number of iterations your code must make to retrieve the size
attribute from the p_position parameter. All the parameters have the same number of ele-
ments. Once the number of overall iterations is determined, you iterate the parameter
collection to display the parameter values for each row returned.
p_team.Direction = ParameterDirection.Output;
p_played.Direction = ParameterDirection.Output;
p_wins.Direction = ParameterDirection.Output;
p_draws.Direction = ParameterDirection.Output;
p_losses.Direction = ParameterDirection.Output;
p_goals_for.Direction = ParameterDirection.Output;
p_goals_against.Direction = ParameterDirection.Output;
if (field_count < 8)
{
Console.Write(",");
}
else
{
field_count = 0;
}
field_count++;
}
Console.WriteLine();
}
}
C:\My Projects\ProOraNet\Oracle\C#\Chapter05\Associative\bin\Debug>
Associative.exe
Truncating table...
Completed truncating table...
C:\My Projects\ProOraNet\Oracle\C#\Chapter05\Associative\bin\Debug>
Chapter 5 Wrap-Up
As I indicated at the beginning of the chapter, this chapter isn’t about becoming a PL/SQL
expert. However, I designed it to illustrate a topic that is sometimes (or often?) overlooked, yet
one that provides a programming technique that is no more difficult than embedding SQL
directly into your client code. I showed you some areas that you’ll most often encounter when
you use PL/SQL in conjunction with .NET code. Specifically I addressed the following:
Reasons for using PL/SQL: Even though you aren’t required to use PL/SQL, it has its ben-
efits. We examined these.
Using PL/SQL packages: Packages and package bodies are an important aspect of
PL/SQL. You saw how to use these when you’re creating stored PL/SQL code.
Working with anonymous PL/SQL blocks: You learned how to use anonymous blocks to
invoke code stored in the database and to batch SQL statements.
Applying parameters and return values: You learned to pass parameters to and receive
return values from PL/SQL. You built a sample using the OracleParameter class that illus-
trates how to do this.
Employing the Ref Cursor PL/SQL data type: You learned that using this data type is a
way to pass a pointer to a result set on the server to a client. In this section, you became
familiar with and learned to understand Ref Cursors and how you declare them.
4258_Ch05_CMP3 10/5/04 7:54 AM Page 245
Returning result sets from PL/SQL: You learned how to apply your newfound knowledge
of the Ref Cursor PL/SQL data type and the OracleRefCursor data provider class to pass a
pointer to a server-based result set to a .NET client program.
Using PL/SQL Associative Arrays: You saw how to create a sample application that uses
PL/SQL Associative Arrays rather than using host language arrays. You also became famil-
iar with performing bulk binding operations.
As you saw in Chapter 4, pragmatism and context sense are important aspects of a develop-
ment effort. Using PL/SQL appropriately can serve to separate the logic and processes that deal
directly with the data in a database from the logic and processes that deal with client-side issues.
In addition to this separation of duties, the PL/SQL compiler compiles and optimizes code
stored in PL/SQL packages once rather than possibly submitting and optimizing it multiple
times, as may happen with embedded code that resides in the client. As you’ll see in Chapter 6,
sometimes using a supplied PL/SQL package is the easiest way to accomplish a task.
4258_Ch05_CMP3 10/5/04 7:54 AM Page 246
4258_Ch07_CMP2 10/4/04 8:28 PM Page 285
CHAPTER 7
■■■
Advanced Connections
and Authentication
I n this chapter, you’ll revisit the topic of connections, which I first addressed in Chapter 1,
as well as look at some additional authentication methods above and beyond the basic user-
name/password method that you’ve used up to this point. You’ll examine the difference
between privileged and non-privileged connections and learn how to connect to the
database using a privileged connection.
A topic you may find confusing is how to use operating system authentication with the
Oracle database. In this chapter, you work through the process of configuring the system to
allow for Windows operating system authentication and create an operating system–authenti-
cated connection. One of the underutilized features of the Oracle database is its ability to
implement password expiration, password lockout and password changing. I’ll address these
topics and round out the chapter by examining connection pooling and multiplexing.
By this point, you’re very familiar with the username/password method of authenticating
to the database using the Connection object because that is the method all of the sample code
has used thus far. One clear characteristic of this connection method is that the user must
provide a username and password. In turn, the password is (ideally) maintained—that is, the
password is changed on a regular basis. Sometimes some users see this maintenance as a
chore or optional task. As a result, the administrator can implement password rules to enforce
things such as required password changes. However, when you use operating system authen-
tication, this activity becomes unnecessary in the database. When you use operating system
authentication, you no longer need to maintain passwords in the database. In fact, you can’t
maintain them in the database because no password information is stored there.
Another type of connection that you haven’t yet seen is what is known as a privileged
connection. Of course, all connections have privileges of some sort. When I’m discussing privi-
leged connections I mean a connection that has one of two system privileges:
The SYSDBA privilege: This is the highest administrative privilege an account can have.
This privilege allows the user to perform all activities on a database.
The SYSOPER privilege: This privilege has slightly fewer abilities than the SYSDBA privi-
lege. For example, a user connected with this privilege can’t create a new database and
may only perform certain types of database recovery operations.
285
4258_Ch07_CMP2 10/4/04 8:28 PM Page 286
In practice, typical .NET applications don’t need to connect to a database with the SYSDBA
or SYSOPER privileges. However, you may encounter times when you’re creating an application
that does need to connect with one of these privileges. For instance, an application that starts
up or shuts down a database needs to use the SYSOPER privilege. Such an application can use
the SYSDBA privilege, but that is an over-privileging if the application doesn’t need to perform
activities other than start up or shut down. If you’re creating an application that creates an Ora-
cle database from within the application, then you need to connect with the SYSDBA privilege
because the SYSOPER privilege doesn’t allow for this capability.
You’ll also take a look at the ability to connect to the default database. I find this most
useful in situations where I don’t know the connection information for a database in advance.
Another situation where this may come in handy is when databases are in different environ-
ments, such as a Sandbox, a Development, and a Test environment. When you allow the
application to connect to the default database, you aren’t required to configure the network-
ing components as you did in Chapter 1 when you created your standard configuration. Of
course, whether or not you want this is a question you and your database administrator need
to discuss. I believe this is convenient or a shortcut that you should use where appropriate in
your environment.
The Registry: You can set the ORACLE_SID under the Oracle Home registry hive. On my
system, the ORACLE_SID for my 10g installation is set under the HKLM\SOFTWARE\ORACLE\
KEY_OraDatabase101 key.
The Environment Variables dialog: You can set the ORACLE_SID in the System Variables
section.
The Environment Variables dialog: You can set the ORACLE_SID in the User Variables
section.
In a Command Prompt window: Using the set command, you can set the ORACLE_SID
value. For example, set ORACLE_SID=LT10G.
The locations where you may specify the value are listed in hierarchical order from lowest
precedence to highest. For example, if the value is set in the registry and also in the User Vari-
ables section of the Environment Variables dialog, the value specified in the User Variables
section takes precedence over the value specified in the registry.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 287
If you need to set values for the Oracle environment variables, I strongly recommend
that you set them in the registry and then override them in a command prompt window if
you need to. If you choose to set them using the Environment Variables dialog, be aware that
it may cause unexpected behavior in the Oracle utilities that can be hard to debug. When
multiple versions of the Oracle software are installed on a system, Oracle determines the envi-
ronment or profile for each version based on the entries specified under the registry hive for
each version of the software. This effectively segregates the environment configuration for
each version into distinct profiles. When you specify a configuration variable using the Envi-
ronment Variables dialog, that value overrides any entries in the registry for all versions of the
software installed.
As a practical example, let’s look at my system; I have Oracle versions 8i, 9i, and 10g
installed. Under the registry keys for each of these versions, I’ve specified the ORACLE_SID for
the corresponding version of the database: LT8I for the 8i database, LT9I for the 9i database,
and LT10G for the 10g database. If I start the 9i version of SQL*Plus, Oracle uses the LT9I value
for the ORACLE_SID because it’s specified under the 9i home registry hive. If I specify the
ORACLE_SID using the Environment Variables dialog as LT10G, when I start the 9i version of
SQL*Plus, the ORACLE_SID is no longer LT9I; it is LT10G. This means that LT10G has become
my default database for my 9i software. You’ll probably find this confusing, if you are unaware
of what has transpired.
If you need to examine the registry to determine the current value for the ORACLE_SID key,
the key is located in the HKLM\SOFTWARE\ORACLE\KEY_HomeName hive for 10g and the HKLM\SOFTWARE\
ORACLE\HOMEN hive for previous releases. Refer to Chapter 1 if you need a refresher on the Oracle
server architecture.
■TIP Because the ORACLE_SID value is stored in the registry, you can’t issue an echo %ORACLE_SID%
command in a command prompt window to determine the value.
In order to illustrate how to connect to the default database on an Oracle host machine, I
take the “HelloOracle” example I used in Chapter 1 and, with a very slight modification, enable
it to connect to the default database. By doing so, I create a new Visual Studio solution named
DefaultConnection.
■NOTE Be sure to add a reference to the Oracle.DataAccess.dll assembly to the project and include
the using Oracle.DataAccess.Client; directive at the top of the source file if you’re creating a new
solution.
Listing 7-1 contains the modified Main method from the HelloOracle example. You can
find this sample (DefaultConnection) in this chapter’s folder in the Downloads section of the
Apress website (www.apress.com).
4258_Ch07_CMP2 10/4/04 8:28 PM Page 288
Listing 7-1. The Modified Main Method from the HelloOracle Sample
try
{
oraConn.Open();
To take advantage of connecting to the default database, the only change you need to
make to the source code is in the definition of the connection string. By omitting the Data
Source attribute from the connection string, you indicate to the Oracle Data Provider for .NET
that you want to connect to the default database. Listing 7-2 contains the output that results
from running this example.
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\DefaultConnection\bin\Debug>
DefaultConnection.exe
Connection String:
User Id=oranetuser;
4258_Ch07_CMP2 10/4/04 8:28 PM Page 289
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\DefaultConnection\bin\Debug>
Notice in the output that the connection string that displays no longer contains the Data
Source attribute. If you have more than one database on the machine you’re using, it’s easy to
illustrate how you can set the ORACLE_SID environment variable in a command prompt win-
dow to override the value set in the registry. This also illustrates that you are, in fact, connecting
to the default database as specified by the value of this variable. Listing 7-3 contains the output
of the DefaultConnection sample after you change the value of the ORACLE_SID environment
variable in the command prompt window.
Listing 7-3. The Output of the DefaultConnection Example After Changing the ORACLE_SID
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\DefaultConnection\bin\Debug>
set ORACLE_SID=LT8I
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\DefaultConnection\bin\Debug>
DefaultConnection.exe
Connection String:
User Id=oranetuser;
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\DefaultConnection\bin\Debug>
In Listing 7-3, you see that I’ve overridden the value of the ORACLE_SID as specified in
my registry (LT10G) with the value LT8I. The database specified by LT8I is, as I am sure you
have guessed, an Oracle8i database that you can see in the Oracle Database Server Version
informational output.
At this point, you may be asking yourself what would happen if the value for the ORACLE_SID
was missing from the registry and wasn’t specified elsewhere. In order to demonstrate what
happens in this case, I’ve temporarily removed the ORACLE_SID key from my registry. Listing 7-4
illustrates what occurs when this is the case. In this listing, I also explicitly undefine the
ORACLE_SID value in the command prompt window.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 290
■CAUTION If you remove the registry key or set it to no value, be sure you undo this operation immedi-
ately after testing.
Listing 7-4. The Output of the DefaultConnection Sample with No ORACLE_SID Value Set
C:\My Projects\ProOraNet\Chapter07\DefaultConnection\bin\Debug>
DefaultConnection.exe
Error occured: ORA-12560: TNS:protocol adapter error
C:\My Projects\ProOraNet\Chapter07\DefaultConnection\bin\Debug>
As you can see in Listing 7-4, the Oracle client software immediately returns an error when
it is unable to determine a value for the ORACLE_SID variable. To round out your exploration of
connecting to the default database, I’ll illustrate that the value specified for the ORACLE_SID
must be a valid SID. This value can’t be a TNS alias.
Listing 7-5 contains the results of running the sample code with a TNS alias specified in
place of a valid SID. The error generated in this case is the same as when no value is present.
Listing 7-5. The Output of the DefaultConnection Sample with an Invalid ORACLE_SID
C:\My Projects\ProOraNet\Chapter07\DefaultConnection\bin\Debug>
set ORACLE_SID=ORANET
C:\My Projects\ProOraNet\Chapter07\DefaultConnection\bin\Debug>
DefaultConnection.exe
Error occured: ORA-12560: TNS:protocol adapter error
C:\My Projects\ProOraNet\Chapter07\DefaultConnection\bin\Debug>
where oranet is a tnsnames alias. You can take the information that the oranet alias is used to
represent from the tnsnames file and substitute it into the Data Source attribute of the con-
nection string. This results in a more busy connection string for the Connection object, but I
provide some abstraction for this in the sample code. If you use this technique, the connec-
tion string above would become
In order to implement this connection technique, you need to know the host, the port,
and the service name of the database to which you wish to connect. If you aren’t using the
service name to connect to a database, you could use the SID method instead. However,
Oracle recommends the service name method for databases version 8i or later. The service
name method supports Real Application Clusters and Grid systems whereas the SID method is
directed toward single instance/host systems.
An end user can dynamically supply the host, port, and service name parameters at run-
time, or you may specify them in an application-specific configuration file that you create.
However, if you store the information in a configuration file, you are basically duplicating the
creation of an entry in a tnsnames.ora file. As a result, I typically use dynamic values at run-
time. How you get the values at run-time is up to you. An end user can dynamically supply
them in an interactive application, or they you can read them from a database table—in a
noninteractive, system-level application, for example. You’ll implement this connection tech-
nique by creating a Windows Forms–based application, and you’ll supply the required values
interactively at run-time.
You can temporarily disable the tnsnames.ora file by renaming it to ensure that you’re
using your tnsnames-less connection. A new feature in the Oracle9i networking components
(which carries on into 10g as well) is that the command line utilities tell you which file they
used to carry out the request. One of these commands is the tnsping utility. If you’ve ever used
the ping utility, this utility will be very familiar. Instead of pinging any network address, with
the tnsping utility, you ping an Oracle listener service. The utility allows you to verify a path-
way between Point A and Point B. Listing 7-6 illustrates the simple usage of the tnsping utility.
Here, I simply ping the oranet TNS alias that you’ve been using as your standard.
C:\>tnsping oranet
C:\>
As you might recall from Chapter 1, the sqlnet.ora file is an optional file, but if it exists, it
influences the Oracle networking environment. In Listing 7-6, you can see that the Oracle net-
working client has determined that my machine does have a sqlnet.ora file, and it reads it to
extract configuration information. The Used TNSNAMES adapter to resolve the alias infor-
mational message indicates that my sqlnet.ora file contains a directive to use the
tnsnames.ora file to resolve names.
Listing 7-7 shows what happens when the tnsnames.ora file is renamed, thus preventing
the Oracle networking client from using it to resolve names. The sqlnet.ora file has been left
intact.
Listing 7-7. The Results of a tnsping After Renaming the tnsnames.ora File
C:\oracle\10.1\database\network\admin>tnsping oranet
C:\oracle\10.1\database\network\admin>
Like the previous tnsping, this attempt reads the sqlnet.ora file to retrieve configuration
information. However, the tnsnames.ora file no longer exists, so the networking client is unable
to resolve the TNS alias oranet. Ordinarily you don’t want this to happen. However, for this
sample, you want to ensure that the Oracle networking client isn’t able to resolve names. To
create your tnsnames-less sample, you need to create a new Visual C# Windows application.
I have called my Visual Studio solution DynamicConnection, as illustrated in Figure 7-1.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 293
Figure 7-1. The New Project dialog for the tnsnames-less sample
Once you’ve created the project and generated the skeleton code, create the labels, text
boxes, and buttons, as illustrated in Figure 7-2. (I’ve included relevant sections of the code
here—for all the details, refer to the code in this chapter’s folder on the Apress website.) Of
course, you should add a reference to the Oracle Data Provider for .NET assembly to the
project and include the relevant using directive in the source code for the form.
For this simple example, you create a single private method that takes the values it needs
to generate a TNS connection string as parameters. You call this method by simply passing
the values entered on the form. Listing 7-8 contains the private method and the code in the
Connect button click event.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 294
try
{
oraConn.Open();
txtUserName.Text,
txtPassword.Text,
txtHost.Text,
txtPort.Text,
txtServiceName.Text);
}
Although the code to create the Data Source may seem busy, basically, it dynamically cre-
ates the text that appears in a tnsnames.ora file based on the input values. Simply substituting
this dynamically generated text in place of the TNS alias in the Data Source attribute of the
connection string allows you to connect to a database based on values supplied at run-time.
In Figure 7-3, I’ve completed the form with the same values I used to create the standard
TNS alias.
After you complete the form with the appropriate values, click the Connect button. A
message box that displays the generated connection string appears. A simple dialog displays
if an exception is trapped during the connection attempt. Figure 7-4 shows the dialog that
displays given the input values supplied in Figure 7-2.
solution that connects dynamically at run-time to subscribed databases. If you move the func-
tionality demonstrated in the private method in your sample form into a library, multiple
applications can take advantage of this technique.
C:\>sqlplus /nolog
Enter password:
ERROR:
ORA-28009: connection to sys should be as sysdba or sysoper
SQL>
The default configuration of Oracle, as installed in the Appendix, is configured so that SYS
must explicitly connect as either SYSDBA or SYSOPER. In order to connect as SYSDBA, simply
specify as SYSDBA in the connection string.
■CAUTION Exercise proper care when you’re connecting as SYSDBA. This is a fully privileged connection
and should never be used for a normal connection. In addition, make sure you coordinate with your DBA if
you’re creating an application that makes such connections to the database—any administrator would want
to know about an application that makes connections of this type.
Because the SYSDBA and SYSOPER privileges are so powerful, you’ll grant them to your
administrative user for the samples in this section, and then you’ll revoke them when you
have completed the samples. Let’s begin by connecting as SYS using the SYSDBA privilege.
This is one of the few times that you use the SYS user. In this case, it is necessary to connect as
SYS in order to grant the SYSDBA privilege. Listing 7-10 demonstrates the process of connect-
ing as SYS and granting the SYSDBA and the SYSOPER privileges. Notice that you’re using the
default database connection as discussed in the last section.
Listing 7-10. Connecting as SYS and Granting the SYSDBA and SYSOPER Privileges
C:\>sqlplus /nolog
Grant succeeded.
Grant succeeded.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
4258_Ch07_CMP2 10/4/04 8:28 PM Page 298
C:\>
Now that your administrative user has the SYSDBA and the SYSOPER privileges, you can
connect with each privilege. When you connect as a privileged connection, as with the SYS
user, you must use the as clause to specify which privilege you wish to use for your connec-
tion. If you omit the clause, you’ll simply connect with your normal, non-privileged account.
Listing 7-11 demonstrates these concepts.
■NOTE You must install and configure the database and networking components in the same fashion as
the setup in the Appendix for this to work properly. This doesn’t mean that you must follow the installation
steps in the Appendix, only that your configuration must match it. This installation is a preconfigured installa-
tion type and it results in an installation configuration that is common.
C:\>sqlplus /nolog
USERNAME DATABASE
---------------- ------------------------
ORANETADMIN LT10G.SAND
1 row selected.
USERNAME DATABASE
---------------- ------------------------
SYS LT10G.SAND
1 row selected.
no rows selected
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
As illustrated in Listing 7-11, when you connect with no system-level privilege specified,
you connect to your normal schema of ORANETADMIN. However, when you connect with SYS-
DBA or SYSOPER, things begin to get interesting. When you connect with the SYSDBA privilege,
your query to determine who you are returns SYS as the username. When you connect with the
SYSOPER privilege, your query returns no rows. This is because you’re connected to the special
schema PUBLIC and not to a real user. To further illustrate this, you’ll connect with no system-
level privileges, create a table, insert a record, and attempt to query the table. Listing 7-12
contains the code to illustrate this.
Listing 7-12. Schema Object Visibility When Connecting with System-Level Privileges
C:\>sqlplus /nolog
Table created.
1 row created.
SQL> commit;
Commit complete.
C
--------------------------------
Can you see me?
1 row selected.
C
--------------------------------
Can you see me?
1 row selected.
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
As Listing 7-12 illustrates, all goes as expected when you connect with no system-level
privileges. You operate as yourself in this connection. When you connect with the SYSDBA
privilege, you get an error when you query your table. This is because you’re now connected
with the SYS schema. However, by specifying the table owner, you are able to successfully
query your table. When connected with the SYSOPER privilege, you simply aren’t able to see
the table at all.
■NOTE If you execute the grant select on t to public command, you can query your table while
you’re connected with the SYSOPER privilege.
theClass.privilegeTest(conn_1, l_sql);
theClass.privilegeTest(conn_2, l_sql);
theClass.privilegeTest(conn_3, l_sql);
}
try
{
4258_Ch07_CMP2 10/4/04 8:28 PM Page 303
oraConn.Open();
oraReader = oraCmd.ExecuteReader();
while (oraReader.Read())
{
Console.WriteLine("User: ");
Console.WriteLine(" " + oraReader.GetString(0));
Console.WriteLine("Database: ");
Console.WriteLine(" " + oraReader.GetString(1) + "\n");
}
}
catch (Exception ex)
{
Console.WriteLine("Error occured: " + ex.Message);
}
finally
{
if (oraConn.State == System.Data.ConnectionState.Open)
{
oraConn.Close();
}
}
}
After you create the project and successfully compile the code, a sample test should yield
results similar to those in Listing 7-14.
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\PrivilegedConnection\bin\Debug>
PrivilegedConnection.exe
User:
ORANETADMIN
Database:
LT10G.SAND
User:
SYS
Database:
LT10G.SAND
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\PrivilegedConnection\bin\Debug>
4258_Ch07_CMP2 10/4/04 8:28 PM Page 304
Recall that when you connect with the SYSOPER privilege, you connect to the special
PUBLIC schema. Because you connect to the PUBLIC schema, your “Who am I?” query
returns no rows, and, therefore, there is output. Now that you’ve completed your exploration
of the SYSDBA and SYSOPER privileges, you’ll revoke them from our administrative user.
Listing 7-15 illustrates this process.
C:\>sqlplus /nolog
Revoke succeeded.
Revoke succeeded.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
In this section, you configure operating system authentication using the external user
scheme and ensure that everything is working properly by using SQL*Plus as your litmus test.
Once you verify that your configuration is working correctly, use your “Who am I?” console
application to illustrate connecting via operating system authentication in a .NET application.
C:\oracle\10.1\database\network\admin>type sqlnet.ora
# SQLNET.ORA Network Configuration File:
C:\oracle\10.1\database\network\admin\sqlnet.ora
# Generated by Oracle configuration tools.
SQLNET.AUTHENTICATION_SERVICES= (NTS)
NAMES.DIRECTORY_PATH= (TNSNAMES)
C:\oracle\10.1\database\network\admin>
If your sqlnet.ora file doesn’t contain the entry for SQLNET.AUTHENTICATION_SERVICES, you
must add it to the file. Although the sqlnet.ora file influences the behavior of the Oracle net-
working components, some parameters influence the behavior of the database itself. These
parameters reside in what is known as the init file or the spfile. The spfile exists for Oracle9i
and Oracle10g databases, whereas you must use the init file in earlier versions. It’s still possi-
ble to use an init file in 9i and 10g if you manually configure your database and installation
process, but the preconfigured install types and the Oracle tools such as the Database Cre-
ation Assistant create an spfile.
You won’t make modifications to these parameters here; however, you do need to know
the value of one of them, os_authent_prefix, to properly configure your authentication exam-
ple. Oracle uses this parameter when it is authenticating external users. As the name of the
parameter implies, the value of this parameter is prefixed to the operating system username.
Listing 7-17 illustrates one method of determining the value of this parameter.
C:\>sqlplus oranetadmin
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
You can see that, on my system, the value of this parameter is OPS$. The value may be
different on your system or it may not be specified at all. If the value isn’t specified, it doesn’t
mean that operating system authentication won’t work or isn’t available, it just means that no
value will be prefixed to an operating system username. The Windows username that I use on
my system is willim18, and my host name is ridmrwillim1801. Therefore, when I authenticate
using operating system authentication to my database, I authenticate as OPS$RIDMRWILLIM1801\
WILLIM18.
Because I authenticate as OPS$RIDMRWILLIM1801\WILLIM18, I need to create that user in the
Oracle database. Listing 7-18 illustrates the process of creating a user for operating system
authentication.
C:\>sqlplus oranetadmin
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
User created.
Grant succeeded.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
As you can see, I used the administrative user to log in to SQL*Plus and manually create a
new user.
■TIP If you are part of a Windows domain, when you create the new user, you should include the
domain name in the username. For example, if I was in a domain called Liverpool, my user would be
OPS$LIVERPOOL\WILLIM18.
C:\>sqlplus /nolog
SQL> connect /
Connected.
SQL> COL USERNAME FORMAT A32
SQL> COL DATABASE FORMAT A24
SQL> SELECT A.USERNAME,
2 B.GLOBAL_NAME DATABASE
3 FROM USER_USERS A,
4 GLOBAL_NAME B;
USERNAME DATABASE
4258_Ch07_CMP2 10/4/04 8:28 PM Page 308
-------------------------------- ------------------------
OPS$RIDMRWILLIM1801\WILLIM18 LT10G.SAND
1 row selected.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
By specifying only a / for your connection string, you’ve successfully connected to your
default database using operating system authentication. If you wish to connect to a database
using its TNS alias and operating system authentication, you simply supply the TNS alias as
part of the connection string as we discussed earlier. Of course, when you connect to a TNS
alias, your account must be set up properly in the destination database as it was in Listing 7-19.
The sample .NET code you develop in the following section (see Listing 7-20) illustrates both
methods of connecting.
Listing 7-20. The Core Code for Testing Operating System Authentication
try
{
oraConn.Open();
oraReader = oraCmd.ExecuteReader();
while (oraReader.Read())
{
Console.WriteLine("User: ");
Console.WriteLine(" " + oraReader.GetString(0));
Console.WriteLine("Database: ");
Console.WriteLine(" " + oraReader.GetString(1) + "\n");
}
}
catch (Exception ex)
{
Console.WriteLine("Error occured: " + ex.Message);
}
finally
{
if (oraConn.State == System.Data.ConnectionState.Open)
{
oraConn.Close();
}
}
}
4258_Ch07_CMP2 10/4/04 8:28 PM Page 310
Running the sample code should produce results that are appropriate for your system
and resemble those in Listing 7-21.
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\OSAuthenticatedConnection\bin\Debug>
OSAuthenticatedConnection.exe
Using the default database...
User:
OPS$RIDMRWILLIM1801\WILLIM18
Database:
LT10G.SAND
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\OSAuthenticatedConnection\bin\Debug>
As expected, the results of your “Who am I?” query display the same data when connect-
ing via operating system authentication to both the default database and that same database
specified as a TNS alias. Using operating system authentication is a viable method for not hav-
ing to maintain a password for a database user as we discussed earlier. However, if you wish to
have a finer grained control over the password, then Oracle can accommodate that as well
when you’re using database authentication.
Password Management
The topic of password management can be rather broad. I will, therefore, limit the discussion
to three areas:
Changing a Database Password: You’ll look at changing a password via SQL*Plus as well
as in .NET code.
Dealing with Expired Database Passwords: Passwords can expire and thus need to be
changed.
Locking Out a Database Password: Accounts can be locked if password rules created by
the database administrator are violated.
These are the most common areas for dealing with password management. One common
misconception regarding Oracle passwords is that what is stored in the database isn’t the actual
password. Passwords in Oracle are really one-way hash values that result from an internal algo-
rithm that incorporates the password as defined by the user. It is, therefore, not possible to
reverse-engineer an Oracle password—no password is stored in the database, only the result
of the one-way hash algorithm.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 311
■NOTE The alter user privilege allows for more than just changing another user’s password. You can
find the complete list of attributes that can be changed by alter user under the alter user SQL state-
ment reference in the “Database SQL Reference” in the documentation set.
The command you use to change a password is simply alter user <username> identified
by <password>. Listing 7-22 illustrates how to do this via SQL*Plus.
C:\>sqlplus oranetuser@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
User altered.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
The database user, oranetuser, now has the password newpass. You may wish to change
the password back to what it was before by simply executing the command and specifying the
old password. Listing 7-23 contains the core code for a Windows Forms application named
PasswordChange that allows you to change the password back (or to any other value) if you
wish. This sample doesn’t allow you to change the password for any user in the database—
only for the oranetuser user.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 312
return;
}
OracleCommand cmd;
OracleConnection oraConn = new OracleConnection(l_connect);
try
{
oraConn.Open();
cmd.ExecuteNonQuery();
Figure 7-5 shows the simple form you’d use to capture the relevant information you need
to change the database password.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 313
Figure 7-6 shows the form at run-time. Because you changed the password for the oranet-
user in SQL*Plus earlier, I changed it back to the value of demo, as it was previously.
Figure 7-7 shows the confirmation message that states that the password was changed
successfully.
To verify that the password for oranetuser was successfully changed from newpass to
demo, create a SQL*Plus session and specify the new password. Listing 7-24 illustrates this
process (in order to make it explicit that the password was successfully changed, I specify the
password as part of the connection string in SQL*Plus).
C:\>sqlplus /nolog
C:\>
C:\>sqlplus oranetadmin@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
User altered.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
The password for oranetuser has expired. When the user attempts their next connection,
Oracle detects that the password has expired and triggers a prompt for a new password.
Listing 7-26 illustrates this process.
C:\>sqlplus oranetuser@oranet
Enter password:
ERROR:
ORA-28001: the password has expired
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
■NOTE The password that is initially entered must be the correct expired password. If an invalid password
is entered, you receive the standard “invalid username or password” error message instead of being prompted
for a new password.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 316
To demonstrate how to trap and process the expired password in .NET, I clone the simple
Windows Form application used to change the password. The form itself doesn’t change. I
simply change the code inside the button click event. Listing 7-27 represents the code that
detects the expired password condition and connects with a new password. The new project is
called PasswordExpiration (see this chapter’s folder on the Downloads section of the Apress
website).
return;
}
try
{
// attempt to open a connection
// this should fail since we have expired the password
oraConn.Open();
}
catch (OracleException ex)
{
// trap the "password is expired" error code
if (ex.Number == 28001)
{
// dislay a simple marker to indicate we trapped the error
MessageBox.Show("Trapped Expired Password", "Expired Password");
By completing the text field in the form and clicking the Change Password button, you
should see the dialog depicted in Figure 7-9.
After dismissing the dialog that informs you that the expired password condition was
detected, you should see the dialog depicted in Figure 7-10.
To verify that the expired password was correctly changed, use SQL*Plus as illustrated in
Listing 7-28.
C:\>sqlplus oranetuser@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
The sample code, as presented here, doesn’t verify that the new password you entered
was different from the old one. You should coordinate any password rules such as no pass-
word reuse with your database administrator if you’re using the password expiration feature.
Oracle has the ability to enforce a variety of password rules; however, this is a subject outside
the scope of this book because the creation of the password rules in the database relates to
your database administrator more than it does to us as developers. The definition of those
rules, on the other hand, is something that application developers and corporate security
departments are frequently involved in creating. If your database administrator has imple-
mented password rules, it’s possible that, in addition to password expiration, you may
encounter a user account that has been locked.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 319
■NOTE The act of locking or unlocking an account doesn’t affect the password per se. I included it in this
section because, from a user perspective, a locked account may be interpreted as a password issue.
C:\>sqlplus oranetadmin@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
User altered.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
4258_Ch07_CMP2 10/4/04 8:28 PM Page 320
At this point, your oranetuser account is locked. Any attempt to connect to the database
generates a trappable error as illustrated in Listing 7-30.
C:\>sqlplus /nolog
SQL> exit
C:\>
As you can see in Listing 7-30, Oracle generates an ORA-28000 error message when it
detects an attempt to connect with a locked account. Your test code from the PasswordLocked
project in Listing 7-31 (see this chapter’s folder in the Downloads section of the Apress website
for the complete sample code) takes advantage of this fact to trap the condition and display a
simple dialog that indicates that the condition has been trapped.
try
{
// attempt to open a connection
// this should fail since we have locked the account
oraConn.Open();
}
catch (OracleException ex)
{
// trap the "account is locked" error code
if (ex.Number == 28000)
4258_Ch07_CMP2 10/4/04 8:28 PM Page 321
{
// display a simple marker to indicate we trapped the error
MessageBox.Show("Trapped Locked Account", "Locked Account");
}
else
{
MessageBox.Show(ex.Message, "Error Occured");
}
}
finally
{
if (oraConn.State == System.Data.ConnectionState.Open)
{
oraConn.Close();
}
}
}
Figure 7-11 shows the design time representation of the form. This is the same form I
used in the previous samples except I’ve removed the New Password and Confirm Password
labels and text boxes.
Figure 7-11. The design time representation of the locked account form
When you run the sample code and attempt to log in to the database as the oranetuser
account as depicted in Figure 7-12, you should receive an error dialog.
When you click the Connect button, you’re presented with the dialog in Figure 7-13.
Figure 7-13. The dialog indicating you trapped the locked account condition
Finally, to wrap-up your exploration of locked accounts, you’ll unlock the oranetuser
account in SQL*Plus using your administrative user. This is illustrated in Listing 7-32.
C:\>sqlplus oranetadmin@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
User altered.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition
Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
C:\>
■NOTE The connection pooling mechanism isn’t limited to the Oracle Data Provider for .NET. Other data
providers can also expose the connection pooling feature.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 323
The term multiplexing can be confused with the term connection pooling, though they
represent distinct approaches to limiting resource usage. In a multiplexing configuration, a
single connection to the resource (a database, in your case) is shared by multiple clients of the
resource. The Oracle Data Provider for .NET doesn’t expose a multiplexing method. An appli-
cation (such as a middle-tier application) is responsible for creating the connection and
brokering its usage among its clients. However, the database itself supports a multiplexing
scheme through the shared server (formerly known as multi-threaded server) connection
type. We examined shared server and dedicated server modes in Chapter 1.
On the other hand, since the connection pooling functionality is directly exposed by
the data provider, and because it is trivial to implement, you use it as your resource saving
method. In fact, you have to explicitly not use the connection pooling feature, because it is
enabled by default.
In order to see how the connection pooling feature works, you’ll create two applications.
The first is a simple console application called NoConnectionPooling that explicitly disables
connection pooling. The other, called ConnectionPooling, uses the default value of having
connection pooling enabled. You’ll then run these applications and examine the connections
to your database in SQL*Plus. You incorporate a small pause function in your code that gives
you time to examine the connections in SQL*Plus. In order to examine the effect of the con-
nection pooling attribute, you have to do a bit of bouncing around between SQL*Plus and
your application.
In order to verify that the connection pooling attribute has been disabled and to see the
effects of this in SQL*Plus, create a new console application like the one in Listing 7-33.
conn_1.Dispose();
conn_2.Dispose();
Here, you repeatedly execute a query in SQL*Plus to monitor your connections. After each
Examine in SQL*Plus message, you toggle over to the window where you’re running SQL*Plus
and execute the query. The query simply displays your user name, the program that is execut-
ing, and the time the user logged in to the database. The time that the user logged in to the
database is important, because it is this that verifies that you are or aren’t using connection
pooling. If connection pooling isn’t being used, the time that the user logged in to the data-
base varies with the iterations of your query. If connection pooling is being used, the time
the user logged in to the database remains constant because the single connection is being
reused.
The steps you use to verify if connection pooling is or isn’t being used are as follows:
1. Once you have created and compiled your console application, open a command
prompt window, change to the directory that contains the executable for your test,
and execute the binary, as shown here:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
NoConnectionPooling.exe
Connection 1 created... Examine in SQL*Plus
C:\>sqlplus oranetadmin@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
2 PROGRAM,
3 TO_CHAR(LOGON_TIME,'HH24:MI:SS') LOGON_TIME
4 FROM V$SESSION
5 WHERE USERNAME = 'ORANETUSER';
1 row selected.
3. Press the Enter key to un-pause the application as illustrated in the following code:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
NoConnectionPooling.exe
Connection 1 created... Examine in SQL*Plus
4. Toggle over to the SQL*Plus session and re-execute the query by entering a single for-
ward slash (/) character and pressing Enter. The following code illustrates this:
SQL> /
no rows selected
Your query has returned no rows because the connection was disposed and you don’t
have connection pooling enabled.
5. Return to the application window and press the Enter key to un-pause the application
as illustrated here:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
NoConnectionPooling.exe
Connection 1 created... Examine in SQL*Plus
6. Return to the SQL*Plus session and re-execute the query as you did in step 4. The fol-
lowing code illustrates this:
SQL> /
1 row selected.
You can see that you’ve established a new connection and that this connection has a
different logon time from the previous connection.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 326
7. Return to the application window and press Enter to un-pause the application as illus-
trated here:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
NoConnectionPooling.exe
Connection 1 created... Examine in SQL*Plus
8. Return to SQL*Plus and re-execute the query. The following code contains the results:
SQL> /
no rows selected
Once again, your query doesn’t return results because you disposed of your second
connection.
9. Return to the application window and press Enter to un-pause the application. The
application terminates at this point.
In order to demonstrate connection pooling, you’ll use virtually the same code as you did
in Listing 7-33. The only difference between this code and the following code to demonstrate
connection pooling is that you remove the pooling=false attribute from the connection string.
Since pooling is True by default, this enables your application to take advantage of connection
pooling. Listing 7-34 contains the code you used in your connection pooling test application.
conn_1.Dispose();
conn_2.Dispose();
You perform the same steps as you did with the no connection pooling example. How-
ever, the results of your connection monitoring query are slightly different.
1. Once you’ve created and compiled your console application, open a command
prompt window, change to the directory that contains the executable for your test, and
execute the binary. The following code illustrates this process:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
ConnectionPooling.exe
Connection 1 created... Hit enter key
C:\>sqlplus oranetadmin@oranet
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
4258_Ch07_CMP2 10/4/04 8:28 PM Page 328
1 row selected.
3. Press the Enter key to un-pause the application as illustrated in the following code:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
ConnectionPooling.exe
Connection 1 created... Examine in SQL*Plus
4. Toggle over to the SQL*Plus session and re-execute the query by entering a single for-
ward slash (/) and pressing Enter as illustrated here:
SQL> /
1 row selected.
Your query has returned a row even though the connection was disposed of. This is the
effect of connection pooling. Rather than terminate your connection, you’ve returned
it to the pool.
5. Return to the application window and press the Enter key to un-pause the application
as illustrated here:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
ConnectionPooling.exe
Connection 1 created... Examine in SQL*Plus
SQL> /
1 row selected.
Even though your application created a new connection, you can see that you haven’t
created a new database connection. The logon time remains constant—the same as it
was for the first connection.
7. Return to the application window and press Enter to un-pause the application:
C:\My Projects\ProOraNet\Oracle\C#\Chapter07\NoConnectionPooling\bin\Debug>
ConnectionPooling.exe
Connection 1 created... Examine in SQL*Plus
8. Return to SQL*Plus and re-execute the query. Here are the results:
SQL> /
1 row selected.
Once again, your query has returned the same result even though you disposed of your
second connection.
9. Return to the application window and press Enter to un-pause the application.
The application terminates. If you executed your query at this point, it wouldn’t return
a row because the application terminated.
4258_Ch07_CMP2 10/4/04 8:28 PM Page 330
In the second application, you can clearly see the impact of the pooling attribute. By allow-
ing connection pooling to take place, you were able to save resources because you didn’t need
to establish a second connection. You could reuse the existing connection, and thus bypass
the overhead associated with starting up a new connection to the database. As an application
developer, you have a great deal of control over the connection pooling environment. The
complete set of attributes is available in the Oracle Data Provider for .NET documentation set.
Connection pooling has a positive impact on web-based applications because they frequently
follow a pattern of receiving the request, getting data from the database, and returning results.
By maintaining a pool of readily available connections, a web-based application can reduce its
service cycle time.
By storing the connection pooling attributes in the application’s web.config (or app.con-
fig) file and reading them at application run-time, you can allow an application administrator
to tune the connection pool without rewriting or recompiling the application. An example of
this may look like the following:
<appSettings>
<add key="Connection Pooling" value="false"/>
</appSettings>
Chapter 7 Wrap-Up
You began this chapter by looking at the default database connection and how to implement
the ability to connect to the default database in your code. We discussed how this ability can
be a benefit when you don’t know the TNS alias for a database in advance. After discussing a
default database connection, we examined how to create a tnsnames-less connection. This
type of connection affords you a great deal of flexibility, especially in a table-driven sort of
application.
We then examined the difference between system-level privileged and non-privileged
connections in a fair amount of detail. You created code to connect as a system-level privi-
leged user and, using SQL*Plus, you learned how these types of connections work with the
database. In addition to privileged and non-privileged connections, you tackled the some-
times-confusing topic of operating system authentication. You saw how to connect to the
default database and a database via the TNS alias using operating system authentication.
After looking at the methods to employ authentication by the operating system, you examined
a few areas that are common in environments where password rules and account lockout are
in effect. And finally, you finished the chapter by taking in a fairly lengthy set of examples that
illustrate how to use connection pooling. All in all, we covered a lot of ground in this chapter.