.Net MySql User Defined Variable as Output Parameter - c#

I've been searching for a while but the answers I find usually involve stored procedures or different functionality.
I am trying to execute a reader and also return a scalar in the one query. I thought I could do this using an output parameter, but I get an exception to check my syntax near NULL = #rows_found(), so it appears the output parameter is not getting initialized.
Basically I need to know if this is possible as I haven't found a code sample like this that works.
command.CommandText = #"
SELECT SQL_CALC_FOUND_ROWS
accounting.*
FROM
accounting
WHERE
transaction_type = #transaction_type
LIMIT
#index, #results;
SET #total_rows = FOUND_ROWS();
";
command.Parameters.AddWithValue("transaction_type", transaction_type);
command.Parameters.AddWithValue("index", index);
command.Parameters.AddWithValue("results", results);
MySqlParameter totalRows = new MySqlParameter("total_rows", 0);
totalRows.Direction = System.Data.ParameterDirection.Output;
command.Parameters.Add(totalRows);
using (MySqlDataReader dr = command.ExecuteReader())
{
while (dr.Read())
invoices.Add(new AccountingDataModel(dr));
}
invoices.Total = (int)totalRows.Value;

cmd.Parameters["#output"].Value.ToString()
Use command object to access your out parameter ....
you can not access out perameter directly.
You Should use like
invoices.Total = Convert.ToInt32(command.Parameters["total_rows"].Value.ToString())
example for stored procedure
MySqlCommand cmd = new MySqlCommand(nameOfStoredRoutine, connection);
cmd.CommandType = CommandType.StoredProcedure;
//input parameters
for (int i = 0; i < (parameterValue.Length / 2); i++)
{
cmd.Parameters.AddWithValue(parameterValue[i, 0], parameterValue[i, 1]);
cmd.Parameters[parameterValue[i, 0]].Direction = ParameterDirection.Input;
parameterList = parameterList + parameterValue[i,0] + " " + parameterValue[i,1] + " ";
}
//single output parameter
cmd.Parameters.AddWithValue("#output", MySqlDbType.Int32);
cmd.Parameters["#output"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery(); //Execute command
return Convert.ToInt32(cmd.Parameters["#output"].Value.ToString());

It seems that this is not possible. From the documentation of MySqlParameter's members for the Direction property:
Gets or sets a value indicating whether the parameter is input-only, output-only, bidirectional, or a stored procedure return value parameter. As of MySQL version 4.1 and earlier, input-only is the only valid choice.
So the parameter is, no matter what you set Direction to, always an input parameter. If you change the value from null to anything else (e.g. 15) you should see that the generated query is something like
SET 15 = FOUND_ROWS()
Eventually i ended up running two queries. Probably this is not as performant as it could be, but at least it gets the desired result (using EF Core):
using (var context = new MyDbContext(...))
{
context.Database.OpenConnection();
var estates = context.MySet
.FromSql("SELECT SQL_CALC_FOUND_ROWS * FROM myset LIMIT 25 OFFSET 25")
.ToList();
var cmd = context.Database.GetDbConnection().CreateCommand();
cmd.CommandText = "SELECT FOUND_ROWS()";
var rows = (long)cmd.ExecuteScalar();
context.Database.CloseConnection();
}

Related

Trying to use SQL data to multiply with other SQL data

So I am trying to use two different inputs from a user to get two different values then multiply them together to get an answer.
//code to get value
SqlCommand cmd = new SqlCommand("select Charges, Students from Subs where Subject_name='" + Subject + "'and Level='" + Level + "'", con);
//code to read and times the values
var reader = cmd.ExecuteReader();
int Price = Convert.ToInt32(reader["Charges"]);
int NumS = Convert.ToInt32(reader["Subject_name"]);
int final = (Price*NumS) / 100;
status = final + "$";
You should try something like this:
// Define **parametrized** query
string query = "SELECT Charges, Students FROM dbo.Subs WHERE Subject_name = #SubjectName AND Level = #Level;";
using (SqlCommand cmd = new SqlCommand(query, con))
{
// define the parameters and set value
// I'm just *guessing* what datatype and what length these parameters are - please adapt as needed !
cmd.Parameters.Add("#SubjectName", SqlDbType.VarChar, 50).Value = Subject;
cmd.Parameters.Add("#Level", SqlDbType.Int).Value = Level;
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
int Price = reader.GetInt32(0); // index = 0 is "Charges" of type INT
// you are **NOT** selecting "Subject_Name" in your query - you therefore **CANNOT** read it from the SqlDataReader
// int NumS = Convert.ToInt32(reader["Subject_name"]);
int NumS = 1.0;
int final = (Price * NumS) / 100;
status = final + "$";
}
}
Points to ponder:
You should also put your SqlConnection con into a proper using (..) { ... } block to ensure disposal
You need to check the parameters - since you hadn't specified anything in your question, and also not posted any information about them, I can only guess
Be aware - the SqlDataReader might (and in a great many cases will) return multiple rows - which you need to iterate over
If you want to read out a column from the database table - it must be in the SELECT list of columns! You're trying to read out a column you're not selecting - that won't work, of course. ...

update a table by using for loop with parameters in C#

I have a table with some columns like
now I want to use a for loop to set
out_0 = 0,
out_1 = 1,
out_2 = 2,
out_3 = 3,
out_4 = 4
so I update it with such code as
string sql = "update exchange_out set #column = #id where member_id = 6;";
SqlCommand cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#column", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
int n = 0;
for (int i = 0; i < 5; i++)
{
cmd.Parameters["#column"].Value = "out_" + i;
cmd.Parameters["#gid"].Value = i;
n = cmd.ExecuteNonQuery();
MessageBox.Show("" + n);
}
but it didn't write any data into the table while it literally did five times of updating, because the messagebox returns "1" five times.
finally I solve this by
for (int i = 0; i < 5; i++){
sql = string.Format("update exchange_out set {0} = {1} where member_id = 6", "out_" + i, i);
}
but I'm still wondering why it didn't work by adding parameters?
any respond will be appreciated. :)
I'm still wondering why it didn't work by adding parameters?
Identifiers such as table and column names cannot be parameterized in this way, only data. Your attempt effectively runs a query like this:
update exchange_out set 'out_1' = 1 where member_id = 6;
It's the same in any programming language:
var data1 = "hello";
var whichData = "1";
Console.WriteLine(data+whichData); //it doesn't compile; you cannot programmatically build a variable name `data1` in this way
The way you found is reasonably the only way but you should still parameterize the data:
using var cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#data", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
for (int i = 0; i < 5; i++){
sql = string.Format("update exchange_out set out_{0} = #data where member_id = #id", i);
cmd.CommandText = sql;
cmd.Parameters["#data"].Value = ...
cmd.Parameters["#id].Value = 6;
...
You could also start with an SQL stub like "UPDATE t SET " and repeatedly concatenate on identifiers and parameters:
using var cmd = new SqlCommand(sql, connet);
cmd.Parameters.Add("#data", SqlDbType.NVarChar);
cmd.Parameters.Add("#id", SqlDbType.Int);
var sql = "UPDATE exchange_out SET ";
for (int i = 0; i < 5; i++){
sql += string.Format("out_{0} = #data{0},", i);
cmd.Parameters["#data"+i].Value = ...
}
sql = sql.TrimEnd(',');
sql += " where member_id = #id";
cmd.Parameters["#id"].Value = 6;
cmd.CommandText = sql;
...
This does the update in one operation, running a query like UPDATE t SET out_1 = #data1, out_2 = #data2 ...
These are safe from SQL injection because your code controls the entire SQL; there isn't any capacity for a user to provide '; DROP TABLE Students;-- as the {0} going into the identifier in this case but take care that you don't arrange for it to be possible (don't let the user provide identifier text)..
Your non-parameter attempt is also safe from SQL injection in this case by virtue of inserting intergers that you control, rather than strings you don't, but be careful you don't universally apply the technique and one day include user-suppied strings. If you do find yourself in that suitable you should use something like a whitelist of user input - any string identifier provided by the user that isn't whitelisted should not be put in the SQL

Read Entity objects from database using EntityDataReader

Due to some reason I need to read entity objects directly from database using ADO.Net.
I've found below snippet from Microsoft documentation. I want to know are there any methods to read whole row into an Onject ('contact' in this sample) using EntityDataReader instead of mapping every single field to every property? I mean instead of reading Contact.Id and Contact.Name and other fields one by one, are there any methods which read one row into one object or not?
using (EntityConnection conn =
new EntityConnection("name=AdventureWorksEntities"))
{
conn.Open();
string esqlQuery = #"SELECT VALUE contacts FROM
AdventureWorksEntities.Contacts AS contacts
WHERE contacts.ContactID == #id";
// Create an EntityCommand.
using (EntityCommand cmd = conn.CreateCommand())
{
cmd.CommandText = esqlQuery;
EntityParameter param = new EntityParameter();
param.ParameterName = "id";
param.Value = 3;
cmd.Parameters.Add(param);
// Execute the command.
using (EntityDataReader rdr =
cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
// The result returned by this query contains
// Address complex Types.
while (rdr.Read())
{
// Display CustomerID
Console.WriteLine("Contact ID: {0}",
rdr["ContactID"]);
// Display Address information.
DbDataRecord nestedRecord =
rdr["EmailPhoneComplexProperty"] as DbDataRecord;
Console.WriteLine("Email and Phone Info:");
for (int i = 0; i < nestedRecord.FieldCount; i++)
{
Console.WriteLine(" " + nestedRecord.GetName(i) +
": " + nestedRecord.GetValue(i));
}
}
}
}
conn.Close();
}
Your cleanest option is to use execute your query using EntityFramework as suggested by #herosuper
In your example, you'd need to do something like this:
EntityContext ctx = new EntityContext();
var contacts= ctx.Contacts
.SqlQuery("SELECT * FROM AdventureWorksEntities.Contacts AS contacts"
+ "WHERE contacts.ContactID =#id", new SqlParameter("#id", 3)).ToList();
From here, you would be able to:
var myvariable = contacts[0].ContactID;//zero is index of list. you can use foreach loop.
var mysecondvariable = contacts[0].EmailPhoneComplexProperty;
Alternatively, you might skip the whole SQL string by by doing this:
EntityContext ctx = new EntityContext();
var contact= ctx.Contacts.Where(a=> a.ContactID ==3).ToList();
I'm assuming the query returns more than one record, otherwise you would just use FirstOrDefault() instead of Where()

Error when inserting data into AS400 using C# Application

I'm using .Net framework 4.0. and I've the the source code like this:
....
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = conn;
String query = "SELECT a.process_id, a.prod_dt, a.vlt_dt, a.prod_month, a.karoseri, a.error_flag, a.created_by, date_format(a.created_dt, '%Y%m%d') as created_dt, a.updated_by, date_format(a.updated_dt, '%Y%m%d') as updated_dt FROM tb_t_vlt_web a " +
"WHERE a.process_id = '" + processId + "'";
DataTable dt = CSTDDBUtil.ExecuteQuery(query);
if (dt.Rows.Count > 0)
{
as400Con = CSTDDBUtil.GetAS400Connection();
as400Con.Open();
using (OdbcCommand as400Cmd = new OdbcCommand())
{
as400Cmd.Connection = as400Con;
as400Cmd.CommandText = "INSERT INTO DCDLIB.TBTVLT(VLPRID, VLPRDT, VLVLDT, VLPRMO, VLKRCD, VLERFG, VLCRBY, VLCRDT, VLCHBY, VLCHDT) VALUES " +
"(?,?,?,?,?,?,?,?,?,?)";
foreach (DataRow dr in dt.Rows)
{
as400Cmd.Parameters.Add("1", OdbcType.VarChar).Value = dr["process_id"].ToString();
as400Cmd.Parameters.Add("2", OdbcType.Numeric).Value = dr["prod_dt"];
as400Cmd.Parameters.Add("3", OdbcType.Numeric).Value = dr["vlt_dt"];
as400Cmd.Parameters.Add("4", OdbcType.VarChar).Value = dr["prod_month"].ToString();
as400Cmd.Parameters.Add("5", OdbcType.VarChar).Value = dr["karoseri"].ToString();
as400Cmd.Parameters.Add("6", OdbcType.VarChar).Value = dr["error_flag"].ToString();
as400Cmd.Parameters.Add("7", OdbcType.VarChar).Value = dr["created_by"].ToString();
as400Cmd.Parameters.Add("8", OdbcType.Numeric).Value = dr["created_dt"];
as400Cmd.Parameters.Add("9", OdbcType.VarChar).Value = dr["updated_by"].ToString();
as400Cmd.Parameters.Add("10", OdbcType.Numeric).Value = dr["updated_dt"];
as400Cmd.ExecuteNonQuery();
as400Cmd.Parameters.Clear();
}
as400Cmd.Dispose();
}
}
... Next Process Below ...
When I execute the program, there is an error occurred on as400Cmd.ExecuteNonQuery();. The error was:
ERROR [00000] [IBM][System i Access ODBC Driver]Column 4: CWB0111 - Input data is too big to fit into field
ERROR [22001] [IBM][System i Access ODBC Driver]Column 4: Character data right truncation.
And then, I've checked the AS400 and there is no problem with the column size.
How can I resolved this error..?
Structure table of TBTVLT
We can't tell what the actual problem is easily because you haven't given us the error message, but the first thing to do is stop doing this:
insertQuery = String.Format("INSERT INTO DCDLIB.TBTVLT(VLPRID, VLPRDT, VLVLDT, VLPRMO, VLKRCD, VLERFG, VLCRBY, VLCRDT, VLCHBY, VLCHDT) VALUES " +
"('{0}',{1},{2},'{3}','{4}','{5}','{6}',{7},'{8}',{9})",
Never build up SQL queries by inserting values into the SQL itself. Instead, you parameterized SQL, and add the parameters to the command.
This will:
Keep your SQL easier to read
Avoid SQL injection attacks
Avoid unnecessary string conversions
I wouldn't be at all surprised if the problem is due to date/time to string conversions.
See the "Using Parameters" MySql documentation for examples.

indexOutofRangeException occurred

I am getting 'indexOutofRangeException occurred' error - 'FixedActual'
this is the code i am using any help would be appropriated.
SqlDataReader dataReader = null;
SqlCommand Scmd = new SqlCommand("SalesGetRecalcOrderItemCosts", this._connection);
Scmd.CommandType = System.Data.CommandType.StoredProcedure;
Scmd.Transaction = currentTransaction;
Scmd.Parameters.AddWithValue("#OrderNumber", ItemSODBOM.SONO); //SoItem.fSnoNo
Scmd.Parameters.AddWithValue("#UTCompFCostRef", sUTCompFCostRef);//utcomp.fcostref
Scmd.Parameters.AddWithValue("#UTCompFCostEst", sUTCompFCostEst);//utcomp.fcostest
Scmd.Parameters.AddWithValue("#UTCompFCostMType", sUTCompFCostMType);//utcomp.fcostmtype
Scmd.Parameters.AddWithValue("#OrderItemNumber", finumber); //SoItem.finumber
Scmd.Parameters.AddWithValue("#OrderType", "S");//Sales Order
Scmd.Parameters.AddWithValue("#UseStandardTransitCost", "0");
Scmd.Parameters.AddWithValue("#GetExtendedCosts", "0");
dataReader = Scmd.ExecuteReader();
while (dataReader.Read())
{
using (System.Data.SqlClient.SqlCommand updateCommand = this._connection.CreateCommand())
{
string sql = #"
UPDATE SOITEM SET
FFIXACT = #FixedActual, FLABACT = #LaborActual, FMATLACT = #MaterialActual,
FOTHRACT = #OtherActual, FOVHDACT= #OverheadActual, FRTGSETUPA= #SetupActual,
FSUBACT = #SubcontractActual, FTOOLACT = #ToolActual,FSTANDPART = 0,
FTOTPTIME = #TotalPTime, FTOTSTIME = #TotalSTime, FULABCOST1 = #ULaborCost1
WHERE FSONO = #FSONO and FINUMBER = #FINUM
";
updateCommand.CommandText = sql;
updateCommand.CommandType = System.Data.CommandType.Text;
updateCommand.Transaction = currentTransaction;
updateCommand.Parameters.AddWithValue("#FixedActual", dataReader["FixedActual"]); //This is where i am getting error
updateCommand.Parameters.AddWithValue("#LaborActual", dataReader["LaborActual"]);
updateCommand.Parameters.AddWithValue("#MaterialActual", dataReader["MaterialActual"]);
updateCommand.Parameters.AddWithValue("#OtherActual", dataReader["OtherActual"]);
updateCommand.Parameters.AddWithValue("#OverheadActual", dataReader["OverheadActual"]);
updateCommand.Parameters.AddWithValue("#SetupActual", dataReader["SetupActual"]);
updateCommand.Parameters.AddWithValue("#SubcontractActual", dataReader["SubcontractActual"]);
updateCommand.Parameters.AddWithValue("#ToolActual", dataReader["ToolActual"]);
updateCommand.Parameters.AddWithValue("#TotalPTime", dataReader["TotalPTime"]);
updateCommand.Parameters.AddWithValue("#TotalSTime", dataReader["TotalSTime"]);
updateCommand.Parameters.AddWithValue("#ULaborCost1", dataReader["ULaborCost1"]);
updateCommand.Parameters.AddWithValue("#FSONO", ItemSODBOM.SONO);
updateCommand.Parameters.AddWithValue("#FINUM", finumber);
updateCommand.ExecuteNonQuery();
}
}
Well, the exception means your SqlDataReader doesn't have a FixedActual column. That's all we can really tell from what you've shown, to be honest. We don't know what your SalesGetRecalcOrderItemCosts stored procedure does, but it appears not to be returning exactly what you expect.
You might want to look at the SqlDataReader in a debugger and see what fields are available.
(As an aside, you should be using using statements for these resources - the command, reader etc - so that you dispose of everything properly. It's also not clear why you're using fully-qualified type names in some places but not others.)

Categories