Beruflich Dokumente
Kultur Dokumente
NET Framework
with C# and Sql Server and Oracle Data Provider
Introduction
Summary
Implementing a basic transaction using ADO.NET in an application can be fairly straightforward. The most
common sequence of steps that would be performed while developing a transactional application is as follows:
o Open a database connection using the Open method of the connection object.
o Begin a transaction using the BeginTransaction method of the connection object. This
method provides us with a transaction object that we will use later to commit or rollback the
transaction. Note that changes caused by any queries executed before calling the
BeginTransaction method will be committed to the database immediately after they execute.
o Set the Transaction property of the command object to the above mentioned transaction
object.
o Execute the SQL commands using the command object. We may use one or more
command objects for this purpose, as long as the Transaction property of all the objects is set to a
valid transaction object.
o Commit or roll back the transaction using the Commit or Rollback method of the
transaction object.
o Close the database connection.
A data provider in the .NET Framework serves as a bridge between an application and a data source. A .NET
Framework data provider enables you to return query results from a data source, execute commands at a data
source, and propagate changes in a DataSet to a data source. This article includes tips on which .NET Framework
data provider is best suited for your needs.
To achieve the best performance for your application, use the .NET Framework data provider that is most
appropriate for your data source. There are a number of data provider options for use in your applications. The
following table provides information about the available data providers and which data sources a data provider is
most appropriate for.
Provider Details
SQL Server .NET Data Found in the System.Data.SqlClient namespace.
Provider
Recommended for middle-tier applications using Microsoft SQL Server version
7.0 or later.
For Microsoft SQL Server 7.0 or later, the .NET Framework Data Provider for
SQL Server is recommended.
Recommended for single-tier applications using a Microsoft® Access database.
Use of an Access database for a middle-tier application is
not recommended.
ODBC .NET Data Provider Found in the Microsoft.Data.Odbc namespace.
Provides access to data sources that are connected to using an ODBC driver.
Oracle .NET Data Provider Found in the System.Data.OracleClient namespace.
The .NET Framework Data Provider for Oracle, unlike the Microsoft OLE DB
provider for Oracle, also supports new Oracle 9i datatypes, as well as ref
cursors (useful for running Oracle stored procedures that return result sets).
This provider, System.Data.OracleClient, is similar to the .NET Framework
Data Provider for SQL Server, System.Data.SqlClient.
The Microsoft® .NET Framework is the infrastructure for the overall .NET Platform. The common language runtime
and class libraries (including Microsoft Windows® Forms, ADO.NET, and ASP.NET) combine to provide services
and solutions that can be easily integrated within and across a variety of systems.
The .NET Framework provides a fully managed, protected, and feature-rich application execution environment,
simplified development and deployment, and seamless integration with a wide variety of languages.
The Microsoft® .NET Framework Software Development Kit (SDK) includes the .NET Framework, as well as
everything you need to write, build, test, and deploy .NET Framework applications - documentation, samples, and
command-line tools and compilers.
Goto:
http://msdn.microsoft.com/downloads/
Select:
You'll get the whole framework and a C# command line compiler. Run the downloaded setup.exe if you haven't
installed yet Microsoft .NET and follow the installation steps. You will be asked for Server Components which you
don't need. If the installation asks for Microsoft Data Access Components MDAC you may continue or quit the
installation. Anyway, you need to install MDAC 2.7 or higher prior ODBC data access is going to work.
The SqlClient provider ships with ADO.NET and resides in the System.Data.SqlClient namespace. It should be
used to access SQL Server 2000. The classes within SqlClient provider all begin with "Sql", so the connection class
is SqlConnection, the command class is SqlCommand, and so on. Lets look at an example to update SQL Server
2000.
using System;
using System.Data;
using System.Data.SqlClient;
namespace TranDemoSqlClient
{
class TranDemoSqlClient
{
static void Main(string[] args)
{
// Declare database objects such as connection,
// command and transaction
string connectionString = "server=localhost;"
+ "database=Northwind;uid=sa;pwd=manager";
SqlConnection cnn;
SqlCommand cmd;
SqlTransaction tran;
This file is called TranDemoSqlClient.cs, we can compile it from the command line simply by typing csc
TranDemoSqlClient.cs. There is no need to add any references.
Introduction
If you are building applications using Microsoft® .NET against an Oracle backend database, you will want to take a
close look at the new .NET Framework Data Provider for Oracle released on MSDN in June 2002. The goal of the
provider is to boost the performance and scalability of .NET applications with Oracle databases by providing a
native .NET interface to Oracle databases that bypasses the need to use an OLE DB provider.
The .NET Framework Data Provider for Oracle, unlike the Microsoft OLE DB provider for Oracle, also supports new
Oracle 9i datatypes, as well as ref cursors (useful for running Oracle stored procedures that return result sets).
This provider, System.Data.OracleClient, is similar to the .NET Framework Data Provider for SQL Server,
System.Data.SqlClient.
Basically, developers now have a much faster database access mechanism for ADO.NET in the form of new
System.Data.OracleClient framework classes that work in much the same way as the System.Data.SqlClient
classes. In both cases, the fastest database read mechanism will be ADO.NET Data Readers, as opposed to
Data Sets, although both are fully functional using the new Oracle Managed Provider. The good news is the new
OracleClient classes should provide significant performance improvements for .NET applications, and migrating
code between OLE DB data classes and OracleClient data classes is not very difficult, although some work is
required. However, the performance boost can be dramatic.
The OLE DB client classes are designed to provide a database-independent layer for accessing generic databases.
While the value of a generic layer is nearly universal access, it is difficult to deliver database-specific optimizations
in this generic access layer. Also, the OLE DB layer is implemented as a COM library, so the System.Data.Oledb
namespace works through COM interop. To achieve the significant performance boost described here, the .NET
Framework Data Provider for Oracle avoids the cost of COM interop, and also employs Oracle-specific
optimizations.
The Microsoft® .NET Framework Data Provider for Oracle is an add-on component to the Microsoft .NET
Framework that provides access to an Oracle database using the Oracle Call Interface (OCI) as provided by
Oracle Client software. Oracle 8i Release 3 (8.1.7) Client or later must be installed for this provider to function.
All of these files, except Mtxoci8.dll, are installed in C:\Program Files\Microsoft.NET\OracleClient.Net by default
(assuming that C:\Program Files is your default Program Files folder location). Mtxoci8.dll is installed in the
windows system directory (for example, C:\Windows\System32 on a Windows 2000 computer on which C: is the
system drive).
As part of Setup, the System.Data.OracleClient namespace is added to the global assembly cache.
The classes that compose the provider are in the System.Data.OracleClient namespace and all have the prefix
"Oracle". Lets look at an example to update Oracle 9.2.0.
using System;
using System.Data;
using System.Data.OracleClient;
namespace TranDemoOra
{
class TranDemoOra
{
static void Main(string[] args)
{
// Declare database objects such as connection,
// command and transaction
string connectionString = "Data Source=ARK2;"
+ "User ID=scott; Password=tiger";
OracleConnection cnn;
OracleCommand cmd;
OracleTransaction tran;
Introduction
The examples used in this article uses the Pubs database that comes as a sample database when you install SQL
Server. If you need to rebuild the Pubs database, follow the steps to install a fresh copy :
a. Run the osql command prompt utility and detach the Pubs database from SQL Server by using the
sp_detach_db system stored procedure.
b. Delete the database files for pubs database (pubs.mdf, pubs_log.ldf). These files are located in the \Data
directory.
c. Re-creating the Pubs database requires the Instpubs.sql script to be executed. Run the script from the
command line (if the .sql files are in a different directory, adjust the path accordingly). You can also run
this script file from the Query Analyzer.
d. osql -U sa -P "" -i
"C:\Program Files\Microsoft SQL Server\MSSQL\Install\InstPubs.sql"
(The osql utility uses case-sensitive options. If neither the -U or -P options are used, SQL Server 2000
attempts to connect using Windows Authentication Mode. More information about the osql Utility can be
found in the Sql Server Books Online)
Transactions
Transactions group a set of tasks into a single execution unit. Each transaction begins with a specific task and
ends when all the tasks in the group successfully complete. If any of the tasks fails, the transaction fails.
Therefore, a transaction has only two results: success or failure. Incomplete steps result in the failure of the
transaction.
Users can group two or more Transact-SQL statements into a single transaction using the following statements:
• Begin Transaction
• Rollback Transaction
• Commit Transaction
If anything goes wrong with any of the grouped statements, all changes need to be aborted. The process of
reversing changes is called rollback in SQL Server terminology. If everything is in order with all statements
within a single transaction, all changes are recorded together in the database. In SQL Server terminology, we say
that these changes are committed to the database.
USE pubs
BEGIN TRAN
UPDATE Authors
SET Phone = '415 354-9866'
WHERE au_id = '724-80-9391'
UPDATE Publishers
SET city = 'Calcutta', country = 'India'
WHERE pub_id = '9999'
PROBLEM:
IF (@intErrorCode <> 0) BEGIN
PRINT 'Unexpected error occurred!'
ROLLBACK TRAN
END
Before the real processing starts, the BEGIN TRAN statement notifies SQL Server to treat all of the following
actions as a single transaction. It is followed by two UPDATE statements. If no errors occur during the updates, all
changes are committed to the database when SQL Server processes the COMMIT TRAN statement, and finally the
stored procedure finishes. If an error occurs during the updates, it is detected by if statements and execution is
continued from the PROBLEM label. After displaying a message to the user, SQL Server rolls back any changes
that occurred during processing. Note: Be sure to match BEGIN TRAN with either COMMIT or ROLLBACK.
Nested Transactions
SQL Server allows you to nest transactions. Basically, this feature means that a new transaction can start even
though the previous one is not complete. Transact-SQL allows you to nest transaction operations by issuing
nested BEGIN TRAN commands. The @@TRANCOUNT automatic variable can be queried to determine the level of
nesting - 0 indicates no nesting , 1 indicates nesting one level deep, and so fourth.
A COMMIT issued against any transaction except the outermost one doesn't commit any changes to disk - it
merely decrements the@@TRANCOUNT automatic variable. A ROLLBACK, on the other hand, works regardless of
the level at which it is issued, but rolls back all transactions, regardless of the nesting level. Though this is
counterintuitive, there's a very good reason for it. If a nested COMMIT actually wrote changes permanently to
disk, an outer ROLLBACK wouldn't be able to reverse those changes since they would already be recorded
permanently.
When you explicitly begin a transaction, the @@TRANCOUNT automatic variable count increases from 0 to 1;
when you COMMIT, the count decreases by one; when you ROLLBACK, the count is reduced to 0. As you see, the
behavior of COMMIT and ROLLBACK is not symmetric. If you nest transactions, COMMIT always decreases the
nesting level by 1, as you can see illustrated in Figure 1. The ROLLBACK command, on the other hand, rolls back
the entire transaction, illustrated in Figure 2. This asymmetry between COMMIT and ROLLBACK is the key to
handling errors in nested transactions.
Figure 1: A COMMIT always balances a BEGIN TRANSACTION by reducing the transaction count by one.
As you can see from Figure 1 and Figure 2, you can nest transactions and use the @@TRANCOUNT automatic
variable to detect the level. You also learned that COMMIT and ROLLBACK do not behave symmetrically; COMMIT
just decreases the value of @@TRANCOUNT, while ROLLBACK resets it to 0. The implication is that a transaction is
never fully committed until the last COMMIT is issued. No matter how deeply you nest a set of transactions, only
the last COMMIT has any effect.
USE pubs
SELECT 'Before BEGIN TRAN', @@TRANCOUNT -- The value of @@TRANCOUNT is 0
BEGIN TRAN
SELECT 'After BEGIN TRAN', @@TRANCOUNT -- The value of @@TRANCOUNT is 1
DELETE sales
BEGIN TRAN nested
SELECT 'After BEGIN TRAN nested', @@TRANCOUNT
-- The value of @@TRANCOUNT is 2
DELETE titleauthor
COMMIT TRAN nested
-- Does nothing except decrement the value of @@TRANCOUNT
In this example we see that despite the nested COMMIT TRAN, the outer ROLLBACK still reverses the effects of
the DELETE titleauthor command.
USE pubs
SELECT 'Before BEGIN TRAN', @@TRANCOUNT -- The value of @@TRANCOUNT is 0
BEGIN TRAN
SELECT 'After BEGIN TRAN', @@TRANCOUNT -- The value of @@TRANCOUNT is 1
DELETE sales
BEGIN TRAN nested
SELECT 'After BEGIN TRAN nested', @@TRANCOUNT
-- The value of @@TRANCOUNT is 2
DELETE titleauthor
ROLLBACK TRAN
In this example, execution never reaches the out COMMIT TRAN because the ROLLBACK TRAN reverses all
transactions currently in progress and sets @@TRANCOUNT to 0. Unless ROLLBACK TRAN is called with a save
point, ROLLBACK TRAN always rolls back all transactions and sets @@TRANCOUNT to 0, regardless of the context
in which it's called.
Savepoints offer a mechanism to roll back portions of transactions. A user can set a savepoint, or marker, within a
transaction. The savepoint defines a location to which a transaction can return if part of the transaction is
conditionally canceled. SQL Server allows you to use savepoints via the SAVE TRAN statement, which doesn't
affect the @@TRANCOUNT value. A rollback to a savepoint (not a transaction) doesn't affect the value returned by
@@TRANCOUNT, either. However, the rollback must explicitly name the savepoint: using ROLLBACK TRAN
without a specific name will always roll back the entire transaction.
Collapse
USE pubs
SELECT 'Before BEGIN TRAN main', @@TRANCOUNT
-- The value of @@TRANCOUNT is 0
Error Handling
The examples presented here are specific to stored procedures as they are the desired method of interacting with
a database. When an error is encountered within a stored procedure, the best you can do is halt the sequential
processing of the code and either branch to another code segment in the procedure or return processing to the
calling application. The @@ERROR automatic variable is used to implement error handling code. It contains the
error ID produced by the last SQL statement executed during a client’s connection. When a statement executes
successfully, @@ERROR contains 0. To determine if a statement executes successfully, an IF statement is used to
check the value of @@ERROR immediately after the target statement executes. It is imperative that @@ERROR
be checked immediately after the target statement, because its value is reset to 0 when the next statement
executes successfully. If a trappable error occurs, @@ERROR will have a value greater than 0. SQL Server resets
the @@ERROR value after every successful command, so you must immediately capture the @@ERROR value.
Most of the time, you'll want to test for changes in @@ERROR right after any INSERT, UPDATE, or DELETE
statement.
BEGIN TRAN
INSERT titles(title_id, title, type)
VALUES (@title_id, @title, @title_type)
COMMIT TRAN
RETURN 0
This kind of solution contains substantial repetition especially if your business logic requires more than two
Transact-SQL statements to be implemented. A more elegant solution is to group codes into a generic error
handling procedure:
BEGIN TRAN
INSERT titles(title_id, title, type)
VALUES (@title_id, @title, @title_type)
COMMIT TRAN
RETURN 0
ERR_HANDLER:
PRINT 'Unexpected error occurred!'
ROLLBACK TRAN
RETURN 1
COMMIT
Default Behavior COMMIT WORK WRITE IMMEDIATE WAIT;
Alter commit behavior for the ALTER SYSTEM SET COMMIT_WRITE = NOWAIT;
system ALTER SESSION SET COMMIT_WRITE = NOWAIT;
Alter commit behavior for the ALTER SYSTEM SET COMMIT_WRITE = NOWAIT;
session ALTER SESSION SET COMMIT_WRITE = NOWAIT;
COMMIT [WORK]
Complete a transaction
show autocommit
CREATE TABLE t0 (
testcol NUMBER);
INSERT INTO t0 (testcol) VALUES (1);
COMMIT;
COMMIT WORK;
COMMIT COMMENT <comment_string_255_char>;
Comment Commit
CREATE TABLE t (
testcol NUMBER(2));
Commit will not return until the INSERT INTO t (testcol) VALUES (6);
corresponding redo is persistent
in the online redo log COMMIT WRITE WAIT;
ROLLBACK
ROLLBACK [WORK] [TO SAVEPOINT <savepoint_name>]
Undo a transaction
SELECT * FROM t0;
ROLLBACK;
ROLLBACK WORK;
DECLARE
i INTEGER := 3;
BEGIN
INSERT INTO t1 (testcol) VALUES (10/i);
SAVEPOINT A;
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
/*
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
*/
COMMIT;
EXCEPTION
WHEN ZERO_DIVIDE THEN
ROLLBACK TO SAVEPOINT A;
COMMIT;
END testblock;
/
DECLARE
i INTEGER := 3;
BEGIN
INSERT INTO t1 (testcol) VALUES (10/i);
SAVEPOINT A;
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
i := i-1;
INSERT INTO t1 (testcol) VALUES (10/i);
COMMIT;
EXCEPTION
WHEN ZERO_DIVIDE THEN
ROLLBACK TO SAVEPOINT A;
COMMIT;
END testblock;
/
commit;
UPDATE servers
SET srvr_id = 501
WHERE srvr_id = 900;
rollback;
UPDATE servers
SET srvr_id = 501
WHERE srvr_id = 900;
commit;
Inconsistent States
The following refers to use of the DBMS_TRANSACTION built-in package:
Before automatic recovery runs, the transaction may show up in DBA_2PC_PENDING as state "collecting",
"committed", or "prepared". If the DBA has forced an in-doubt transaction to have a particular result by using
"commit force" or "rollback force", then states "forced commit" or "forced rollback" may also appear. Automatic
recovery will normally delete entries in any of these states. The only exception is when recovery finds a forced
transaction which is in a state inconsistent with other sites in the transaction; in this case, the entry will be left in
the table and the MIXED column will have a value 'yes'.
However, under certain conditions, it may not be possible for automatic recovery to run. For example, a remote
database may have been permanently lost. Even if it is recreated, it will get a new database id, so that recovery
cannot identify it (a possible symptom is ORA-02062). In this case, the DBA may use the procedure
purge_lost_db_entry to clean up the entries in any state other than "prepared". The DBA does not need to be in
any particular hurry to resolve these entries, since they will not be holding any database resources.
-- The following table indicates what the various states indicate about
-- the transaction and what the DBA actions should be:
Demos
CREATE TABLE t2 (
testcol NUMBER);
BEGIN
INSERT INTO t2 (testcol) VALUES (1);
SAVEPOINT A;
INSERT INTO t2 (testcol) VALUES (2);
SAVEPOINT B;
INSERT INTO t2 (testcol) VALUES (3);
SAVEPOINT C;
INSERT INTO t2 (testcol) VALUES (4);
Dynamic Savepoints
EXECUTE IMMEDIATE 'ROLLBACK TO SAVEPOINT ' || sp_in;
COMMIT;
END spdemo;
/
exec spdemo('B');
exec spdemo('C');
The following exercise will help you to understand the difference between autonomous and normal transactions.
Create a non-autonomous procedure which will insert one row to this table.
COMMIT;
END;
DECLARE
BEGIN
INSERT INTO test_table
VALUES ('Before Non Autnomous Insert');
non_autonomous_proc;
ROLLBACK;
END;
A
--------------------------------------------------
Before Non Autnomous Insert
Non Autnomous Insert
Create an autonomous procedure which will insert one row to this table.
CREATE OR REPLACE PROCEDURE autonomous_proc
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO test_table
VALUES ('Autnomous Insert');
COMMIT;
END;
DECLARE
BEGIN
INSERT INTO test_table
VALUES ('Before Autnomous Insert');
autonomous_proc;
ROLLBACK;
END;
A
--------------------------------------------------
Autnomous Insert
Conclusion
1) The commit statement in the autonomous procedure will commit the DML operations in the autonomous
procedure without commiting the transactions before that.
2) The rollback statement caused both the inserts before and after the autonomous transaction to be rolled back,
but not the autonomous transaction
we can use commit or rolback in trigger without error by using "PRAGMA AUTONOMOUS_TRANSACTION;" on
declaration section