Using ODP.NET for Oracle 12.2c with IPC connections: NullReferenceException - c#

After installing Oracle 12c Release 2, I am running into some problems with database and transaction handling. The version of the ODP.NET dll (Oracle.DataAccess.dll) is 4.122.1.0, and I am using a PC with Windows 7 Professional.
I am no longer able to use the OracleConnection.State property to monitor the state of a connection, which breaks existing code.
It seems that simply accessing the State property corrupts the OracleConnection object, but only when using IPC connections. Below is a code example that reproduces the problem. The database in this example is run on my local system.
private const string ConnectionStringIPC = #"Data Source=(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = IPC)(Key = EXTPROC1521))) (CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = GLOBAL)));User Id=TestCase;Password=TestCase;Validate Connection=true;";
private const string ConnectionStringTCP = #"Data Source=(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))) (CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = GLOBAL)));User Id=TestCase;Password=TestCase;Validate Connection=true;";
static void Main(string[] args)
{
Console.WriteLine("IPC:");
TestCase1(ConnectionStringIPC);
TestCase2(ConnectionStringIPC);
Console.WriteLine("TCP:");
TestCase1(ConnectionStringTCP);
TestCase2(ConnectionStringTCP);
}
private static void TestCase1(string connectionString)
{
try
{
OracleConnection conn = new OracleConnection(connectionString);
conn.Open();
OracleTransaction trans = conn.BeginTransaction();
Console.WriteLine("TestCase1 succesful!");
}
catch(Exception e)
{
Console.WriteLine("TestCase1 failed:");
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}
private static void TestCase2(string connectionString)
{
try
{
OracleConnection conn = new OracleConnection(connectionString);
conn.Open();
var state = conn.State;
OracleTransaction trans = conn.BeginTransaction();
Console.WriteLine("TestCase2 succesful!");
}
catch (Exception e)
{
Console.WriteLine("TestCase2 failed:");
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}
The output of this program is
IPC:
TestCase1 succesful!
TestCase2 failed:
Object reference not set to an instance of an object.
at Oracle.DataAccess.Client.OracleConnection.get_TxnHndAllocated()
at Oracle.DataAccess.Client.OracleConnection.BeginTransaction()
at OracleConnectionTest.Program.TestCase2(String connectionString)
TCP:
TestCase1 succesful!
TestCase2 succesful!
As you can see, simply accessing the OracleConnection.State property is enough to invalidate the object, triggering an exception in the subsequent OracleTransaction.BeginTransaction call.
Is there anything wrong with the way I try to use the OracleConnection and OracleTransaction objects?
If not, is this a known bug, and is there any good work-around?

Related

Why is Transaction being Elevated to Distributed Transaction (MSDTC), Even after Closing 1st Connection and then Opening the 2nd Connection

I am trying this code which I think SHOULD NOT elevate/promote the TransactionScope to Distributed Transaction (MSDTC), because I read somewhere that MSDTC only happens under 2 conditions,
1. when we have a Nested TransactionScope
2. If we try to open 2 SIMULTANEOUS Connections to 2 different Sources within the same TransactionScope
But I am Closing the Earlier Connection and then Opening the New Connection to the Different Source, but still it is Promoting the Transaction to a Distributed one.
The Error I get is "The partner transaction manager has disabled its support for remote/network transactions."
Here is the Code:
var options = new TransactionOptions()
{
IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead,
Timeout = TimeSpan.FromMinutes(5)
};
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))
{
// Connection To Server Database
string connectionString = "Server=188.188.188.188;Database=SomeDb;User=SomeUser;Password=SomePassword;Trusted_Connection=false;Connection Timeout=30;Persist Security Info=False";
SqlConnection conn1 = null;
try
{
conn1 = new SqlConnection(connectionString);
conn1.Open();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
finally
{
if (conn1 != null)
{
conn1.Close();
conn1.Dispose();
conn1 = null;
}
}
// Connection To Local Machine Database
string connectionString2 = "Server=MyPC\\SqlServer2014;Database=SomeDb;User=SomeUser;Password=SomePassword;Trusted_Connection=false;Connection Timeout=30;Persist Security Info=False";
SqlConnection conn2 = null;
try
{
conn2 = new SqlConnection(connectionString2);
conn2.Open();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
finally
{
conn2.Close();
conn2.Dispose();
conn2 = null;
}
scope.Complete();
}

cannot access database from multiple unit tests

I had the connection string and a bunch of my unit tests using it in order to test the logic of some class which was applying some CRUD operations to it. So I was passing it as a private constant field in test class and sharing it to my tests. Everything worked perfectly fine!
But then I realized I have to do it as integration testing. So I've decided to use static helper class to create database via session for me tests to work with it and then drop.
The class is the following:
public static class LocalDB
{
public const string DB_DIRECTORY = "Data";
public static string GetLocalDB(string dbName, bool deleteIfExists = false)
{
try
{
var outputFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), DB_DIRECTORY);
var mdfFilename = dbName + ".mdf";
var dbFileName = Path.Combine(outputFolder, mdfFilename);
var logFileName = Path.Combine(outputFolder, $"{dbName}_log.ldf");
if (!Directory.Exists(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
if (File.Exists(dbFileName) && deleteIfExists)
{
if (File.Exists(logFileName)) File.Delete(logFileName);
File.Delete(dbFileName);
CreateDatabase(dbName, dbFileName);
}
else if (!File.Exists(dbFileName))
{
CreateDatabase(dbName, dbFileName);
}
var connectionString = string.Format(#"Data Source=(LocalDB)\v11.0;AttachDBFileName={1};Initial Catalog={0};Integrated Security=True;", dbName, dbFileName);
CreateTable(connectionString, "Cpa", dbName);
return connectionString;
}
catch(Exception ex)
{
throw;
}
}
public static bool CreateDatabase(string dbName, string dbFileName)
{
try
{
var connectionString = #"Data Source=(LocalDB)\v11.0;Initial Catalog=master;Integrated Security=True";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var cmd = connection.CreateCommand();
DetachDatabase(dbName);
cmd.CommandText = string.Format("CREATE DATABASE {0} ON (NAME = N'{0}', FILENAME = '{1}')", dbName, dbFileName);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
return File.Exists(dbFileName);
}
catch
{
throw;
}
}
public static bool DetachDatabase(string dbName)
{
try
{
var connectionString = $#"Data Source=(LocalDB)\v11.0;Initial Catalog=master;Integrated Security=True";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var cmd = connection.CreateCommand();
cmd.CommandText = $"exec sp_detach_db '{dbName}'";
cmd.ExecuteNonQuery();
cmd.Dispose();
return true;
}
}
catch(Exception ex)
{
return false;
}
}
public static bool CreateTable(string connectionString, string tableName, string dbName)
{
connectionString = connectionString.Replace("master", dbName);
try
{
using (var connection = new SqlConnection(connectionString))
{
var createTableQuery = $#"CREATE TABLE {tableName}(
CrmId nvarchar(50) NOT NULL,
Service nvarchar(25) NOT NULL,
RecurringReference nvarchar(50),
ExpiryDate datetime,
CardNumber nvarchar(50),
Enabled bit,
Brand nvarchar(50),
CpaType nvarchar(50),
Channel nvarchar(50)
);";
var command = new SqlCommand(createTableQuery, connection);
connection.Open();
var reader = command.ExecuteReader();
reader.Dispose();
return true;
}
}
catch (Exception ex)
{
return false;
}
}
}
I was calling it's GetLocalDB method in my test class' ctor amd initializing field.
After that I've got the following error "the process cannot access the file blah log.ldf because it is used by another process"
!!! All the tests were using the same connection string, I have no idea what's gone wrong. It can't be the unit tests failure for all I've changed is connection string (was for already existent db -> changed to temporary local (LocalDb class))
Thanks!
I also used localDb for testing in my web application and had a similar issue. This issue can sometime happen if while debugging you stopped in between or some test ran into any exception and disposing of localdb did not happen properly. If that happens then next time when you start running test it wont create the db(inspite of the check ifdbexists) and run into some exception like you mentioned. To check I added a different name every time we ran a set of tests so that db gets created on start of running test and drops after executing all the tests.As its not ideal to give different name every time , try disposing the localdb in a finally block to make sure it happens even in case of exception

How to use transactions in SMO.Server ConnectionContext

Following examples 1, 2 I wrote the following:
using System;
using System.Data.SqlClient;
using System.IO;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
namespace ScriptRunner
{
class Program
{
static void Main(string[] args)
{
var script = File.ReadAllText("Test.sql");
const string sqlConnectionString = #"Data Source=my\ds;
Initial Catalog=myic;
Connection Timeout=0;
Integrated Security=true";
SqlConnection connection = null;
Server server = null;
try
{
connection = new SqlConnection(sqlConnectionString);
server = new Server(new ServerConnection(connection));
connection.Open();
server.ConnectionContext.BeginTransaction();
server.ConnectionContext.ExecuteNonQuery(script);
server.ConnectionContext.CommitTransaction();
}
catch { server.ConnectionContext.RollBackTransaction(); }
finally { connection?.Dispose(); }
}
}
}
Everything works, except the transactions. The command just runs, if there is the error, everything before is already in database. How to make transactions work here?
[EDIT]
When I change the code to open the transaction at the SqlConnection level like this (here it's suggested, that there should be no difference):
SqlTransaction transaction = null;
try
{
connection = new SqlConnection(sqlConnectionString);
server = new Server(new ServerConnection(connection));
connection.Open();
transaction = connection.BeginTransaction();
server.ConnectionContext.ExecuteNonQuery(script);
transaction.Commit();
}
It throws InvalidOPexception : "ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized."
Yet I do not see a place where I can access the command object.
I've faced with same problem "ExecuteNonQuery requires the command to have a transaction...".
It seems that if perform begin/commit transaction for ServerConnection, not for SqlConnection then everything works.
...
SqlConnection connection = new SqlConnection(sqlConnectionString);
ServerConnection srvCon = new ServerConnection(connection);
try
{
server = new Server(srvCon);
srvCon.BeginTransaction();
server.ConnectionContext.ExecuteNonQuery(script);
srvCon.CommitTransaction();
}
catch
{
srvCon.RollBackTransaction();
}
...

C# OleDBConnection connecting to previous IP?

I have multiple databases (30+) that are used at clinics and setup automatically via the license software they use. So each database is named the same and use the same port, the only thing that changes is the IP. That being said, I am using the following code to attempt to run a query against them individually. However, when I change out the IP and run the script again it is returning the results from the previous server.
using System;
using System.Diagnostics;
using System.Data.OleDb;
namespace ConnectionTest
{
class Program
{
static void Main(string[] args)
{
using (OleDbConnection conn = new OleDbConnection("Provider=SAOLEDB.10;LINKS=tcpip(host=X.X.X.X,PORT=2638);ServerName=EAGLESOFT;Integrated Security = True; User ID = dba; PWD = sql"))
{
try
{
conn.Open();
using (OleDbCommand cmd = new OleDbCommand("SELECT tran_num, provider_id, tran_date FROM transactions WHERE tran_date LIKE '2015-11-23%'", conn))
{
using (OleDbDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("{0}|{1}|{2}", reader.GetValue(0).ToString(), reader.GetValue(1).ToString(), reader.GetValue(2).ToString());
}
}
}
}
catch (Exception connerr) { Debug.WriteLine(connerr.Message); }
conn.Close();
}
if (Debugger.IsAttached)
{
Console.ReadLine();
}
}
}
}
I don't see any reason why you would be getting previous results, here. Are you running multiple instances of this code at the same time? Are you manually changing the IP address in your code each time? I assume the "Transactions" object is an actual table and not something being generated on-the-fly?
With just a tiny bit of modification, you could pass the IP address as a command line parameter:
static void Main(string[] args)
{
string ip, port = null;
for (int i = 0; i < args.Length; i++)
{
if (args[i].StartsWith("/i:"))
ip = args[i].Substring(args[i].IndexOf(':') + 1);
else if (args[i].StartsWith("/p:"))
port = args[i].Substring(args[i].IndexOf(':') + 1);
}
// Default the port value to 2638 (since I have no idea if that changes).
if (string.IsNullOrEmpty(port))
port = "2638";
string connStr = string.Format("Provider=SAOLEDB.10;LINKS=tcpip(host={0},PORT={1});ServerName=EAGLESOFT;Integrated Security = True; User ID = dba; PWD = sql", ip, port);
using (OleDbConnection conn = new OleDbConnection(connStr))
{
try
{
conn.Open();
if (conn.State != System.Data.ConnectionState.Open)
// You could also implement a WHILE loop with a small delay (~1200ms) and try again to open the connection, with a counter to "fail" after a certain number (like 3) of attempts.
throw new Exception("Unable to open connection to database.");
using (OleDbCommand cmd = new OleDbCommand("SELECT tran_num, provider_id, tran_date FROM transactions WHERE tran_date LIKE '2015-11-23%'", conn))
using (OleDbDataReader reader = cmd.ExecuteReader())
while (reader.Read())
Console.WriteLine("{0}|{1}|{2}", reader.GetValue(0).ToString(), reader.GetValue(1).ToString(), reader.GetValue(2).ToString());
}
catch (Exception connerr)
{ Debug.WriteLine(connerr.Message); }
finally
{ conn.Close(); }
}
if (Debugger.IsAttached)
{
Console.ReadLine();
}
}
First and foremost (for testing purposes, that is) I would probably try removing the ServerName parameter since you're already providing an IP address explicitly.
So your connection string would be:
Provider=SAOLEDB.10;LINKS=tcpip(host=X.X.X.X,PORT=2638);Integrated Security = True; User ID = dba; PWD = sql
Similar to ORA files, DSNs, etc, it almost sounds like you have a server alias configured for EAGLESOFT that may be overwriting the IP preference in your testing.
Make sure you change EagleSoft as well as IP. You have to pass the IP address as an arg[]. Modify code as below to allow the IP to change
string host = "X.X.X.X";
string conStr = string.Format("Provider=SAOLEDB.10;LINKS=tcpip(host={0},PORT=2638);ServerName=EAGLESOFT;Integrated Security = True; User ID = dba; PWD = sql", host);
using (OleDbConnection conn = new OleDbConnection(conStr))
​

cannot connect to oracle server from C#.net application

I'm trying to connect to remote Oracle server. My connection string -
OdbcConnection con = new OdbcConnection();
con.ConnectionString = #"Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= xxxx)(PORT=xxxxx))(CONNECT_DATA=(SERVER=dedicated)(SERVICE_NAME=abc.domain.com)));USER ID=user1;Password=pwd;";
I encountered error saying - "ERROR [IM002] [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified" (System.Data.Odbc.OdbcException) Exception Message = "ERROR [IM002] [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified", Exception Type = "System.Data.Odbc.OdbcException", Exception WinRT Data = ""
I specified my connection string according to my TNSNAMES.ora
Entry for my DB in TNSNAMES.ora goes like this:
DB.WORLD=
(DESCRIPTION=
(ADDRESS=
(PROTOCOL=TCP)
(HOST= xxxx)
(PORT=xxxxx)
)
(CONNECT_DATA=
(SERVER=dedicated)
(SERVICE_NAME=abc.domain.com)
)
)
Can someone explain on the error. Please help/suggest if my connection string went wrong and how to connect to Oracle server from my windows application
first install odp.net.managed using nuget packet manager:
Install-Package odp.net.managed
odp.net.managed work without preinstalled Oracle Client
next:
const string connectionString = #"Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= xxxx)(PORT=xxxxx))(CONNECT_DATA=(SERVER=dedicated)(SERVICE_NAME=abc.domain.com)));USER ID=user1;Password=pwd;";
var connection = new OracleConnection(connectionString);
connection.Open();
if you have tnsnames.ora in application folder:
const string connectionString = #"Data Source=DB.WORLD;USER ID=user1;Password=pwd;";
var connection = new OracleConnection(connectionString);
connection.Open();
or if tnsnames.ora in other folder:
Environment.SetEnvironmentVariable("TNS_ADMIN", #"path_to_tnsadmin.ora");
const string connectionString = #"Data Source=DB.WORLD;USER ID=user1;Password=pwd;";
var connection = new OracleConnection(connectionString);
connection.Open();
you need to use OracleConnection
OracleConnection conn = new OracleConnection(connectionString);
download and install Oracle Data Provider for .NET
Go to Connections Strings for Oracle
Maybe will find some help
Use following Code:
using System;
using Oracle.DataAccess.Client;
class ConnectionSample
{
static void Main()
{
OracleConnection con = new OracleConnection();
//using connection string attributes to connect to Oracle Database
con.ConnectionString = "User Id=scott;Password=tiger;Data Source=oracle";
con.Open();
Console.WriteLine("Connected to Oracle" + con.ServerVersion);
// Close and Dispose OracleConnection object
con.Close();
con.Dispose();
Console.WriteLine("Disconnected");
}
}
Source ONE , TWO and THREE
Try something like this class :
public class OracleOperations
{
OracleConnection oraConn = new OracleConnection();
private bool connStatus;
public OracleOperations()
{
connStatus = false;
connect();
}
~OracleOperations()
{
disconnect();
}
public bool getConnStatus()
{
return connStatus;
}
public void connect()
{
string connString = "User Id=xxxx; Password=yyyyy; Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=10.10.10.10)(PORT=1583))(CONNECT_DATA=(SERVER=dedicated)(SID=oracledb)))";
if (oraConn.State != ConnectionState.Open)
{
try
{
oraConn.ConnectionString = connString;
oraConn.Open();
Console.WriteLine("Successful Connection");
connStatus = true;
}
catch (Exception eOra)
{
Console.WriteLine(eOra.Message+ "Exception Caught");
connStatus = false;
throw eOra;
}
}
}
public void disconnect()
{
if (oraConn.State == ConnectionState.Open)
{
try
{
oraConn.Close();
connStatus = false;
Console.WriteLine("Connection Closed");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + "Exception Caught");
}
}
}
}
I would try Tnsping utility to make sure you can connect via tnsnames.ora
Try putting tnsnames.ora and sqlnet.ora in the same folder of the application and see if that addresses the issue.
With Managed ODP.Net there is one catch it does not support LDAP look up (e.g. LDAP.ora)
I'Ve Created an app.config File and configured the DB entry like this
<configuration>
<configSections>
<section name ="Environment" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<Environment>
<add key ="CIT" value ="Password=pwd123;User ID=abc123;Data Source=db1;Persist Security Info=True;Provider=MSDAORA"/>
<add key ="SIT" value ="Password=pwd234;User ID=abc234;Data Source=db2;Persist Security Info=True;Provider=MSDAORA"/>
<add key ="UAT" value ="Password=pwd345;User ID=abc345;Data Source=db3;Persist Security Info=True;Provider=MSDAORA"/>
</Environment>
</configuration>
Reffered that configuration into my form using ConfigurationManager(Need to refer the assembly - system.configuration). Add namespace - using System.Collections.Specialized to avail NameValueCollection. Code goes like this
environments = ConfigurationManager.GetSection("Environment") as NameValueCollection;
string strConnString = environments[envs];
conn = new OleDbConnection(strConnString);
conn.Open();
OleDbDataAdapter objDa = new OleDbDataAdapter("select * from tblABC", conn);
DataSet ds1 = new DataSet();
objDa.Fill(ds1); dataGridView1.DataSource = ds1.Tables[0];
Using datset, i've populated datagrid using an OleDbDataAdapter. It worked for my Windowsapplication.

Categories