I have 32-bit drivers installed on my box (they were installed and configured by some DBAs)
I wrote a simple script to test the drivers which pretty much is as follows
using (DataTable table = new DataTable())
{
using (OracleConnection connection = new OracleConnection())
{
connection.ConnectionString = "Data Source=alias;User id=user;Password=password";
connection.Open();
using (OracleCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = "SELECT * FROM DUAL";
table.Load(command.ExecuteReader());
}
}
}
When I compile this application as 32-bit with the 32-bit Oracle.DataAccess.dll, it executes without a hitch.
However if I compile the application as AnyCPU with the Oracle.ManagedDataAccess.dll, i get an ORA-12154 (could not resolve the connect identifier specified) error.
If I tnsping alias, it works correctly and tells me the connect identifier with the real database name.
If I then change the connection string to use the real database name instead of the alias, and then try with the managed library again, it executes without a hitch.
I've been reading around and found this answer which says the managed driver relies on the tnsnames.ora file to resolve the aliases, however I rely on LDAP servers defined in sqlnet.ora and ldap.ora.
When I tnsping, it says it uses sqlnet.ora to resolve the name.
So how come the managed drivers do not work?
May this workaround is suitable for you. You can query the LDAP server by your own and put the full connection string to your code.
You can resolve the connection string from LDAP with this code:
using (OracleConnection connection = new OracleConnection())
{
connection.ConnectionString = "Data Source=" + ResolveServiceNameLdap("alias") + ";User id=user;Password=password";
connection.Open();
}
...
private static string ResolveServiceNameLdap(string serviceName)
{
string tnsAdminPath = Path.Combine(#"C:\oracle\network", "ldap.ora");
// or something more advanced...
// ldap.ora can contain many LDAP servers
IEnumerable<string> directoryServers = null;
if ( File.Exists(tnsAdminPath) ) {
string defaultAdminContext = string.Empty;
using ( var sr = File.OpenText(tnsAdminPath) ) {
string line;
while ( ( line = sr.ReadLine() ) != null ) {
// Ignore comments or empty lines
if ( line.StartsWith("#") || line == string.Empty )
continue;
// If line starts with DEFAULT_ADMIN_CONTEXT then get its value
if ( line.StartsWith("DEFAULT_ADMIN_CONTEXT") )
defaultAdminContext = line.Substring(line.IndexOf('=') + 1).Trim(new[] { '\"', ' ' });
// If line starts with DIRECTORY_SERVERS then get its value
if ( line.StartsWith("DIRECTORY_SERVERS") ) {
string[] serversPorts = line.Substring(line.IndexOf('=') + 1).Trim(new[] { '(', ')', ' ' }).Split(',');
directoryServers = serversPorts.SelectMany(x => {
// If the server includes multiple port numbers, this needs to be handled
string[] serverPorts = x.Split(':');
if ( serverPorts.Count() > 1 )
return serverPorts.Skip(1).Select(y => string.Format("{0}:{1}", serverPorts.First(), y));
return new[] { x };
});
}
}
}
// Iterate through each LDAP server, and try to connect
foreach ( string directoryServer in directoryServers ) {
// Try to connect to LDAP server with using default admin contact
try {
var directoryEntry = new DirectoryEntry("LDAP://" + directoryServer + "/" + defaultAdminContext, null, null, AuthenticationTypes.Anonymous);
var directorySearcher = new DirectorySearcher(directoryEntry, "(&(objectclass=orclNetService)(cn=" + serviceName + "))", new[] { "orclnetdescstring" }, SearchScope.Subtree);
SearchResult searchResult = directorySearcher.FindOne();
var value = searchResult.Properties["orclnetdescstring"][0] as byte[];
if ( value != null )
connectionString = Encoding.Default.GetString(value);
// If the connection was successful, then not necessary to try other LDAP servers
break;
} catch {
// If the connection to LDAP server not successful, try to connect to the next LDAP server
continue;
}
}
// If casting was not successful, or not found any TNS value, then result is an error
if ( string.IsNullOrEmpty(connectionString) )
throw new Exception("TNS value not found in LDAP");
} else {
// If ldap.ora doesn't exist, then throw error
throw new FileNotFoundException("ldap.ora not found");
}
return connectionString;
}
ODP.NET Managed driver relies on TNS_ADMIN value being set in the config file to find the TNSNAMES.ORA file. If you don't want to set that value, you can include the TNSNAMES.ORA file in the working directory of the executable or create a TNS alias in the config file directly.
Edit: If you are relying on SQLNET.ORA and LDAP.ORA you can also copy those into the working directory as well, or set the LDAP parameters in your config file. See the ODP.NET doc for the config file parameters available for LDAP.
Related
Using C#, .Net framework 4.8, SQLite 1.0.115
I am using System.Data.SQLite.SQLiteConnection to open a sqllite database on the network drive. But it fails with "unable to open database file". While debugging it I found that it is always pointing to the local drive instead of the network.
example: I have provided "\\NW\test\DB\test.sqlite" as a data source in the connection string. But when I see an exception it says can't find the file in "C:\NW\test\DB\test.sqlite". My program runs on C: drive.
private SQLiteConnection _connection;
var builder = new SQLiteConnectionStringBuilder
{
DataSource = "\\\\NW\\test\\DB\\test.sqlite",
Version = 3,
ForeignKeys = true,
};
var connectionString = builder.ConnectionString;
if (_connection == null)
{
_connection = CreateConnection(connectionString );
_connection.Open();
}
return _connection.BeginTransaction(isolationLevel);
On connection open, I am getting this exception
SQLite error (14): os_win.c:47688: (3) winOpen(C:\NW\test\DB\test.sqlite) - The system cannot find the path specified.
Does anyone know why it is pointing to a local drive instead of the network?
By doing multiple trials and errors this change fixed the issue
private SQLiteConnection _connection;
var builder = new SQLiteConnectionStringBuilder
{
DataSource = #"\\\\NW\\test\\DB\\test.sqlite",
Version = 3,
ForeignKeys = true,
};
var connectionString = builder.ConnectionString;
if (_connection == null)
{
_connection = CreateConnection(connectionString );
_connection.Open();
}
return _connection.BeginTransaction(isolationLevel);
So, in the Datasource it needs "\\\\\\\\NW\\test\\DB\\test.sqlite" path. Not sure why so many back slashes but this fixed the problem.
I have two questions regarding connecting to an Oracle database using C#.NET.
My program references Oracle.DataAccess.dll (version 2.122.19.1) in my Oracle 19c client 64-bit folder.
I have a using statement:
using Oracle.DataAccess.Client;
and this code to connect:
string connectionString = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=myserver.sys.mycompany.com)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=mydb.mycompany.com)));User Id=MyLanId;Password=MyPasswors;Validate Connection=True;"
using (OracleConnection connection = new OracleConnection(connectionString))
{
connection.Open();
}
Question #1:
How do I create an OID connection string that does not specify the DB name but instead queries the LDAP for the detail server/port info that is hardcoded in my TNS-less connection string above?
For example, in a Java program, we connect like this.
base.oracle.db.url=jdbc:oracle:thin:#ldap://oid.gtm.internal.mycompany.com:3060/dbname,cn=OracleContext,dc=mycompany,dc=com
What would the .NET equivalent be?
Question Number #2:
When I compile my C# program I selected "Any CPU" yet my program only works when I reference the 64-bit Oracle.DataAccess.dll. Is this because my PATh variable mentions my 64-bit oracle client folder before my 32-bit folder (I have both)? Right now, my PATH variable is way over the max length of 2048 and I cannot change it without making it shorter and I'm not sure what to delete.
I found this SO post,
How do I query LDAP from C# to resolve Oracle TNS hostname while using managed ODP.NET?
Instead of just passing in a connection string that points to an LDAP server, I can do an LDAP query to get the db descriptor to build the connection string.
It's a 2 step process, so I probably want to cache the connection string once I build it. I normally count on connection pooling and "close" connections when I am done with them but this two step process seems like I have to manually add some caching of the connection string to avoid the overhead of hitting LDAP multiple times.
Is there a better way?
string directoryServer = "oid.gtm.internal.mycompany.com:3060";
string defaultAdminContext = "cn=OracleContext,dc=mycompany,dc=com";
string serviceName = "mydb";
string userId = "mylanid";
string password = "mypwd";
using (IDbConnection connection = GetConnection(directoryServer, defaultAdminContext, serviceName, userId, password))
{
connection.Open();
connection.Close();
}
private static IDbConnection GetConnection(string directoryServer, string defaultAdminContext,
string serviceName, string userId, string password)
{
string descriptor = ConnectionDescriptor(directoryServer, defaultAdminContext, serviceName);
// Connect to Oracle
string connectionString = $"user id={userId};password={password};data source={descriptor}";
OracleConnection con = new OracleConnection(connectionString);
return con;
}
private static string ConnectionDescriptor(string directoryServer, string defaultAdminContext,
string serviceName)
{
string ldapAdress = $"LDAP://{directoryServer}/{defaultAdminContext}";
string query = $"(&(objectclass=orclNetService)(cn={serviceName}))";
string orclnetdescstring = "orclnetdescstring";
DirectoryEntry directoryEntry = new DirectoryEntry(ldapAdress, null, null, AuthenticationTypes.Anonymous);
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry, query, new[] { orclnetdescstring },
SearchScope.Subtree);
SearchResult searchResult = directorySearcher.FindOne();
byte[] value = searchResult.Properties[orclnetdescstring][0] as byte[];
if (value != null)
{
string descriptor = Encoding.Default.GetString(value);
return descriptor;
}
throw new Exception("Error querying LDAP");
}
I tried to create a shareable in-memory database as per the documentation provided on SQLite Site. But I end up finding the solution to the problem.
var connectionString = "Data Source=sharedmemdb;Mode=Memory;Cache=Shared";
using (var connection1 = new SQLiteConnection(connectionString))
{
connection1.Open();
var command1 = connection1.CreateCommand();
command1.CommandText =
"CREATE TABLE Message ( Text TEXT );" +
"INSERT INTO Message ( Text ) VALUES ( 'Is there anybody out there?' );";
command1.ExecuteNonQuery();
using (var connection2 = new SQLiteConnection(connectionString))
{
connection2.Open();
var command2 = connection2.CreateCommand();
command2.CommandText = "SELECT Text FROM Message;";
var message = command2.ExecuteScalar() as string;
}
}
If I execute this code, it will create in-memory DB named as sharedmemdb and shared cache is enabled while making the connection, so this connection accessible to other connections also. If I run this first time this works pretty fine but if I close the application and run again it throws error "Table Message already exists" and this looks very strange as I created the table in-memory and this should not be available if application restarts.
After getting this error, I looked into the application directory and found the file "sharedmemdb" which means SQLite is not creating the shareable in-memory DB.
Any clue why this is happening?
After moving command to using block:
var connectionString = "Data Source =sharedmemdb; Mode = Memory; Cache = Shared";
using (var connection1 = new SQLiteConnection(connectionString))
{
connection1.Open();
using (var command1 = connection1.CreateCommand())
{
command1.CommandText =
"CREATE TABLE Message ( Text TEXT );" +
"INSERT INTO Message ( Text ) VALUES ( 'Is there anybody out there?' );";
command1.ExecuteNonQuery();
}
using (var connection2 = new SQLiteConnection(connectionString))
{
connection2.Open();
using (var command2 = connection2.CreateCommand())
{
command2.CommandText = "SELECT Text FROM Message;";
var message = command2.ExecuteScalar() as string;
}
}
}
System.Data.SQLite doesn't support the "Mode" parameter (that's Microsoft.Data.Sqlite).
However, in System.Data.SQLite you can use the "FullUri" parameter to pass arguments directly to SQLite, so you can achieve what you wanted by changing your connection string to
FullUri=file:mem.db?mode=memory&cache=shared
Protocol^ ^ ^ ^
DB Name -----| | |
Use memory mode ----+ |
Use shared cache ---------------+
(The first line being the actual connection string, the next couple of lines breaking it down)
I have created a .Net 4.5 application that reads and writes to MS Access database. While running the application on my machine (which has MS office) everything is fine but when I run this on my server (which does not has MS Office but has ACE ) I get the object reference not set to an instance of an object.
This happens when I click the button. Following is the code
try
{
OleDbConnection conn = new OleDbConnection(connectionString);
conn.Open();
est_index = getTableIndex(conn,estimateTableIndex);
for (int i = 0; i < inputValues.Count; i++)
{
string FieldName = "";
string FieldValue = "";
if (inputValues[i] is System.Web.UI.WebControls.TextBox)
{
FieldName = inputValues[i].ClientID;
FieldValue = ((System.Web.UI.WebControls.TextBox)(inputValues[i])).Text;
}
else
{
FieldName = inputValues[i].ClientID;
FieldValue = ((System.Web.UI.WebControls.DropDownList)(inputValues[i])).SelectedValue;
}
//everything fine till here..then throws the error below
string my_querry = "INSERT INTO Estimate VALUES(#Est_id,#FieldName,#FieldValue)";
OleDbCommand cmd = new OleDbCommand(my_querry, conn);
cmd.Parameters.AddWithValue("#Est_id", est_index);
cmd.Parameters.AddWithValue("#FieldName", FieldName);
cmd.Parameters.AddWithValue("#FieldValue", FieldValue);
cmd.ExecuteNonQuery();
}
conn.Close();
}
catch (Exception e)
{
Console.Write(e.InnerException.Message);
}
Not sure why this is happening ?
EDIT: I created a virtual machine and downloaded the ACE and the same error occurred that clients face
EDIT 2: The connection string looks like "Provider = Microsoft.ACE.OLEDB.12.0; Data Source = C:\cohber.accdb"
I tried to debug and see where the code broke and it broke at cmd.ExecuteNonQuery();. The message at exception was operation must use an updateable query
Edit 3: the MS Access file is located in c driver. I gave all the users full permission but still get the error
You need to set read/ write permissions to the folder containing the access .mdb file
ive been trying to get my sqlite to read a remote file but is just flatout tells me this isnt supported is there a workaround for this ?
here is the function that gives the error
public void Run(string sql,string check,string file)
{
SQLiteConnection m_dbConnection;
string test = "Data Source=" + file + ";Version=3;";
m_dbConnection = new SQLiteConnection(test);
m_dbConnection.Open();
SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection);
SQLiteDataReader reader = command.ExecuteReader();
if (check == "0")
{
while (reader.Read())
comboBox1.Items.Add(reader["name"] + "." + reader["TLD"]);
comboBox1.SelectedIndex = 0;
}
else
{
proxy = reader["proxyip"].ToString();
check = "0";
}
}
error i get is "URI formats are not supported"
the file variable is filled by one of 2 values.
string filelocal = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\\unblocker\\sites.db";
or
string remotefile = "http://127.0.0.1/test.db";
the one that gives the error is the remote file.
The Connection string uses as a datasource the db file which is expected on your local file system, take a look at this example code.
You can transorfm this using:
Uri uriFormatted = new Uri(file);
uriFormatted.AbsolutePath; // Try to use this value instead to call your function
EDIT
SQLite is a local standalone database, that is used for standalone software
Consider using SQLitening:
SQLitening is a client/server implementation of the popular SQLite database.