basically i have a service which looks at two tables - one resides on a remote server, the other locally. I am trying to write a program which will select any required files from the remote server and copy them locally. i can get this working for standard records but how do i handle the blob in c# - i am just starting out with the language so be gentle
a snippet of what i have is below
public static void BroadcastCheck(String ip_addr)
{
OdbcConnection local = new OdbcConnection("DSN=local");
OdbcConnection cloud = new OdbcConnection("DSN=cloud");
local.Open();
cloud.Open();
OdbcCommand update1 = new OdbcCommand("UPDATE exchange set status = '1' where `status`='0' and inp_date=chg_date and LEFT(filename,12)='" + ip_addr + "' and type='UPDATE'", cloud);
update1.ExecuteNonQuery();
OdbcCommand broadcastSelect = new OdbcCommand("select * from exchange where inp_date=chg_date and LEFT(filename,12)='" + ip_addr + "' and status='1' and type='UPDATE'", cloud);
OdbcDataReader DbReader = broadcastSelect.ExecuteReader();
int fCount = DbReader.FieldCount;
byte[] outByte = new byte[500];
while (DbReader.Read())
{
String type = DbReader.GetString(0);
String filename = DbReader.GetString(1);
String data = DbReader.GetBytes(1);
OdbcCommand broadcastCopy = new OdbcCommand("INSERT INTO exchange(type,filename) VALUES('"+type+"','"+filename+"'"+data+")", local);
broadcastCopy.ExecuteNonQuery();
}
itouchcloud.Close();
itouchlocal.Close();
Console.Write("Broadcast Check Completed \n");
}
Basically the cloud db is queried and may return multiple results, i want to process each record returned and copy it to the local DB.
i have looked around and cant seem to really get a decent solution, i can do this simply in Visual FoxPro 9 so im guessing there is a similar solution.
any help appreciated :)
The first part of the answer is, avoid dynamic SQL if you can. You're using "... VALUES ('"+type+"','"+filename+"'"+data+")" when you should be using "... VALUES (?, ?, ?)".
Then, add the parameters using, for instance,
// sample: the name of the parameter (here #Type) can be anything, and the type and length should match your schema.
broadcastCommand.Parameters.Add("#Type", OleDbType.VarChar, 10).Value = type;
The question marks will be replaced by the parameters in the order you specify them, so you should add type, then filename, then data, in that order.
Now, the value you specify should ALSO correspond to the type of field you are inserting into. So instead of String, String, String, you might want your variables to be of type String, String, byte[].
There are about a million reasons not to construct your queries dynamically, so I would recommend studying up on how to use the Parameters collection on your OdbcCommand. Start here.
UPDATE
In general you can get DataReader values simply by using the indexer [], without needing to go through the GetXXX() methods. For byte arrays, that's usually simpler, because you don't need to know or try to guess the length beforehand.
You can convert your code to use indexers this way:
String type = (string)DbReader[0];
String filename = (string)DbReader[1];
byte[] data = (byte[])DbReader[2];
Note that your GetBytes() call originally had a 1 in there, but I assume you aren't trying to get the bytes of the filename field. So, if your byte[] data is in another field, use that instead. Be aware, however, that you could also use the string field names just as easily (and it might be clearer the next time you need to read the code):
String type = (string)DbReader["type"]; // replace with whatever your fields are actually called
String filename = (string)DbReader["filename"];
byte[] data = (byte[])DbReader["data"];
On the off-chance you had filename and data both using the same field because data isn't actually in the database and instead you want to take the filename and read that filesystem object in as your data for the insert query, you'll need to use a different method.
byte[] data = System.IO.File.ReadAllBytes(filename); // requires .NET 2.0+
Either way you fill your variables, insert them with a parameterized query as explained above.
Related
I have in my class "fattura" this
public string sommaFattura(String costo)
{
MySqlCommand command = new MySqlCommand();
String sumQuery = "SELECT SUM(`prezzo`) FROM `fatturetemp`";
command.CommandText = sumQuery;
command.Connection = conn.getConnection();
command.Parameters.Add("#prezzo", MySqlDbType.Int32).Value = costo;
conn.openConnection();
//Need the command for take the result
conn.closeConnection();
}
What is the command for take the result of Sum query?
I want use this command, insert in a variable. Can you correct me?
I think the command you want is
command.ExecuteScalar();
However, note, Execute Scalar is intended that the result will return a single row, single column which your query does without regard to your parameter. However your query does not specifically make sense either..
Now your query itself. You have an explicit prezzo which would imply an existing column name and not that of a parameter. It would have to a numeric field for obvious reasons to sum it.
Now you also have an expected string coming in via "costo" parameter to the function call. If your intent is to have the incoming string be representative of a known column of the table, this is not going to work for you. You would have to build the SQL command with that explicit column name, or build doing dynamic-sql, but that would really be too much.
The concern with building a SQL statement with string parameters is you would be open to SQL-injection, especially if web-based. If your incoming string is coming from a controlled source, such as you are presenting a list of columns to a user and they can only pick one such column. Or, you have buttons on a screen asking for a sum of a certain thing, and YOU control that column name you would be in a better condition, but still be cautious passing parameter to build out SQL.
Say for example your table has numeric fields of qtySold and dollarSales. You want either the sum of either of these columns and are passing one of these respective strings into the function such as
var qtySoldAnswer = sommaFattura( "qtySold" );
or
var dollarSalesAnswer = sommaFattura( "dollarSales" );
Then your function would be CLOSER to...
public string sommaFattura(String costo)
{
MySqlCommand command = new MySqlCommand();
command.CommandText = "SELECT SUM(" + costo + ") FROM fatturetemp";
command.Connection = conn.getConnection();
conn.openConnection();
var answer = command.ExecuteScalar();
conn.closeConnection();
// I would put a breakpoint in here to see the results.
// you can then format the answer such as decimal point, etc.
return answer.ToString();
}
AGAIN, ONLY if YOU have control of the column name you are trying to send in. Someone could do sql-injection and do a lot of damage. I would only do with heavily controlled consideration and explicitly validating the columns you WOULD allow, and if so, set your variable at that point. Don't allow for any more or less than the column name. No special characters, quotes, comments, sql-terminator and new statement...
But hopefully this clarifies what I THINK you are trying to accomplish.
I would like to find in my database which line has its "path" field's value equal to the string "c:\something\somethingelse\anotherthing.thing".
I found the line by browsing and copied it's content, before making an SQL request:
On C# side, my code looks like this:
EDIT: Due to #CompuChip 's comment, I edited my line for something that I hope may be better
String MyPath = "c:\\something\\somethingelse\\anotherthing.thing"
MyPath = String.Format("select * from x where path = '{0}'", MyPath);
Then I called the method to create and send the request to my database but I got an exception
I tried the request itself on my database but even if I copied the exact value it couldn't find the line:
Here's what I tried:
Select * from x where path = "c:\something\somethingelse\anotherthing.thing"
Select * from x where STRCMP(path, "c:\something\somethingelse\anotherthing.thing") = 0
Is there a good way to compare the strings correctly and find my line ?
Thank you for your time and have a nice day.
Edit 2: I also tried getting all my lines in C# then comparing them one by one.
It worked, but with 10K+ lines it's beginning to consume resources
Please, please, please don't construct your SQL queries like that.
The proper solution is using parameters, as explained here:
Why do we always prefer using parameters in SQL statements?
Your C# code would become something like this (adapted from the linked answer):
string sql = "select * from x where path = #path";
using (SqlCommand command = new SqlCommand(sql, connection))
{
var pathParam = new SqlParameter("path", SqlDbType.VarChar);
pathParam.Value = #"c:\something\somethingelse\anotherthing.thing";
command.Parameters.Add(pathParam);
var results = command.ExecuteReader();
}
or, for MySql,
string sql = "select * from x where path = ?path";
using (MySqlCommand command = new MySqlCommand(sql, connection))
{
var pathParam = new MySqlParameter("path",
#"c:\something\somethingelse\anotherthing.thing");
command.Parameters.Add(pathParam);
var results = command.ExecuteReader();
}
Assuming that you (have checked that you) actually have a matching record in the database, the problem is likely either with the quoting, or the escaping of the value.
If that is indeed the case, using parameters to retrieve the record is not only the safe thing to do, it will also make your quoting or escaping problem a non-issue.
It appears I have to do the same thing I do in C# and put two backslashes instead of one in the path, even in MySQL
Furthermore I also had to construct correctly my requests as #CompuChip hinted.
Thank you all for your help !
I'm trying to execute a prepared sql query which updates CLOB fields in an Oracle 10g database (10.2.0.1).
If I execute the following query from inside SQL Developer and supply the values for the placeholders, there is no prblem. If I however execute it through an OracleCommand (Oracle.DataAccess.dll, version 1.102.0.1 (I think), .NET Framework 3.5),
I get the error message below. Note that we are not using the default oracle client as we require bulk insertion. The given ODP version and .NET Framework version are unfortunately a hard requirement and we may not change that.
Query:
UPDATE master_table
SET description = :description,
modification_notes = :modification_notes
WHERE master_id = :master_id;
Error:
ORA-00932: inconsistent datatypes: expected - got CLOB
Further Inormation:
Parameters are assigned as follows:
var param_description = new OracleParameter(":description", OracleDbType.Clob);
param_description.Value = "Test";
I have tried the following things:
insert to_clob() into the SQL query
assign a Oracle.DataAccess.Types.OracleClob object to the parameter.
I have also found the following description, but I would really want to be able to keep the prepared query.
How to insert CLOB field in Oracle using C#
Is it possible to do this through a prepared query?
I've attached a complete example which produces the error. DESCRIPTION and MODIFICATION_NOTES are two columns of type CLOB in the database.
Input data:
connection: OracleConnection to the database
master_id: primary key to filter for
Code:
Disclaimer: I typed the following example by hand, there might be mistakes which are not in the actual code
var query = "UPDATE master_table " +
"SET description = :description " +
" modification_notes = :modification_notes " +
"WHERE master_id = :master_id";
var param_master_id = new OracleParameter(":master_id", OracleDbType.Int64);
param_master_id.Value = master_id;
var param_description = new OracleParameter(":description", OracleDbType.Clob);
param_description.Value = "Test1";
var param_master_id = new OracleParameter(":modification_notes", OracleDbType.Clob);
param_description.Value = "Test2";
IDbCommand command = new OracleCommand(query, connection);
command.parameters.Add(param_master_id);
command.parameters.Add(param_description);
command.parameters.Add(param_modification_notes);
command.ExecuteNonQuery(); // this line throws an exception
You need to set this to true if you want to bind by name. Default is bind by the order of the parameter added.
cmd.BindByName = true;
Edit: My answer below applies for typical use of Clobs where the size is greater than 32k (what they were designed for). If you know you will always be binding less than 32k bytes, or 16k characters in the usual case of unicode you can bind as Varchar2 and free yourself from having to create a temporary lob.
--
Keep in mind that a LOB in an oracle column is really a LOB Locator, a pointer to the actual data. Before you can update a CLOB column with that Lob Locator, you need to create and populate a temporary CLOB first.
In the ODP.NET samples directory in your Oracle Home there should be a LOB directory, in there it looks like samples5.cs might be a good place to start. Here is a snippet from it:
// Set the command
OracleCommand cmd = new OracleCommand(
"update multimedia_tab set story = :1 where thekey = 1");
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
// Create an OracleClob object, specifying no caching and not a NCLOB
OracleClob clob = new OracleClob(con, false, false);
// Write data to the OracleClob object, clob, which is a temporary LOB
string str = "this is a new story";
clob.Write(str.ToCharArray(), 0, str.Length);
// Bind a parameter with OracleDbType.Clob
cmd.Parameters.Add("clobdata",
OracleDbType.Clob,
clob,
ParameterDirection.Input);
try
{
// Execute command
cmd.ExecuteNonQuery();
See the accepted answer for the actual solution.
[Edit: Former suspected answer]:
After several days of testing and debugging I found the solution which was so far away from everything I considered:
Apparently, you need to bind all Clob fields first before binding anything else - even when using actual placeholders instead of using :1, :2 etc.
Changing the bind order (i.e. the order of the AddParameter calls) fixed it.
Try This :
string Query3 = " DECLARE " +
"str varchar2(32767); " +
" BEGIN " +
" str := '" + base64ImageRepresentationLogo + "'; " +
" update map_general_settings set value=str where DESC_AR='LOGO_IMG' ; END; ";
command.CommandText = Query3;
command.ExecuteNonQuery();
I have an app for pictures that can be added to sql database with some information.. I keep pictures as varbinary in sql table. I want to check whether the picture was added before. my code as follows:
byte[] img = File.ReadAllBytes(item);
//that converts the file to bytearray
string str = ByteString(img);
//ByteString is a method that converts bytearray to string (It works)
DataRow[] satirlar = das.Tables[0].Select("PicBinary=" + str);
//sql table keeps picture as varbinary in PicBinary Column
if (satirlar[0]!=null)
{
//codes
continue;
}
but I get an exception like ("column (//something unreadable) not found") over my das.Tables[0].Select command.. Any advice?
My first thought is that "something unreadable" is the string representation of the contents of the image file and Select is treating the right hand side of the expression "PicBinary=" + str as a column name since it's not quoted.
That said, I'm not certain that DataTable's Select method will allow you to do the comparison this way. You might want to look in to LINQ to DataSet (as recommended by this answer) to do a byte-by-byte comparison of the data from your file and the contents of each cell in that column.
You need to add correct Escape Sequences when passing the string variable in select.
Update:
I might have been wrong.
Try passing parameter as following?
SqlParameter parameter = new SqlParameter("#PicBinary", SqlDbType.VarBinary, buffer.Length);
parameter.Value = buffer;
sqlCommand.Parameters.Add(parameter);
where buffer is your input data for the query.
i am trying to save a xml file as a string in clob type column in oracle db from c# am not sure how to insert clob type data from c#.
code here:
public bool Insert_XMLDocument(string ReportType,object XMLDocument)
{
try
{
Database db = DatabaseFactory.CreateDatabase("XMLDOC_ConnectionString");
DbCommand dbc = db.GetStoredProcCommand("insert_XMLDOC");
dbc.CommandType = CommandType.StoredProcedure;
db.AddInParameter(dbc, "pid", DbType.Int32, 1);
db.AddInParameter(dbc, "repo_document", DbType.Object,XMLDocument);
int i = db.ExecuteNonQuery(dbc);
if (i > 0)
return true;
else
return false;
}
catch (Exception ex) {
//HandleException(ex);
return false; }
}
Error due to compilation of this : Cannot bind type System.String as Blob.
Can you show us your stored procedure and/or your db.AddInParameter() method? Without seeing more code this is more or less just a guess:
It seems like passing DbType.Object to your data layer would indicate that the data type of the parameter is supposed to be a BLOB (binary) yet you have indicated you want it to be a CLOB (character).
Let's assume your stored procedure is defined like this:
CREATE PROCEDURE insert_XMLDOC (pid IN NUMBER, repo_document IN CLOB)
If the input object XMLDocument parameter is a string, or if your db.AddInParameter() method is converting this object to a string or any sort of textual representation then your data layer could be trying to assign a string as a BLOB parameter.
instead of using database factory i used the .net oracle provider method in which we can get "OracleType.Clob" which solves the problem i just passed the xml document as string and the job was done
Although the question seems to be outdated, I want to share an example that worked for me.
My intention was to save a JSON string (with more than 32k characters) into a clob field.
This is what I did:
string JSON_string = JsonConvert.SerializeObject(SomeObject);
System.Data.OleDb.OleDbCommand myCommand = new System.Data.OleDb.OleDbCommand();
myCommand.Parameters.AddWithValue("", SomeAttribute);
myCommand.Parameters.AddWithValue("", SomeAttribute2);
myCommand.Parameters.AddWithValue("", SomeAttribute3);
myCommand.Parameters.AddWithValue("", JSON_string);
And then execute the command. I'm using our companies library to do that, so I don't have to worry about the database connection:
DataSet myDS = myUser.myLoginUser._MySpAppS.RunSQL("INSERT INTO MARS$T_BCSAVINGS (MASSNAHMEN_ID, USER_ID, AKTIV, HEBELDATEI) VALUES (?, ?, ?, ?);", myCommand.Parameters);
I'm saving the result in a DataSet only to check if the query was successful.
So basically what I did, is to handover the string to the OleDbCommand parameters list and executed the query with those parameters.