Background: I am rewriting my ASP.NET application - currently it uses MS SQL database but I need to support also MySQL (both in the same time!).
Most of work is done - I used MySQL Connector/Net for work with MySQL database from C# (Visual Studio 2010) - but I have troubles with certain SQL queries. This is SQL query I need to execute:
"SELECT #username=username FROM t_Users WHERE id=#id"
Here is my code:
public static int ExecuteSqlNonQuery(string i_szQuery)
{
bool bConnectionWasOpen = false;
AbstractConnection dbConnection = CMSSQLInstanceCreator.CreateConnection();
if(dbConnection.IsMySQL())
{
MySqlConnection aSqlConnection = dbConnection.GetMysConnection();
try
{
if (aSqlConnection.State == System.Data.ConnectionState.Closed)
aSqlConnection.Open();
else
if (aSqlConnection.State == System.Data.ConnectionState.Open)
bConnectionWasOpen = true;
else
throw new ApplicationException("Connection not available!");
List<MySqlParameter> aParameters1 = new List<MySqlParameter>();
aParameters1.Add(new MySqlParameter("#username", MySqlDbType.VarString, 128));
aParameters1.Add(new MySqlParameter("#id", MySqlDbType.Int32));
aParameters1[0].Direction = System.Data.ParameterDirection.Output;
aParameters1[1].Direction = System.Data.ParameterDirection.Input;
aParameters1[1].Value = (int)1;
MySqlCommand aSqlCommand = new MySqlCommand(i_szQuery, aSqlConnection);
aSqlCommand.CommandType = System.Data.CommandType.Text;
aSqlCommand.CommandTimeout = 0;
aSqlCommand.Parameters.Clear();
aSqlCommand.Parameters.AddRange(aParameters1.ToArray());
int iResult = aSqlCommand.ExecuteNonQuery();
if(iResult <= 0)
Debug.WriteLine("aResult <= 0: " + i_szQuery);
return iResult;
}
catch (Exception ex)
{
throw new ApplicationException("Cannot execute query!\nReason: '" + ex.Message + "'", ex);
}
finally
{
if (!bConnectionWasOpen && aSqlConnection.State == System.Data.ConnectionState.Open)
aSqlConnection.Close();
}
}
else
{
//... almost the same for MS SQL
}
}
The problem is that the Output parameter #username (System.Data.ParameterDirection.Output;) is not filled with the value from query.
If I use other query - e.g.
UPDATE t_Users SET password=#new_password WHERE (password=#old_password AND id=#user)
Everything is fine.
I cannot return scalar as there are many other queries like
SELECT #username=username, #is_blocked=is_blocked, #full_name=full_name, #must_change_pwd=must_change_pwd ...
returning many values and I do not want to rewrite most of code.
It looks like there is problem only in MySQL because the same code works with MS SQL fine.
What should I use to force MySqlParameter to load the value from query?
You should be able to read the value of the output like this
aSqlCommand.Parameters["#username"].Value
It apparently is a shortcoming in the MySql .Net connector, see #75267. Tested it with connector v6.9.7 and MySql server 5.5.45. It is still broken and the bug report - of december 2014 - is still open. Output parameters work for stored procedures, with a command type StoredProcedure, and do not work for a SELECT statement that assigns output parameters as part of the select; command type Text.
2023-02-16 Update. A couple of weeks ago it is fixed in version 8.0.32. I tested it by updating Connector/NET to 8.0.32, and by using NuGet package MySql.Data 8.0.32.
A command of type text, an output parameter and an ExecuteNonQuery now works for the following case:
SET #valNum:=(SELECT ValueNumber FROM `sky.application` WHERE Name='Agents')
Value #valNum can then be retrieved through the output parameter.
Note that retrieving two values does not work as follows, despite it does the job in the Workbench:
SELECT #valNum:=ValueNumber, #instNum:=InstanceNumber FROM `sky.application` WHERE Name='Agents'
Error: Parameter 'valNum:=ValueNumber' not found in the collection.
Same error if retrieving one value.
Fortunately the following syntax works:
SELECT ValueNumber, InstanceNumber INTO #valNum, #instNum FROM `sky.application` WHERE Name='Agents'
Related
I have a simple select query in a verbatim string in C#/.net core 2.1 that looks somewhat like this
public const string QUERY_HTML = #"SELECT col1, col2 from table1 JOIN table2 ON table1.id = table2.id WHERE data LIKE '%<span class=''myclass-ident''%' ORDER BY col1";
EDIT: Per commenters suggestion, I ran the profiler and the the query in the profiler is the same as the one in the code and the text visualizer in debug. In other words, the genericized query above does seem to be the one that gets run, at least that's what the profiler says.
The actual query isn't important - I genericized the column names, but its the same otherwise. If I run it in SQL Server Management Studio, it works just fine.
But when my code runs the query, it clobbers the database. Meaning the database is completely barebones. All users, tables, stored procedures and data (obviously if there are no tables) are gone.
If I run the query without the # sign it works just fine.
I couldn't believe it at first - I thought there must be something else going on. I restored my local database and ran it again with the # sign and it again reset the database to a bare/skeleton state. I also tried it on a different machine.
Obviously, I know to not use the # sign in front of the query. I originally had it in there to escape some characters that have since been removed, so I no longer need the # but it still leaves the question as to why would the # cause the database to be clobbered?
SQL Server version is 2012 (v11.0.7001.0).
If it helps, here is the code that runs the query
using (var conn = new SqlConnection(Constants.LOCAL))
{
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = Constants.QUERY_HTML;
conn.Open();
DataTable dt = new DataTable();
try
{
using (var reader = cmd.ExecuteReader())
{
dt.Load(reader);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
if (dt.Rows.Count > 0)
{
result.payload = dt;
result.hasPayload = true;
result.code = 200;
}
return result;
}
}
Currently I'm working on cleaning up some code on the backend of an application I'm contracted for maintenance to. I ran across a method where a call is being made to the DB via Oracle Data Reader. After examining the SQL, I realized it was not necessary to make the call to open up Oracle Data Reader seeing how the object being loaded up was already within the Context of our Entity Framework. I changed the code to follow use of the Entity Model instead. Below are the changes I made.
Original code
var POCs = new List<TBLPOC>();
Context.Database.Connection.Open();
var cmd = (OracleCommand)Context.Database.Connection.CreateCommand();
OracleDataReader reader;
var SQL = string.Empty;
if (IsAssociate == 0)
SQL = #"SELECT tblPOC.cntPOC,INITCAP(strLastName),INITCAP(strFirstName)
FROM tblPOC,tblParcelToPOC
WHERE tblParcelToPOC.cntPOC = tblPOC.cntPOC AND
tblParcelToPOC.cntAsOf = 0 AND
tblParcelToPOC.cntParcel = " + cntParcel + " ORDER BY INITCAP(strLastName)";
else
SQL = #"SELECT cntPOC,INITCAP(strLastName),INITCAP(strFirstName)
FROM tblPOC
WHERE tblPOC.cntPOC NOT IN ( SELECT cntPOC
FROM tblParcelToPOC
WHERE cntParcel = " + cntParcel + #"
AND cntAsOf = 0 )
AND tblPOC.ysnActive = 1 ORDER BY INITCAP(strLastName)";
cmd.CommandText = SQL;
cmd.CommandType = CommandType.Text;
using (reader = cmd.ExecuteReader())
{
while (reader.Read())
{
POCs.Add(new TBLPOC { CNTPOC = (decimal)reader[0],
STRLASTNAME = reader[1].ToString(),
STRFIRSTNAME = reader[2].ToString() });
}
}
Context.Database.Connection.Close();
return POCs;
Replacement code
var sql = string.Empty;
if (IsAssociate == 0)
sql = string.Format(#"SELECT tblPOC.cntPOC,INITCAP(strLastName),INITCAP(strFirstName)
FROM tblPOC,tblParcelToPOC
WHERE tblParcelToPOC.cntPOC = tblPOC.cntPOC
AND tblParcelToPOC.cntAsOf = 0
AND tblParcelToPOC.cntParcel = {0}
ORDER BY INITCAP(strLastName)",
cntParcel);
else
sql = string.Format(#"SELECT cntPOC,INITCAP(strLastName), INITCAP(strFirstName)
FROM tblPOC
WHERE tblPOC.cntPOC NOT IN (SELECT cntPOC
FROM tblParcelToPOC
WHERE cntParcel = {0}
AND cntAsOf = 0)
AND tblPOC.ysnActive = 1
ORDER BY INITCAP(strLastName)",
cntParcel);
return Context.Database.SqlQuery<TBLPOC>(sql, "0").ToList<TBLPOC>();
The issue I'm having right now is when the replacement code is executed, I get the following error:
The data reader is incompatible with the specified 'TBLPOC'. A member of the type 'CNTPOCORGANIZATION', does not have a corresponding column in the data reader with the same name.
The field cntPOCOrganization does exist within tblPOC, as well as within the TBLPOC Entity. cntPOCOrganization is a nullable decimal (don't ask why decimal, I myself don't get why the previous contractors used decimals versus ints for identifiers...). However, in the past code and the newer code, there is no need to fill that field. I'm confused on why it is errors out on that particular field.
If anyone has any insight, I would truly appreciate it. Thanks.
EDIT: So after thinking on it a bit more and doing some research, I think I know what the issue is. In the Entity Model for TBLPOC, the cntPOCOrganization field is null, however, there is an object tied to this Entity Model called TBLPOCORGANIZATION. I'm pondering if it's trying to fill it. It too has cntPOCOrganization within itself and I'm guessing that maybe it is trying to fill itself and is what is causing the issue.
That maybe possibly why the previous contractor wrote the Oracle Command versus run it through the Entity Framework. I'm going to revert back for time being (on a deadline and really don't want to play too long with it). Thanks!
This error is issued when your EF entity model does not match the query result. If you post your entity model you are trying to fetch this in, the SQL can be fixed. In general you need to use:
sql = string.Format(#"SELECT tblPOC.cntPOC AS <your_EF_model_property_name_here>,INITCAP(strLastName) AS <your_EF_model_property_name_here>,INITCAP(strFirstName) AS <your_EF_model_property_name_here>
FROM tblPOC,tblParcelToPOC
WHERE tblParcelToPOC.cntPOC = tblPOC.cntPOC
AND tblParcelToPOC.cntAsOf = 0
AND tblParcelToPOC.cntParcel = {0}
ORDER BY INITCAP(strLastName)",
cntParcel);
We have an old application that runs against an Oracle database and makes use of DataSets, DataTables and TableAdapters. It uses the now deprecated System.Data.OracleClient and we are looking to replace this dll with those from DevArt.
I used the Oracle Migration Wizard to migrate from System.Data.OracleClient to Devart.Data.Oracle. After the migration, all queries continue to work just fine, except one type: MERGE queries. They still merge records just fine (the expected records are inserted/updated). However, previously the number of records merged was returned, but now it always returns "-1" instead.
Any idea what the issue could be? Preferable without having to manually update all merge queries (there are a lot of them), but if updating all queries is the only way to solve this that is acceptable.
The code that handles the connecting to the database is generated by the DataSet file:
Devart.Data.Oracle.OracleCommand command = "MERGE INTO [...]";
System.Data.ConnectionState previousConnectionState = command.Connection.State;
if ((command.Connection.State & System.Data.ConnectionState.Open) != System.Data.ConnectionState.Open)
{
command.Connection.Open();
int returnValue;
try
{
returnValue = command.ExecuteNonQuery();
}
finally
{
if (previousConnectionState != System.Data.ConnectionState.Closed)
{
command.Connection.Close();
}
}
}
return returnValue;
I found an article about migration from oracle client to devart. He basically says "this is how devart works". You can find details below:
Devart can return the number of affected rows only for INSERT, UPDATE
and DELETE DML commands, in all the other cases it returns -1. We used
these return values sometimes in our api to indicate whether the
operation was successful or not. With these 3 DML commands it is ok.
We have several usages of oracle’s MERGE command too, OracleClient can
return the correct number of affected rows, devart returns -1. We
changed our API where MERGE is used to not count on the return value.
to read full article, click here
In ODP.NET ExecuteNonQuery for Oracle statement merge also returns -1.
The only workaround I know is to call it in PLSQL block and use SQL%ROWCOUNT, like here:
string sql =
"begin " +
" merge into test t1 " +
" using (select col1, col2 from test) t2 on (t2.col1=t1.col1) " +
" when matched then update set col2=col2+1; " +
" :num := sql%rowcount; "+
"end;";
OracleCommand cmd = new OracleCommand(sql, myConnection);
var p = cmd.Parameters.Add(":num", OracleDbType.Int32, ParameterDirection.Output);
cmd.ExecuteNonQuery();
Console.WriteLine("Rows affected: " + p.Value);
--------------------------
Sample output for my data:
Rows affected: 2
I hope you'll get/find better answers for Devart.
The first MessageBox.Show() below simply shows me the exact same thing as const string SQL_GET_VENDOR_ITEMS, which seems fine to me, but I'm getting, "There was an error parsing the query. [Token line number, Token line offset,, Token in error,,]"
Is there a way to spy on the contents of the SQL after parameters have been added; it should then be something like: "SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = 'TEST' AND VendorItemID = '852963'
Here's the pertinent code:
const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize " +
"FROM VendorItems " +
"WHERE VendorID = #VendorID AND VendorItemID = #VendorItemID";
string retVal = string.Empty;
checkConnection();
SqlCeCommand vendorCMD = objCon.CreateCommand();
try
{
vendorCMD.CommandText = SQL_GET_VENDOR_ITEMS;
vendorCMD.Parameters.Add("#VendorID", SqlDbType.NVarChar, 10).Value = VendorID;
vendorCMD.Parameters.Add("#VendorItemID", SqlDbType.NVarChar, 19).Value = VendorItemID;
MessageBox.Show(string.Format("Made it up to vendorCMD.ExecuteReader() with sql {0}", vendorCMD.CommandText));
. . .
vendorReader.Close();
}
catch (SqlCeException sqlceex)
{
MessageBox.Show(string.Format("SqlCeException in GetValsForVendorAndItem == {0}", sqlceex.Message));//TODO: Remove
}
finally
{
vendorCMD.Dispose();
}
return retVal;
. . .
but I can almost guarantee that won't work in my VS2003/.NET 1.0 world
ahhh... version - see MSDN:
The .NET Compact Framework data provider for SQL Server CE does not support named parameters for passing parameters to an SQL statement called by a SqlCeCommand when CommandType is set to Text. You must use the question mark (?) placeholder. For example: SELECT * FROM Customers WHERE CustomerID = ?
Since you are on CE, your options are limited, but there are some suggestions for how to peek into the database: Profiler for Sql CE
If you were on normal SQL Server, you might consider using SQL Profiler. You'd be able to see what is getting executed against the database.
I have a .NET Wndows application in C#. It's a simple Windows application that is using the MySql 5.1 database community edition. I've downloaded the MySql ODBC driver and have created a dsn to my database on my local machine. On my application, I can perform get type queries without problems, but when I execute a given insert statement (not that I've tried doing any others), I get the following error:
{"ERROR [HY001] [MySQL][ODBC 5.1 Driver][mysqld-5.0.27-community-nt]Memory allocation error"}
I'm running on a Windows XP machine. My machine has 1 GB of memory.
Anyone have any ideas? See code below
OdbcConnection MyConn = DBConnection.getDBConnection();
int result = -1;
try
{
MyConn.Open();
OdbcCommand myCmd = new OdbcCommand();
myCmd.Connection = MyConn;
myCmd.CommandType = CommandType.Text;
OdbcParameter userName = new OdbcParameter("#UserName", u.UserName);
OdbcParameter password = new OdbcParameter("#Password", u.Password);
OdbcParameter firstName = new OdbcParameter("#FirstName", u.FirstName);
OdbcParameter LastName = new OdbcParameter("#LastName", u.LastName);
OdbcParameter sex = new OdbcParameter("#sex", u.Sex);
myCmd.Parameters.Add(userName);
myCmd.Parameters.Add(password);
myCmd.Parameters.Add(firstName);
myCmd.Parameters.Add(LastName);
myCmd.Parameters.Add(sex);
myCmd.CommandText = mySqlQueries.insertChatUser;
result = myCmd.ExecuteNonQuery();
}
catch (Exception e)
{
//{"ERROR [HY001] [MySQL][ODBC 5.1 Driver][mysqld-5.0.27-community-nt]Memory
// allocation error"} EXCEPTION ALWAYS THROWN HERE
}
finally
{
try
{
if (MyConn != null) MyConn.Close();
}
finally { }
}
It was because some fields accept null, I had passed them as null where they should be passed as DBNull.Value. For all the fields which allow null should be checked for null and if found null, DBNull.Value should be passed.
Just for the sake of completeness, Chinjoo's SQL statement would likely be something like this:
mySqlQueries.insertChatUser = "insert into ChatUsers (UserName, Password, FirstName, LastName, sex) values (?,?,?,?,?);";
This is known as a parameterized insert where each question mark represents one of his parameters. In this simple example the order of the parameters in the parameter collection in code must match the order of the column names in the SQL statement.
While less elegant than using a function, the fix for his null problem would look something like this for one of his parameters:
OdbcParameter LastName = new OdbcParameter("#LastName", u.LastName);
is replaced with
// if the value is "null" return DBNull, else just the value
OdbcParameter LastName = new OdbcParameter("#LastName",
(u.LastName == null) ? System.DBNull.Value : (object)u.LastName);
At least in my code (which is slightly different) the inner cast to type object is required since otherwise the compiler isn't sure what type the ?: operator should return.
Hope this helps anyone who is relatively new to parameterization, etc.
No criticism of Chinjoo implied at all--his posting helped me out! Just thought I'd share for the less-experienced. I'm by no means expert so take everything I say with a grain of salt.
This exception also can be raised if you try to insert invalid chars in a VARCHAR field. In example, if the string is generated by a UNIX machine and has end of line characters '\n'. You can just replace that problematic characters to Windows style, or viceversa, or just delete it if you don't want to store end of lines.
You can check the strings, and if any of them has end of line characters, try to repeat the insert deleting them. If it works, the problem are these characters.