I'm not the first to have these issues, and will list some reference posts below, but am still looking for a proper solution.
I need to call a stored procedure (Oracle 10g database) from a C# web service. The web server has an Oracle 9i client installed and I am using Microsofts System.Data.OracleClient.
The procedure takes an XML as a CLOB. When the XML was over 4000 Bytes (which is likely in a normal use case), I stumbled over the following error:
ORA-01460 - unimplemented or unreasonable conversion requested
I've found this, this and this post.
Further I found a promising workaround which doesn't call the stored procedure directly from C# but defines a piece of anonymous PL/SQL code instead. This code is run as an OracleCommand. The XML is embedded as a string literal and the procedure call is done from within that piece of code:
private const string LoadXml =
"DECLARE " +
" MyXML CLOB; " +
" iStatus INTEGER; " +
" sErrMessage VARCHAR2(2000); " +
"BEGIN " +
" MyXML := '{0}'; " +
" iStatus := LoadXML(MyXML, sErrMessage); " +
" DBMS_OUTPUT.ENABLE(buffer_size => NULL); " +
" DBMS_OUTPUT.PUT_LINE(iStatus || ',' || sErrMessage); " +
"END;";
OracleCommand oraCommand = new OracleCommand(
string.Format(LoadXml, xml), oraConnection);
oraCommand.ExecuteNonQuery();
Unfortunately, this approach now fails as soon as the XML is over 32 KBytes or so, which still is very likely in my application. This time the error stems from the PL/SQL compiler which says:
ORA-06550: line1, column 87: PLS-00172: string literal too long
After some research I conclude that it's simply not feasible to solve the problem with my second approach.
Following the above-mentioned posts I have the following two options.
Switch to ODP.NET (because it is supposed to be a bug in Microsoft's deprecated DB client)
Insert the CLOB into a table and make the stored proc read from there
(The first post said some clients are buggy, but mine (9i) does not fall in the mentioned range of 10g/11g versions.)
Can you confirm that these are the only two options left? Or is there another way to help me out?
Just to clarify: the XML won't eventually be saved in any table, but it is processed by the stored procedure which inserts some records in some table based on the XML contents.
My considerations about the two options:
Switching to ODP.NET is difficult because I have to install it on a web server on which I don't have system access so far, and because we might also want to deploy the piece of code on clients, so each client would have to install ODP.NET as part of the deployment.
The detour over a table makes the client code quite a bit more complicated and also takes quite some effort on the database adapting/extending the PL/SQL routines.
I found that there is another way to work around the problem! My fellow employee saved my day pointing me to this blog, which says:
Set the parameter value when
BeginTransaction has already been
called on the DbConnection.
Could it be simpler? The blog relates to Oracle.DataAccess, but it works just as well for System.Data.OracleClient.
In practice this means:
varcmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;
var xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);
// DO NOT assign the parameter value yet in this place
cmd.Transaction = _oracleConnection.BeginTransaction();
try
{
// Assign value here, AFTER starting the TX
xmlParam.Value = xmlWithWayMoreThan4000Characters;
cmd.ExecuteNonQuery();
cmd.Transaction.Commit();
}
catch (OracleException)
{
cmd.Transaction.Rollback();
}
In my case, chiccodoro's solution did not work. I'm using ODP.NET ( Oracle.DataAccess ).
For me the solution is using OracleClob object.
OracleCommand cmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);
//connection should be open!
OracleClob clob = new OracleClob(_oracleConnection);
// xmlData: a string with way more than 4000 chars
clob.Write(xmlData.ToArray(),0,xmlData.Length);
xmlParam.Value = clob;
try
{
cmd.ExecuteNonQuery();
}
catch (OracleException e)
{
}
I guess I just googled this for you to get cheap points, but there's a great explanation here:
http://www.orafaq.com/forum/t/48485/0/
Basically you cannot use more than 4000 chars in a string literal, and if you need to do more, you must use a stored procedure. Then, you are limited to 32KB at max so you have to "chunk" the inserts. Blech.
-Oisin
chiccodoro is right.
public static int RunProcedure(string storedProcName, IDataParameter[] parameters)
{
using (OracleConnection connection = new OracleConnection(connectionString))
{
int rowsAffected;
OracleCommand command = new OracleCommand(storedProcName, connection);
command.CommandText = storedProcName;
command.CommandType = CommandType.StoredProcedure;
foreach (OracleParameter parameter in parameters)
{
command.Parameters.Add(parameter);
}
connection.Open();
try
{
// start transaction
command.Transaction = connection.BeginTransaction();
rowsAffected = command.ExecuteNonQuery();
command.Transaction.Commit();
}
catch (System.Exception ex)
{
command.Transaction.Rollback();
throw ex;
}
connection.Close();
return rowsAffected;
}
}
Related
This question already has answers here:
What are good ways to prevent SQL injection? [duplicate]
(4 answers)
Closed 1 year ago.
I've been stuck on this issue for a few hours and I can't seem to get over what is causing this issue to populate. When running my application in debug mode, it will run the foreach two times successfully returning "Query Executed!".
However, when it goes for the third time, I get this error:
Incorrect syntax near ']'.Unclosed quotation mark after the character string '')'.
My method that will perform the insert to the SQL Server table Logs:
static String connectionString = "Data Source=.\\SQLExpress;Database=ElasticSearchService;Trusted_Connection=True;";
public static async Task<int> InsertLogData()
{
SqlConnection connection = null;
SqlCommand command = null;
int numrows = 0;
try
{
var response = await _elasticClient.SearchAsync<EsSource>(s => s
.Size(3000)
.Source(src => src.Includes(i => i
.Fields(f => f.timestamp,
fields => fields.messageTemplate,
fields => fields.message)))
.Index("customer-simulation-es-app-logs*")
.Query(q => +q
.DateRange(dr => dr
.Field("#timestamp")
.GreaterThanOrEquals("2021-06-07T17:13:54.414-05:00")
.LessThanOrEquals(DateTime.Now))));
// var json = _elasticClient.RequestResponseSerializer.SerializeToString(response);
connection = new SqlConnection(connectionString);
Console.WriteLine("\nOpening connection...");
connection.Open();
Console.WriteLine("\nConnection successful!");
foreach (var item in response.Hits)
{
var dateCreated = item.Source.timestamp;
var messageTemplate = item.Source.messageTemplate;
var message = item.Source.message;
command = new SqlCommand("insert into Logs (DateCreated, MessageTemplate, Message) values ('" + dateCreated + "', '" + messageTemplate + "', '" + message + "')", connection);
numrows = command.ExecuteNonQuery();
Console.WriteLine("\nQuery Executed!");
}
connection.Close();
Console.WriteLine("\nConnection Closed....");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
command.Dispose();
connection.Dispose();
}
return numrows;
}
Is there something that is causing this that I am not seeing? Is my command = new SqlCommand() incorrect that is causing it to come with that error of:
Incorrect syntax near ']'.Unclosed quotation mark after the character string '')'.
For starters:
command = new SqlCommand("insert into Logs (DateCreated, MessageTemplate, Message) values (#dt, #mt, #m)";
command.Parameters.AddWithValue("#dt", dateCreated);
command.Parameters.AddWithValue("#mt", messageTemplate);
command.Parameters.AddWithValue("#m", message);
Then execute it
After you get comfortable with that, take a read of this blog - AddWithValue is convenient but it can cause performance issues in some contexts. I don't care about it so much for something like an insert statement but I'd recommend you read the blog to see why you should move away from making a habit of it in SQL Server, once you're down with how parameterizing works. Also worth noting that not every database has issues with AWV, and to some great extent it is preferable to use it and suffer occasional performance issues than not use parameterizing at all and get hacked
SQL in the way you've got it (and the way I've got it) adding parameters etc is actually fairly tedious. Take a look at dapper, a library that makes it a lot easier to do this sort of stuff and more, such as running a sql query and turning the result setinto a list of c# objects, a bit like parsing Json
Other things to consider:
use of ExecuteNonQueryAsync
create your command outside of the foreach loop and add dummy parameters of the right types using AddWithValue (or take the fine opportunity to branch out into adding carefully constructed parameters of the right sql db type), then open the connection and run the loop, changing the parameter values on every pass of the loop. In summary: setup once, change the values and execute 1000 times, rather than setting up 1000 times
use using to declare your command, that way it gets disposed later
Trying to figure out if it's best to use ExecuteScalar or ExecuteNonQuery if I want to return the identity column of a newly inserted row. I have read this question and I understand the differences there, but when looking over some code I wrote a few weeks ago (whilst heavily borrowing from this site) I found that in my inserts I was using ExecuteScalar, like so:
public static int SaveTest(Test newTest)
{
var conn = DbConnect.Connection();
const string sqlString = "INSERT INTO dbo.Tests ( Tester , Premise ) " +
" VALUES ( #tester , #premise ) " +
"SET #newId = SCOPE_IDENTITY(); ";
using (conn)
{
using (var cmd = new SqlCommand(sqlString, conn))
{
cmd.Parameters.AddWithValue("#tester", newTest.tester);
cmd.Parameters.AddWithValue("#premise", newTest.premise);
cmd.Parameters.Add("#newId", SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.CommandType = CommandType.Text;
conn.Open();
cmd.ExecuteScalar();
return (int) cmd.Parameters["#newId"].Value;
}
}
}
This works fine for what I need, so I'm wondering
Whether I should be using ExecuteNonQuery here because it is "more proper" for doing inserts?
Would retrieving the identity value be the same either way since I'm using an output parameter?
Are there any performance hits associated with one way or the other?
Is there generally a better way to do this overall?
I'm using Visual Studio 2010, .NET 4.0, and SQL Server 2008r2, in case that makes any difference.
As suggested by Aaron, a stored procedure would make it faster because it saves Sql Server the work of compiling your SQL batch. However, you could still go with either approach: ExecuteScalar or ExecuteNonQuery. IMHO, the performance difference between them is so small, that either method is just as "proper".
Having said that, I don't see the point of using ExecuteScalar if you are grabbing the identity value from an output parameter. In that case, the value returned by ExecuteScalar becomes useless.
An approach that I like because it requires less code, uses ExecuteScalar without output parameters:
public static int SaveTest(Test newTest)
{
var conn = DbConnect.Connection();
const string sqlString = "INSERT INTO dbo.Tests ( Tester , Premise ) " +
" VALUES ( #tester , #premise ) " +
"SELECT SCOPE_IDENTITY()";
using (conn)
{
using (var cmd = new SqlCommand(sqlString, conn))
{
cmd.Parameters.AddWithValue("#tester", newTest.tester);
cmd.Parameters.AddWithValue("#premise", newTest.premise);
cmd.CommandType = CommandType.Text;
conn.Open();
return (int) (decimal) cmd.ExecuteScalar();
}
}
}
Happy programming!
EDIT: Note that we need to cast twice: from object to decimal, and then to int (thanks to techturtle for noting this).
I have a problem trying to parameterize some "dynamic" SQL build in an existing C# class used by an ASP app. The environment is:
Win Server 2008
.NET 3.0
C#
DB2 9.x ([IBM][CLI Driver][DB2])
The existing code just concatenates the SQL with the param strings in a long SQL string - which is of course at risk for SQL injection. As is my practice whenever I see this, I tend to change the code to use parameters. But with this code I am failing. I have tried "#" and I have tried "?" - the latter is what I understand to be necessary for ODBC.
Here is a simplified code snippet (forgive me if I don't format it right - this is my first question) that I have compiled and run:
private DataSet test(String schemaName )
{
String sortField = "TABLE_NAME.COLUMN_NAME";
String sortDirection = "ASC";
OdbcConnection conn = new OdbcConnection();
DataSet ds = new DataSet();
string connStr = ConfigurationManager.AppSettings[schemaName] + dbUser;
try
{
conn.ConnectionString = connStr;
OdbcCommand cmd = new OdbcCommand("SELECT * FROM TABLE_NAME ORDER BY ? ? ");
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(sortField);
cmd.Parameters.Add(sortDirection);
logger.log("cmd SQL = \t" + cmd.CommandText );
OdbcDataAdapter da = new OdbcDataAdapter(cmd);
da.Fill(ds);
return ds;
}
catch (Exception ex)
{
ex.Data.Add("Location:", "test()");
ex.Data.Add("Connection", conn.ConnectionString);
logger.logException(ex);
throw ex;
}
finally
{
conn.Close();
}
}
Log printout:
cmd SQL = SELECT * FROM TABLE_NAME ORDER BY ? ?
Where TABLE_NAME is of course the table I am querying.
What I get in return is this (some proprietary info removed:
EXCEPTION occured at 4/26/2012 12:29:41 PM ERROR [42601] [IBM][CLI
Driver][DB2] SQL0104N An unexpected token "?" was found following "".
Expected tokens may include: "MICROSECONDS MICROSECOND SECONDS SECOND
MINUTES MINUTE HOURS". SQLSTATE=42601
at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode)Connection Driver={IBM DB2 ODBC DRIVER};
.....
Changing this to a stored proc is not allowed.
Upgrading to a later version of .NET is not allowed.
Changing/upgrading the ODBC driver is not allowed.
What I am seeing indicates to me that the "?" parameter is not being replaced.
I have tried AddWithValue() and I have tried Add(OdbcType.VarChar).Value = sortField (or something to that effect).
I am kind of at my whit's end - all of the googling and searching here indicates to me that the code above should work, but so far I have not been able to get the parameters in the SQL substituted with the variables.
Thanks in advance.
The reason the ? is an unexpected token is because you are using it in the ORDER BY clause (which I don't think is allowed).
The reason to use parameters is to mitigate the risks of user input. When building your query, if the ORDER BY field and direction are not coming via user input, you are safe in building the query with concatenation.
Only use the ? in the WHERE clause:
OdbcCommand cmd = new OdbcCommand("SELECT * FROM TABLE_NAME WHERE ID = ? ORDER BY " + sortField + " " + sortDirection);
I'm not the first to have these issues, and will list some reference posts below, but am still looking for a proper solution.
I need to call a stored procedure (Oracle 10g database) from a C# web service. The web server has an Oracle 9i client installed and I am using Microsofts System.Data.OracleClient.
The procedure takes an XML as a CLOB. When the XML was over 4000 Bytes (which is likely in a normal use case), I stumbled over the following error:
ORA-01460 - unimplemented or unreasonable conversion requested
I've found this, this and this post.
Further I found a promising workaround which doesn't call the stored procedure directly from C# but defines a piece of anonymous PL/SQL code instead. This code is run as an OracleCommand. The XML is embedded as a string literal and the procedure call is done from within that piece of code:
private const string LoadXml =
"DECLARE " +
" MyXML CLOB; " +
" iStatus INTEGER; " +
" sErrMessage VARCHAR2(2000); " +
"BEGIN " +
" MyXML := '{0}'; " +
" iStatus := LoadXML(MyXML, sErrMessage); " +
" DBMS_OUTPUT.ENABLE(buffer_size => NULL); " +
" DBMS_OUTPUT.PUT_LINE(iStatus || ',' || sErrMessage); " +
"END;";
OracleCommand oraCommand = new OracleCommand(
string.Format(LoadXml, xml), oraConnection);
oraCommand.ExecuteNonQuery();
Unfortunately, this approach now fails as soon as the XML is over 32 KBytes or so, which still is very likely in my application. This time the error stems from the PL/SQL compiler which says:
ORA-06550: line1, column 87: PLS-00172: string literal too long
After some research I conclude that it's simply not feasible to solve the problem with my second approach.
Following the above-mentioned posts I have the following two options.
Switch to ODP.NET (because it is supposed to be a bug in Microsoft's deprecated DB client)
Insert the CLOB into a table and make the stored proc read from there
(The first post said some clients are buggy, but mine (9i) does not fall in the mentioned range of 10g/11g versions.)
Can you confirm that these are the only two options left? Or is there another way to help me out?
Just to clarify: the XML won't eventually be saved in any table, but it is processed by the stored procedure which inserts some records in some table based on the XML contents.
My considerations about the two options:
Switching to ODP.NET is difficult because I have to install it on a web server on which I don't have system access so far, and because we might also want to deploy the piece of code on clients, so each client would have to install ODP.NET as part of the deployment.
The detour over a table makes the client code quite a bit more complicated and also takes quite some effort on the database adapting/extending the PL/SQL routines.
I found that there is another way to work around the problem! My fellow employee saved my day pointing me to this blog, which says:
Set the parameter value when
BeginTransaction has already been
called on the DbConnection.
Could it be simpler? The blog relates to Oracle.DataAccess, but it works just as well for System.Data.OracleClient.
In practice this means:
varcmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;
var xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);
// DO NOT assign the parameter value yet in this place
cmd.Transaction = _oracleConnection.BeginTransaction();
try
{
// Assign value here, AFTER starting the TX
xmlParam.Value = xmlWithWayMoreThan4000Characters;
cmd.ExecuteNonQuery();
cmd.Transaction.Commit();
}
catch (OracleException)
{
cmd.Transaction.Rollback();
}
In my case, chiccodoro's solution did not work. I'm using ODP.NET ( Oracle.DataAccess ).
For me the solution is using OracleClob object.
OracleCommand cmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);
//connection should be open!
OracleClob clob = new OracleClob(_oracleConnection);
// xmlData: a string with way more than 4000 chars
clob.Write(xmlData.ToArray(),0,xmlData.Length);
xmlParam.Value = clob;
try
{
cmd.ExecuteNonQuery();
}
catch (OracleException e)
{
}
I guess I just googled this for you to get cheap points, but there's a great explanation here:
http://www.orafaq.com/forum/t/48485/0/
Basically you cannot use more than 4000 chars in a string literal, and if you need to do more, you must use a stored procedure. Then, you are limited to 32KB at max so you have to "chunk" the inserts. Blech.
-Oisin
chiccodoro is right.
public static int RunProcedure(string storedProcName, IDataParameter[] parameters)
{
using (OracleConnection connection = new OracleConnection(connectionString))
{
int rowsAffected;
OracleCommand command = new OracleCommand(storedProcName, connection);
command.CommandText = storedProcName;
command.CommandType = CommandType.StoredProcedure;
foreach (OracleParameter parameter in parameters)
{
command.Parameters.Add(parameter);
}
connection.Open();
try
{
// start transaction
command.Transaction = connection.BeginTransaction();
rowsAffected = command.ExecuteNonQuery();
command.Transaction.Commit();
}
catch (System.Exception ex)
{
command.Transaction.Rollback();
throw ex;
}
connection.Close();
return rowsAffected;
}
}
I am trying to save unicode data (greek) in oracle database (10 g). I have created a simple table:
I understand that NVARCHAR2 always uses UTF-16 encoding so it must be fine for all (human) languages.
Then I am trying to insert a string in database. I have hardcoded the string ("How are you?" in Greek) in code. Then I try to get it back from database and show it.
class Program
{
static string connectionString = "<my connection string>";
static void Main (string[] args) {
string textBefore = "Τι κάνεις;";
DeleteAll ();
SaveToDatabase (textBefore);
string textAfter = GetFromDatabase ();
string beforeData = String.Format ("Before: {0}, ({1})", textBefore, ToHex (textBefore));
string afterData = String.Format ("After: {0}, ({1})", textAfter, ToHex (textAfter));
Console.WriteLine (beforeData);
Console.WriteLine (afterData);
MessageBox.Show (beforeData);
MessageBox.Show (afterData);
Console.ReadLine ();
}
static void DeleteAll () {
using (var oraConnection = new OracleConnection (connectionString)) {
oraConnection.Open ();
var command = oraConnection.CreateCommand ();
command.CommandText = "delete from UNICODEDATA";
command.ExecuteNonQuery ();
}
}
static void SaveToDatabase (string stringToSave) {
using (var oraConnection = new OracleConnection (connectionString)) {
oraConnection.Open ();
var command = oraConnection.CreateCommand ();
command.CommandText = "INSERT into UNICODEDATA (ID, UNICODESTRING) Values (11, :UnicodeString)";
command.Parameters.Add (":UnicodeString", stringToSave);
command.ExecuteNonQuery ();
}
}
static string GetFromDatabase () {
using (var oraConnection = new OracleConnection (connectionString)) {
oraConnection.Open ();
var command = oraConnection.CreateCommand ();
command.CommandText = "Select * from UNICODEDATA";
var erpReader = command.ExecuteReader ();
string s = String.Empty;
while (erpReader.Read ()) {
string text = erpReader.GetString (1);
s += text + ", ";
}
return s;
}
}
static string ToHex (string input) {
string bytes = String.Empty;
foreach (var c in input)
bytes += ((int)c).ToString ("X4") + " ";
return bytes;
}
}
Here are different outputs:
Text before sending to database in a message box:
Text after getting from database in a message box:
Console Output:
Please can you suggest what I might be doing wrong here?
I can see five potential areas for problems:
How are you actually getting the text into your .NET application? If it's hardcoded in a string literal, are you sure that the compiler is assuming the right encoding for your source file?
There could be a problem in how you're sending it to the database.
There could be a problem with how it's being stored in the database.
There could be a problem with how you're fetching it in the database.
There could be a problem with how you're displaying it again afterwards.
Now areas 2-4 sound like they're less likely to be an issue than 1 and 5. How are you displaying the text afterwards? Are you actually fetching it out of the database in .NET, or are you using Toad or something similar to try to see it?
If you're writing it out again from .NET, I suggest you skip the database entirely - if you just display the string itself, what do you see?
I have an article you might find useful on debugging Unicode problems. In particular, concentrate on every place where the encoding could be going wrong, and make sure that whenever you "display" a string you dump out the exact Unicode characters (as integers) so you can check those rather than just whatever your current font wants to display.
EDIT: Okay, so the database is involved somewhere in the problem.
I strongly suggest that you remove anything like ASP and HTML out of the equation. Write a simple console app that does nothing but insert the string and fetch it again. Make it dump the individual Unicode characters (as integers) before and after. Then try to see what's in the database (e.g. using Toad). I don't know the Oracle functions to convert strings into sequences of individual Unicode characters and then convert those characters into integers, but that would quite possibly be the next thing I'd try.
EDIT: Two more suggestions (good to see the console app, btw).
Specify the data type for the parameter, instead of just giving it an object. For instance:
command.Parameters.Add (":UnicodeString",
OracleType.NVarChar).Value = stringToSave;
Consider using Oracle's own driver instead of the one built into .NET. You may wish to do this anyway, as it's generally reckoned to be faster and more reliable, I believe.
You can determine what characterset your database uses for NCHAR with the query:
SQL> SELECT VALUE
2 FROM nls_database_parameters
3 WHERE parameter = 'NLS_NCHAR_CHARACTERSET';
VALUE
------------
AL16UTF16
to check if your database configuration is correct, you could run the following in SQL*Plus:
SQL> CREATE TABLE unicodedata (ID NUMBER, unicodestring NVARCHAR2(100));
Table created
SQL> INSERT INTO unicodedata VALUES (11, 'Τι κάνεις;');
1 row inserted
SQL> SELECT * FROM unicodedata;
ID UNICODESTRING
---------- ---------------------------------------------------
11 Τι κάνεις;
One more thing worth noting.
If you are using oracle client, and would like to include unicode characters in the CommandText, you should add the folloing line to the start of your application:
System.Environment.SetEnvironmentVariable("ORA_NCHAR_LITERAL_REPLACE", "TRUE");
This will allow you, in case you need it, to use the following syntax:
command.CommandText = "INSERT into UNICODEDATA (ID, UNICODESTRING) Values (11, N'Τι κάνεις;')";
After some investigations here we go:
string input = "•";
char s = input[0];
//table kuuku with column kuku(nvarchar2(100))
string connString = "your connection";
//CLEAN TABLE
using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
{
cn.Open();
System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("delete from kuku ", cn);
cmd.ExecuteNonQuery();
cn.Close();
}
//INSERT WITH PARAMETER BINDING - UNICODE SAVED
using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
{
cn.Open();
System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("insert into kuku (kuku) values(:UnicodeString)", cn);
cmd.Parameters.Add(":UnicodeString", System.Data.OracleClient.OracleType.NVarChar).Value = input + " OK" ;
cmd.ExecuteNonQuery();
cn.Close();
}
//INSERT WITHOUT PARAMETER BINDING - UNICODE NOT SAVED
using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
{
cn.Open();
System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("insert into kuku (kuku) values('" +input+" WRONG')", cn);
cmd.ExecuteNonQuery();
cn.Close();
}
//FETCH RESULT
using (System.Data.OracleClient.OracleConnection cn = new System.Data.OracleClient.OracleConnection(connString))
{
cn.Open();
System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand("select kuku from kuku", cn);
System.Data.OracleClient.OracleDataReader dr = cmd.ExecuteReader();
if(dr.Read())
{
string output = (string) dr[0];
char sa = output[0];
}
cn.Close();
}
}
On reading records, try
Encoding utf = Encoding.Default;
var utfBytes = odatareader.GetOracleString(0).GetNonUnicodeBytes();//OracleDataReader
Console.WriteLine(utf.GetString(utfBytes));
Solution: set NLS_LANG!
Details:
I just had the same problem, and actually had exact the same situation as described in Sergey Bazarnik's investigation. Using bind variables it works, and without them it doesn't.
The SOLUTION is to set NLS_LANG in proper place. Since I have Windows server I set it in windows registry under
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\ORACLE\KEY_OraClient11g_home1
Please note that regitry location may difer so the easiest way is to search registry for "ORACLE_HOME" string. Also other systems like Linux, Unix can set this on different way (export NLS_LANG ...)
In my case I put "NLS_LANG"="CROATIAN_CROATIA.UTF8". Since I had no that variable set it went to default value.
After changing registry you should restart process.
In my case I restarted IIS.
Regarding reason why it works with bind variables may be because it actually happens on server side, while without it actually happens on client side. So even that DB can insert proper values - before that happens, client does the unwanted corrections, since it thinks that is should do that. That is because NLS_LANG defaults to simpler code page. But instead of doing useful task, that creates a problem, which (as shown in investigation looks hard to understand).
In case you have multiple oracle versions, be sure to correct all versions in registry (in my case Oracle 10 had valid setting, but Oracle 11 had no NLS_LANG set at all).