Insert byte[] to blob column in Informix DB using c# - c#

Just like these links
Link 1
Link 2
Link 3
Link 4
Am also unable to insert only byte[] related operations on my informix database. I tried many ways and gone through IBM site. but no where its explained "how to use byte[] to insert into blob column using c#".
"LINK 4" is very helpful. but am facing problem with this code.
Error: The %0 enumeration value, %1, is invalid.
At line: blob.Open(IfxSmartLOBOpenMode.ReadWrite);
if i use cmd.Parameters.Add(new IfxParameter()).Value = byteuploaded`;
Here is my code snippet.
protected void uploadfile_Click(object sender, EventArgs e)
{
string extension;
// checks if file exists
if (!_imageUpload.HasFile)
{
_resultLbl.Text = "Please, Select a File!";
return;
}
// checks file extension
extension = System.IO.Path.GetExtension(_imageUpload.FileName).ToLower();
if (!extension.Equals(".jpg") && !extension.Equals(".jpeg") && !extension.Equals(".png"))
{
_resultLbl.Text = "Only image files (.JPGs and .PNGs) are allowed.";
return;
}
try
{
// ========= This is not working ==============
string sqlQuery = "insert into db95:TestBlobUpload (id ,fileblob) values('2', 'two');";
// ========= This is working properly ==============
//string sqlQuery = "insert into db95:TestBlobUpload (id ,filetext) values('4',?);";
string connString = "Database=db95;Host=172.16.XX.XX;Server=vsXXXX;Service=88;Protocol=onsoctcp;UID=ed;Password=ca94;";
using (this.connection = new IfxConnection(connString))
{
this.connection.Open();
using (this.cmd = new IfxCommand(sqlQuery, this.connection))
{
// Start a local transaction
this.trans = this.connection.BeginTransaction(IsolationLevel.Unspecified);
// Assign transaction object for a pending local transaction
this.cmd.Transaction = trans;
try
{
IfxBlob byteuploaded = new IfxBlob(this.connection);
byteuploaded.Read(_imageUpload.FileBytes);
// ========= BOTH OF THESE are not working ==============
//cmd.Parameters.Add(new IfxParameter()).Value = data;// System.Text.Encoding.UTF8.GetString(data);
cmd.Parameters.Add(new IfxParameter()).Value = byteuploaded;// _imageUpload.FileBytes;
int res = cmd.ExecuteNonQuery();
// commiting the transaction
this.cmd.Transaction.Commit();
}
catch
{
//this.cmd.Transaction.Rollback();
}
}
this.connection.Close();
}
}
catch (Exception)
{
}
}
am using this dll as a reference and using IBM.Data.Informix;
particularly am unable to add byte[] to blob columns. All other insert/update/delete operations i can do.
Any help?
I even upgraded to ibm_data_server_driver_package_win64_v10.1.exe & clientsdk.4.10.FC1DE.WIN.exe
But am facing problems with dll compatibility. unable to load'XX.XX.dll" exception is comin.
I even tried to execute the insert query using
INSERT INTO db95#vsXXXX:testblobupload (fileblob)
VALUES (db95#vsXXXX:FILETOBLOB('C:\tmp\Untitled.png', 'client'));
and facing error as
ERROR: Smart-large-object error.
Error Code: -9810.
Smart Large Objects: No sbspace number specified.

This is not your c# app. It's the Informix environment needs to be setup for large smart objects. I think it basically specifies space to use on server. I don't know anything else. If you return the isam error code it would be this
-12053 Smart Large Objects: No sbspace number specified.
No default sbspace was f ound, and the caller has not specified an sbspace
to use.
Either specify the smart-large-object space name in the
smart-large-object function call or set the SBSPACENAME onconfig
file parameter to the name of a valid smart-large-object space.

We can include blob fields in specific sbspace using the PUT IN clause.
If we use connection.GetIfxBlob() without specifying the table name and column name, the blob field will be included in the default sbspace set in the onconfig SBSPACENAME, and if not set will give the error SQLERR-9810 ISAM ERR-12053.

I guess, that the best way of save Informix Blob is using this function :
string insertSql = string.Format("insert into \"{0}\" (sbfotoint,NmArqBlob) values (?,?);", this.table);
using (var command = new IfxCommand(insertSql, this.connection))
{
this.connection.Open();
SetRole();
command.CommandType = System.Data.CommandType.Text;
command.Parameters.Add(new IfxParameter()).Value = CreateIfxBlob(entidade.SBlob);
command.Parameters.Add(new IfxParameter()).Value = entidade.NomeArquivo;
command.ExecuteNonQuery();
this.connection.Close();
}
private IfxBlob CreateIfxBlob(byte[] data)
{
IfxBlob blob = connection.GetIfxBlob(this.table, "sbfotoint");
blob.Open(IfxSmartLOBOpenMode.ReadWrite);
blob.Write(data);
blob.Close();
return blob;
}

Related

How to fix “Invalid operation on a closed object” exception when trying to Read an OracleDataReader

I'm creating a web API using .Net Core and the Oracle.ManagedDataAccess.Core plugin that accesses an Oracle database to retrieve a Blob field and return it as a byte array. I created a function (getBlobfromDB) that gets the Oracle connection string from an encrypted field on a SQL database, connects and uses an OraleDataReader to retrieve and return the Blob field. But when the OraleDataReader tries to Read the record, I'm getting this exception: "Invalid operation on a closed object".
When you search for this issue, the most relevant answer is this post
However, I'm 100% percent sure that user on the conn string has access since that is the schema owner, and I also tried adding the schema owner to the Select query (SELECT FIELD FROM SCHEMA_OWNER.TABLE WHERE ID = "") getting the same error
So I tried two other things, getting a different VARCHAR field from another table on the same database, still getting the same error; and trying on a different Oracle database and I was able to successfully retrieve the data. I noticed that the Oracle versions on the servers are different, 12.2 for the working one and 11.2 for the non-working, this may be the reason? I don't know what else I can do or try, I'll appreciate any help/advice that you can give me
This is the simplified function
private OracleBlob getBlobfromDB(string sSystem, string sID)
{
string sSQL = String.Empty;
string connString = String.Empty;
OracleConnection oConn = new OracleConnection();
if (string.IsNullOrWhiteSpace(sID) || string.IsNullOrWhiteSpace(sSystem) )
{
return null;
}
sSQL = "SELECT BLOB_FIELD FROM TABLE WHERE ID = " + sID;
connString = getConnectionString(sSystem);
try
{
using (oConn = new OracleConnection(connString))
{
oConn.Open();
OracleCommand oCom = new OracleCommand(sSQL, oConn);
OracleDataReader oDr = oCom.ExecuteReader();
if (oDr.HasRows)
{
//I'm able to reach to this point before getting the exception
oDr.Read();
OracleBlob blob = oDr.GetOracleBlob(0);
// Clean up
oDr.Close();
oDr.Dispose();
oCom.Dispose();
return blob;
}
else
{
// Clean up
oDr.Close();
oDr.Dispose();
oCom.Dispose();
return null;
}
}
}
catch (Exception x)
{
return null;
}
finally
{
oConn.Close();
}
}
With the Oracle.ManagedDataAccess dependency you should not Close() the connection and do not use the using clause. It seems that the dependency itself closes the connection at will.
Remove: using(){} keyword and oConn.Close() from your code.

C# no data returned from database - "Invalid attempt to read when no data is present"

I am try to display data from a database. However even though data exists in the database no records are being returned.
If run the following query:
select Id, Movie_Name from [MovieTable] where Movie_Name like '10,000 BC'
I get being returned:
However when running a similar query in c# nothing seems to be being returned. My code is as follows:
try
{
string query = "select * from [MovieTable] where Movie_Name like #MovieName";
string movieName = "10,000 BC"
using (SqlConnection sconnection = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=E:\Application\ApplicationDatabase.mdf;Integrated Security=True");)
using (SqlCommand command = new SqlCommand(query, sconnection))
{
sconnection.Open();
command.Parameters.AddWithValue("#MovieName", movieName);
using (SqlDataReader oReader = command.ExecuteReader())
{
if (oReader != null)
{
while (oReader.Read())
{
MessageBox.Show(oReader["Movie_Name"].ToString());
}
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
The message box never appears. Adding a third message box just above oReader.Read() displays the message "Invalid attempt to read when no data is present". Is there something i am missing?
Your code runs fine for me, with little adaptions though.
You query the value of the attribute 'Year' which is not present in the table and in your first query:
oReader.GetOrdinal("Year")
That causes an exception in the sample.
Single Quotes are not needed since you are using a parametrized query.
For debugging calls to System.Diagnostics.Debug.WriteLine() are more useful than MessageBoxes
Issue was with the data itself. Field was incorrectly set as type "Text". I altered the fields data type to "nvarchar(MAX)". It also mean't the query could be altered to:
"select * from [MovieTable] where Movie_Name = #MovieName"

How to Update a BLOB column, error ORA-00932, while Insert works

I cannot update a BLOB field, but Insert works, see code below.
My guess is that it has something to do with the problem of storing one BLOB value in lots of records, involving copying large data.
In my case, I know that only one record will be updated, but Oracle might be of the opinion that potentially several records may need to be updated. With Insert, there is guaranteed only 1 record involved, but not always with Update. Now how do I get around this problem?
NB: the ArtNr field in the Where-clause is a primary key with a Unique index.
By the way, I find it worrysome that there are lots of code examples for Insert BLOB on the internet, but I could not find one for Update BLOB.
using Oracle.DataAccess.Client;//needs reference to Oracle.DataAccess.dll
using Oracle.DataAccess.Types; //OracleBlob
public static bool StoreBlobImage(OracleConnection conn, string ArtNr, byte[] bImageJpg)
{
bool Ok = false;
#if true // this is what I need, but does not work
string Sql = "update MyTable set Image = :Image where ArtNr = :ArtNr";
#else // this works
string Sql = "insert into MyTable (ArtNr, Image) values (:ArtNr, :Image)";
#endif
using (OracleCommand cmd = new OracleCommand(Sql, conn))
{
//cmd.Connection = conn;
//cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("ArtNr", OracleDbType.Varchar2, 8).Value = ArtNr;
#if false // tried method 1
cmd.Parameters.Add("Image", OracleDbType.Blob).Value = bImageJpg;
#else // now trying method 2
OracleParameter blobParameter = new OracleParameter();
blobParameter.OracleDbType = OracleDbType.Blob;
blobParameter.ParameterName = "Image";
blobParameter.Value = bImageJpg;
blobParameter.Direction = ParameterDirection.Input;
blobParameter.IsNullable = true;
cmd.Parameters.Add(blobParameter);
#endif
try
{
conn.Open();
cmd.ExecuteNonQuery(); // ORA-00932: inconsistent datatypes: expected - got BLOB
}
catch (Exception TheException)
{
}// debug breakpoint
}
return Ok;
}
I really though you were imagining things when I read your post. Out of curiousity, I tried it and was amazed that this error really does occur.
There is good news. I poked around and found this:
How can I update data in CLOB fields using a >> prepared query << with ODP (Oracle.DataAccess)?
It turns out when using an update statement with an LOB, the LOB has to be declared first in the parameters. With that in mind, I got the same error you did with your code, but this worked perfectly:
public static bool StoreBlobImage(OracleConnection conn, string ArtNr, byte[] bImageJpg)
{
bool Ok = false;
string Sql = "update MyTable set Image = :Image where ArtNr = :ArtNr";
using (OracleCommand cmd = new OracleCommand(Sql, conn))
{
cmd.Parameters.Add("Image", OracleDbType.Blob).Value = bImageJpg;
cmd.Parameters.Add("ArtNr", OracleDbType.Varchar2, 8).Value = ArtNr;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception TheException)
{
}
}
return Ok;
}
Simply by switching the parameters.
I gave a kudo to the question and answer of that original question (same guy, in this case).
You are right, there is precious little on the web in the way of help for updates on BLOBs in Oracle.
Great question. I feel like I learned something today.
-- EDIT --
Per OP's suggestion, there is another fix, per the same thread referenced above, that can prevent the necessity of rearranging the parameters. My guess is this might also come in handy if you are updating multiple LOBs.
Switching the BindByName Property appears to also resolve the issue:
cmd.BindByName = true;

Empty database table

I want to insert values in "Navn" row and "Varenr" row in the DB table, when I'm clicking on a button. I have following code:
private void button2_Click(object sender, EventArgs e)
{
using (SqlConnection cn = new SqlConnection(#"Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Produkt.mdf;Integrated Security=True"))
{
try
{
SqlCommand cm = new SqlCommand();
cm.Connection = cn;
string col1 = textBox2.Text;
string col2 = textBox3.Text;
//generate sql statement
cm.CommandText = "INSERT INTO ProduktTable (Navn,Varenr) VALUES (#col1,#col2)";
//add some SqlParameters
SqlParameter sp_add_col1 = new SqlParameter();
sp_add_col1.ParameterName = "#col1";
//data type in sqlserver
sp_add_col1.SqlDbType = SqlDbType.NVarChar;
//if your data type is not number,this property must set
//sp_add_col1.Size = 20;
sp_add_col1.Value = textBox2.Text;
//add parameter into collections
cm.Parameters.Add(sp_add_col1);
//in your insert into statement, there are how many parameter, you must write the number of parameter
SqlParameter sp_add_col2 = new SqlParameter();
sp_add_col2.ParameterName = "#col2";
//data type in sqlserver
sp_add_col2.SqlDbType = SqlDbType.NVarChar;
//if your data type is not number,this property must set
//sp_add_col2.Size = 20;
sp_add_col2.Value = textBox2.Text;
//add parameter into collections
cm.Parameters.Add(sp_add_col2);
//open the DB to execute sql
cn.Open();
cm.ExecuteNonQuery();
cn.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
But unfortunately, my data table is still empty:
I have set a breakpoint on the ExecuteNonQuery function, and it is triggered, when pressing on the button:
My table definition:
Your connection string is causing this:
Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Produkt.mdf;Integrated Security=True"
|DataDirectory| Your database that is being updated in this method is in your App Data Directory while the one you are trying to retrieve data from is in your project folder...
|DataDirectory| is a substitution string that indicates the path to the database. DataDirectory also makes it easy to share a project and also to deploy an application. For my PC my App Data Directory is:
C:\Users\MyUserName\AppData\...
If you browse to this location and then go to following folders
...\Local\Apps\2.0\Data
You will be able to find your particular application directory probably stored with your assembly name, or some hash when you go there you will find it the database there is being updated just fine. This connection string is best for deployment.
You can also try this:
If you notice that Server Explorer is detecting all the databases on my PC and you can notice that there are couple of MINDMUSCLE.MDF files but all are at different paths, this is because there is one file in DEBUG directory, one in my PROJECT directory, one in my APP DATA directory. The ones starting with the numbers are stored in my APP DATA directories... If you select your respective database file and then run the SELECT query against it, you will get your data.
I made a tutorial some time ago. May be it will help you:
Check the value that ExecuteNonQuery is returning. It should return an int with the number of records affected by the SQL statement.
If it comes back with a value other than 0, then you know a record is being inserted somewhere. Before you close the connection, run a SQL query against the table to select all of the records and see if they come back through the code.
SELECT * FROM ProduktTable
If you get some records, then you may want to double check the database you're looking at through the IDE and the one your inserting records into through the code. It could be possible that you've got two different databases and you're querying one while inserting into another one.
Those are the steps that I would go through to help narrow down the issue and sounds like something I've probably done before. I hope it helps!

Efficiently inserting multiple records in oracle db using Oracle client

I've C#-WCF/Winforms application.It inserts 450,000 plus records in a staging table in Oracle database using Oracle Client API and a stored proc having a simple insert query.
Its taking about 15 minutes to insert records in db and sometimes the records dont get inserted too..giving all sorts of timeout errors at the wcf side.
Is there any efficient way of doing these inserts?
Thanks for reading.
Here's my code which does the batch insert:
OracleTransaction tran = null;
UpdateRowSource oldURS = this.cmd.UpdatedRowSource;
OracleCommand oldCmd = this.dbAdapter.InsertCommand;
int oldUBS = this.dbAdapter.UpdateBatchSize;
try
{
SetOutputParams();
this.OpenDBConnection();
tran = this.dbConn.BeginTransaction();
this.cmd.Transaction = tran;
this.cmd.UpdatedRowSource = UpdateRowSource.OutputParameters;
this.dbAdapter.InsertCommand = this.cmd;
this.dbAdapter.UpdateBatchSize = size;
this.dbAdapter.Update(data);
tran.Commit();
SetOutputParamValues();
}
catch (OracleException ex)
{
if (tran != null) {
tran.Rollback();
}
throw;
}
finally
{
this.CloseDBConnection();
this.cmd.Parameters.Clear();
this.cmd.UpdatedRowSource = oldURS;
this.dbAdapter.InsertCommand = oldCmd;
this.dbAdapter.UpdateBatchSize = oldUBS;
}
}
The fastest way to load data into a table is datapump (the impdp utility).
Another fast way is SQL*Loader.
If you want to stick with C#, look into bulk operations.
My google karma found the following examples
I have used SQL*Loader frequently from C# to bulk load records to stage.
The meat of the code goes like this:
public string RunSqlLdr(string user, string password, string dsn, string fileNameCtl, string fileNameLog)
{
// Redirect both streams so we can write/read them.
var cmdProcessInfo = new System.Diagnostics.ProcessStartInfo("cmd.exe")
{
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false
};
// Start the process.
var process = System.Diagnostics.Process.Start(cmdProcessInfo);
// Issue the sqlldr command and exit.
process.StandardInput.WriteLine("cd " + _directoryPath);
process.StandardInput.WriteLine("sqlldr " + user + "/" + password + "#" + dsn + " control=" + fileNameCtl + " log=" + fileNameLog);
process.StandardInput.WriteLine("exit");
// Read all the output generated from it.
var output = process.StandardOutput.ReadToEnd();
process.Dispose();
return output;
}
This will return the output from the command line, but you'll also want to check the generated log file for records loaded and error counts etc.
I am inserting a lot of data into an Oracle database that is located in Australia, far away form where I am running the client application in C#.
Here's the summary of how I am doing it. It amazes me how fast I insert hundreds of thousands of records fast using array binding.
This is not the exact code, but you get the idea:
using System.Data.OleDb;
int numRecords = 2;
int[] DISTRIBNO = new int[numRecords];
DISTRIBNO[0] = 100;
DISTRIBNO[1] = 101;
string sql = "INSERT INTO Distributors (distribno) VALUES (:DISTRIBNO)";
cnn = new Oracle.DataAccess.Client.OracleConnection(conString);
cnn.Open();
using (Oracle.DataAccess.Client.OracleCommand cmd = cnn.CreateCommand())
{
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
cmd.BindByName = true;
// To use ArrayBinding, we need to set ArrayBindCount
cmd.ArrayBindCount = numRecords;
cmd.CommandTimeout = 0;
cmd.Parameters.Add(
":DISTRIBNO",
Oracle.DataAccess.Client.OracleDbType.Int32,
BR_VOLMONTH,
ParameterDirection.Input);
cmd.ExecuteNonQuery();
}//using
Carlos Merighe.
You will probably have to abandon "DataAdapter" code to get any real performance.
Here is the dotnet class that seems to be on target.
Oracle.DataAccess.Client.OracleBulkCopy
OracleBulkCopy Class
An OracleBulkCopy object efficiently bulk loads or copies data into an Oracle table from another data source.
https://docs.oracle.com/cd/E11882_01/win.112/e23174/OracleBulkCopyClass.htm#ODPNT7446
https://docs.oracle.com/cd/E85694_01/ODPNT/BulkCopyCtor3.htm
.........
This is from the (oracle owned) ODP.NET.
See
https://www.nuget.org/packages/Oracle.ManagedDataAccess/

Categories