C# and MySql Procedure - c#

I have 2 tables. Main_items and Help_items.
Main_items has these columns
(main_items_id,main_items_name)
Help_items has these columns
(help_items_id,Help_items_name, main_items_id).
I wrote this Procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `thamer1`(in main_items_id_ int,
out res int)
BEGIN
declare a int;
declare b int;
select count(help_items_id)
into a from help_items
where main_items_id=main_items_id_;
if a=0 then
set b=(main_items_id_*10)+1;
set res=b;
else
select COALESCE(max(help_items_id),0)+1
into res
from help_items
where main_items_id=main_items_id_;
end if;
END
This procedure works with MySql WrokBench.
And this for c# code
private void a_KeyDown(object sender, KeyEventArgs e)
{
using (MySqlConnection mysqlcon6 = new
MySqlConnection(connectString))
{
mysqlcon6.Open();
MySqlCommand mysqlcmd6 = new MySqlCommand("thamer1", mysqlcon6);
mysqlcmd6.CommandType = CommandType.StoredProcedure;
mysqlcmd6.CommandText = "thamer1";
mysqlcmd6.Parameters.Add("#main_items_id_", MySqlDbType.Int32).Value = a.Text;
mysqlcmd6.Parameters.Add("#res", MySqlDbType.Int32).Value=HITEM.Text;
mysqlcmd6.ExecuteNonQuery();
// MessageBox.Show("saved");
// GridFill();
}
}
I select value (for main_items_id) from DataGrideView and fetch it into textbox named a.
When I press ENTER I get this Message
System.FormatException:' Input string was not in a correct format'
I hope to help me to solve this error.

Remove the portion of this line that sets the parameter value:
mysqlcmd6.Parameters.Add("#res", MySqlDbType.Int32).Value=HITEM.Text;
It looks like you expect that to bind the result of #res to the HITEM textbox, and that's not what happens. HITEM.Text is just a string, and when you assign that value to an int parameter, you're telling MySql you expect it to be able to parse that string into an int.
Instead, only create the parameter, like this:
mysqlcmd6.Parameters.Add("#res", MySqlDbType.Int32);
You also need to tell ADO.Net this is an OUTPUT parameter. Then check the parameter value after the query runs by assigning the parameter value to HITEM.Text rather than from HITEM.Text:
private void a_KeyDown(object sender, KeyEventArgs e)
{
//You can re-use the *names* of these variables, since their scopes are limited to the method
//You can also stack them to share the same scope block and reduce nesting/indentation
using (var con = new MySqlConnection(connectString))
using (var cmd = new MySqlCommand("thamer1", con))
{
cmd.CommandType = CommandType.StoredProcedure;
// mysqlcmd6.CommandText = "thamer1"; //you already did this in constructor. Don't need to do it again
cmd.Parameters.Add("#main_items_id_", MySqlDbType.Int32).Value = a.Text;
//DON'T assign to the Value, but DO make sure ADO.Net understands this is an OUTPUT parameter
cmd.Parameters.Add("#res", MySqlDbType.Int32).Direction = ParameterDirection.Output;
//wait as long as possible to call Open()
con.Open();
cmd.ExecuteNonQuery();
//Now you can assign **to** HITEM.Text, rather than from it.
HITEM.Text = cmd.Parameters["#res"].Value;
}
//End the scope as soon as possible, so the connection can be disposed faster
// MessageBox.Show("saved");
// GridFill();
}
And here it is again without all the extra comments:
private void a_KeyDown(object sender, KeyEventArgs e)
{
using (var con = new MySqlConnection(connectString))
using (var cmd = new MySqlCommand("thamer1", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#main_items_id_", MySqlDbType.Int32).Value = a.Text;
cmd.Parameters.Add("#res", MySqlDbType.Int32).Direction = ParameterDirection.Output;
con.Open();
cmd.ExecuteNonQuery();
HITEM.Text = cmd.Parameters["#res"].Value;
}
}
Even better practice would move all your SQL methods to a separate class, away from your event handlers. The event handlers should only need to call methods in the new class, like this:
public static class DB
{
private static string connectionString = "...";
public static int thamer(int main_item_id)
{
using (var con = new MySqlConnection(connectString))
using (var cmd = new MySqlCommand("thamer1", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#main_items_id_", MySqlDbType.Int32).Value = main_item_id;
cmd.Parameters.Add("#res", MySqlDbType.Int32).Direction = ParameterDirection.Output;
con.Open();
cmd.ExecuteNonQuery();
return (int)cmd.Parameters["#res"].Value;
}
}
}
private void a_KeyDown(object sender, KeyEventArgs e)
{
HITEM.Text = DB.thamer(int.Parse(a.Text)).ToString();
}

Change this
mysqlcmd6.Parameters.Add("#main_items_id_",
MySqlDbType.Int32).Value = a.Text;
mysqlcmd6.Parameters.Add("#res", MySqlDbType.Int32).Value =
HITEM.Text;
to
int value1 = 0;
int value2 = 0;
if (!Int32.Text.TryParse(a.Text) || !Int32.TryParse(HITEM.Text))
{
return;
}
mysqlcmd6.Parameters.Add("#main_items_id_", MySqlDbType.Int32).Value = value1;
mysqlcmd6.Parameters.Add("#res", MySqlDbType.Int32).Value = value2;

Related

This C# / sql query code takes a lot of time to update the table

Can anyone help improve performance? Updating the table takes a lot of time.
I am updating the serial number from datagridview to a table called dbo.json
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
string VAL1;
string VAL2;
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
if (string.IsNullOrEmpty(row.Cells[5].Value as string))
{
}
else
{
for (int i = 0; i <= DgvWhistlSorted.Rows.Count - 2; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
var cnn = ConfigurationManager.ConnectionStrings["sql"].ConnectionString;
using (var con = new SqlConnection(cnn))
{
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2";
cmd.Parameters.AddWithValue("#VAL1", VAL1);
cmd.Parameters.AddWithValue("#VAL2", VAL2);
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
MessageBox.Show("dbo.json is ready");
}
You shouldn't create the connection and command inside such a tight loop - create and open the connection and command ONCE before the loop, and in the loop, only set the parameter values and execute the query for each entry.
Something like this:
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
string VAL1;
string VAL2;
// define connection string, query text *ONCE* before the loop
string cnn = ConfigurationManager.ConnectionStrings["sql"].ConnectionString;
string updateQuery = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2;";
// create connection and command *ONCE*
using (SqlConnection con = new SqlConnection(cnn))
using (SqlCommand cmd = new SqlCommand(updateQuery, cnn))
{
// Define parameters - adapt as needed (don't know the actual datatype they have)
cmd.Parameters.Add("#VAL1", SqlDbType.VarChar, 100);
cmd.Parameters.Add("#VAL2", SqlDbType.VarChar, 100);
// open connection ONCE, for all updates
con.Open();
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
{
if (!string.IsNullOrEmpty(row.Cells[5].Value as string))
{
for (int i = 0; i <= DgvWhistlSorted.Rows.Count - 2; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
// set the values
cmd.Parameters["#VAL1"].Value = VAL1;
cmd.Parameters["#VAL2"].Value = VAL2;
// execute query
cmd.ExecuteNonQuery();
}
}
}
// close connection after all updates are done
con.Close();
}
MessageBox.Show("dbo.json is ready");
}
Create the connection ONCE...you're creating a new database connection each time through the loop! And in fact you do not need to create new command objects each time. You can reuse the command object because the parameters are the same. Just clear the params each time through the loop.
Also don't do the grid view count in the loop, set a variable for it.
string query = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2";
int counter = DgvWhistlSorted.Rows.Count - 2;
using (SqlConnection con = new SqlConnection(cnn))
{
con.Open();
using(SqlCommand cmd = new SqlCommand(cnn,query))
{
cmd.Parameters.Clear();
//Do your loop in here
for (int i = 0; i <= counter; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
cmd.Parameters.AddWithValue("#VAL1", VAL1);
cmd.Parameters.AddWithValue("#VAL2", VAL2);
cmd.ExecuteNonQuery();
}
}
}
A better idea is to do this in one command, by passing all the data in a Table-Value Parameter (TVP):
First create the table type. I don't know your data types, so I'm guessing here. Make sure to match the types to the existing table.
CREATE TYPE dbo.OrderJson (
Order int PRIMARY KEY,
RowN nvarchar(max) NOT NULL
);
Then you can pass the whole thing in one batch. You need to create a DataTable to pass as the parameter, or you can use an existing datatable.
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
var table = new DataTable {
Columns = {
{ "Order", typeof(int) },
{ "RowN", typeof(string) },
},
};
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
if (!string.IsNullOrEmpty(row.Cells[5].Value as string))
table.Rows.Add(DgvWhistlSorted.Rows[i].Cells[0].Value, DgvWhistlSorted.Rows[i].Cells[6].Value)
const string query = #"
UPDATE dbo.json
SET RowN = t.RowN
FROM dbo.json j
JOIN #tmp t ON t.order = j.[A-order];
";
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["sql"].ConnectionString))
using (var cmd = new SqlCommand(query, con))
{
cmd.Parameters.Add(new SqlParameter("#tmp", SqlDbType.Structured) { Value = table, TypeName = "dbo.OrderJson" });
con.Open();
cmd.ExecuteNonQuery();
}
MessageBox.Show("dbo.json is ready");
}
I found that the fastest way would be to save the DATAGRIDVIEW to an SQL table and continue the process with - stored procedure + update query - between two tables - now it flies ...
Thank you all

C#(loading search results in textbox)

I am trying to search the database and set the results in textboxes. I am
getting error, which says "invalid cast exception". I need your guide please.
private void btn_search_Click(object sender, EventArgs e)
{
con.Open();
string STR="select * from TICKETSALES where REFERENCE="+txtSearch.Text;
cmd = new SqlCommand(STR,con );
dr = cmd.ExecuteReader();
if(dr.Read())
{
txtTrans.Text = dr.GetInt32("TRANSACTIONNUMBER").ToString();
txtPax.Text = dr.GetString("PASSENGERNAME");
}
else
{
MessageBox.Show("Ticket Number not Found");
}
}
Modify your select statement to get required column details.
While assigning values to test box, use column index to get value from dr
Convert value to string assign it to respective textbox
Here is sample implementation
con.Open();
//Use of camelCasing. transactionNumber instead of TRANSACTIONNUMBER
string STR="select transactionNumber,passengerNumber from TICKETSALES where REFERENCE=#search";
cmd = new SqlCommand(STR,con );
cmd.Parameters.Add("#search", txtSearch.Text);
dr = cmd.ExecuteReader();
if(dr.Read())
{
txtTrans.Text = Convert.ToString(dr[0]);
txtPax.Text = Convert.ToString(dr[1]);
}
Few tips for best coding practices (Credits: #tsahi-asher)
Don't pass values inside query, use parameters in query and use Paramere.Add() function to replace parameter with its value
Don't put your sql statements in presentation layer. Have some dedicated layer of SQL.
Don't use select *, use specific column name.
Don't use all-caps for identifier names, use camelCase.
How about something like this:
Note the sql injection protection by paramtising the sql query.
private void btn_search_Click(object sender, EventArgs e)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
string query = "select top 1 TRANSACTIONNUMBER, PASSENGERNAME from ticketsales where reference=#ref";
using (SqlDataAdapter adap = new SqlDataAdapter(query, con))
{
con.Open();
DataTable dt = new DataTable();
adap.SelectCommand.Parameters.AddWithValue("#ref", txtSearch.Text.Trim());
adap.Fill(dt);
if (dt.Rows.Count > 0)
{
txtTrans.Text = dt.Rows[0]["TRANSACTIONNUMBER"].ToString().Trim();
txtPax.Text = dt.Rows[0]["PASSENGERNAME"].ToString().Trim();
}
else
{
MessageBox.Show("Ticket Number not Found");
}
}
}
}
There are few issue already mentioned in comments and posts. I will chip in my remarks - you don't dispose of unmanaged resources, one answer covers that, but it violates your code. So here is alternative solution:
SqlConnection con;
SqlCommand cmd;
SqlDataReader dr;
//some methods, fields
private void btn_search_Click(object sender, EventArgs e)
{
con.Open();
// as it has benn already said, you have to prevent yourself from SQL injection!
cmd = (new SqlCommand("select * from TICKETSALES where REFERENCE=#ref", con)).Parameters.AddWithValue("#res", txtSearch.Text.Trim());
dr = cmd.ExecuteReader();
if (dr.Read())
{
txtTrans.Text = dr.GetInt32("TRANSACTIONNUMBER").ToString();
txtPax.Text = dr.GetString("PASSENGERNAME");
}
else
{
MessageBox.Show("Ticket Number not Found");
}
}
// it looks like you have unamanaged resources held by fields in your form,
// so to release them you have to call their Dispose() method!
// normally you should use using keyword if they were used locally in a method, as other answer states
public void Dispose()
{
base.Dispose();
if (con != null) con.Dispose();
if (cmd != null) cmd.Dispose();
if (dr != null) dr.Dispose();
}

Fill List box with result from stored procedure C#

I have search a bunch for this and as you can tell I'm very new to C#, I I'm trying to learn this language.
So please forgive me if one of the 100 forms that I have looked at says the same thing that you post here.
I am trying to populate a list box with a the results from a stored procedure called LookupInvoices, this requires a parameter #InvoiceNumber that is an int.
I have a WPF that I would like to populate a listbox called lstResults with the results from this stored procedure.
Please look at my code and tell me where I have made an error, thanks so much for your time!
private void btnFill_Click(object sender, RoutedEventArgs e)
{
if (cmbInvoiceNumbers.SelectedItem == null)
{
System.Windows.MessageBox.Show("Please Choose A Invoice Number!");
return;
}
SqlConnection cs = new SqlConnection("Data Source=JAMES-DESKTOP\\SQLEXPRESS;Initial Catalog=contacts;Integrated Security=True");
int cboItemNumber = cmbInvoiceNumbers.SelectedIndex;
int invNumber = Convert.ToInt32(cmbInvoiceNumbers.Items[cboItemNumber].ToString());
cs.Open();
SqlCommand cmd = new SqlCommand("LookupInvoices", cs);
using (SqlDataReader reader = cmd.ExecuteReader())
{
SqlParameter pram = new SqlParameter("#invoiceNumber", invNumber);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(pram);
cmd.Connection = cs;
reader.Read();
}
cs.Close();
}
Please not that when I click the button it crashes. This doesn't run all the way through.
You should put your all disposable elements (SqlConnection, SqlCommand, SqlDataReader) into using (....) { ...... } blocks, and you need to add the parameter before calling .ExecuteReader() - try this code:
public class InvoiceDto
{
public string Description { get; set; }
public int Hours { get; set; }
public decimal Amount { get; set; }
}
public void List<InvoiceDto> GetInvoiceData(int invoiceNumber)
{
List<InvoiceDto> data = new List<InvoiceData>();
using (SqlConnection cs = new SqlConnection("Data Source=JAMES-DESKTOP\\SQLEXPRESS;Initial Catalog=contacts;Integrated Security=True"))
using (SqlCommand cmd = new SqlCommand("dbo.LookupInvoices", cs))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#invoiceNumber", SqlDbType.Int).Value = invoiceNumber;
cs.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
InvoiceDto dto = new InvoiceDto();
dto.Description = reader.GetFieldValue<string>(0);
dto.Hours = reader.GetFieldValue<int>(1);
dto.Amount = reader.GetFieldValue<decimal>(2);
data.Add(dto);
}
}
cs.Close();
}
return data;
}
and in your code-behind, you should call this method in your event handler
private void btnFill_Click(object sender, RoutedEventArgs e)
{
// get selected invoice number
int cboItemNumber = cmbInvoiceNumbers.SelectedIndex;
int invNumber = Convert.ToInt32(cmbInvoiceNumbers.Items[cboItemNumber].ToString());
// call method to get the data from the stored procedure
List<InvoiceDto> data = GetInvoiceData(invNumber);
// loop over the data and fill your listbox
foreach(InvoiceDto dto in data)
{
listbox.Items.Add(dto.Description);
}
}

C# winform- there is no error code, but i cannot insert items into Mssql DB

I'm making some book management system using WinForm in C#.
i think there is no error insert query(i cant find what's problem)..
but i keep doing process, there is no data in DB. :(
i maked insertForm.cs and database.cs. please help me..
this is database.cs below:
public void GetBookInsert(BookEntity be)
{
ConnectDB();
try
{
cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "INSERT INTO dbo.book (bookName,author,company,price,bookImage,classId,bookPlace)"
+ " VALUES (#bookName,#author,#company,#price,#bookImage,#classId,#bookPlace)";
cmd.Parameters.Add(new SqlParameter("#bookName", SqlDbType.NVarChar, -1,be.BookName));
cmd.Parameters.Add("#author", SqlDbType.NVarChar, -1,be.Author);
cmd.Parameters.Add("#company", SqlDbType.NVarChar, -1,be.Company);
cmd.Parameters.Add("#price", SqlDbType.Int,be.Price);
cmd.Parameters.Add("#bookImage", SqlDbType.NVarChar, -1,be.bookImage);
cmd.Parameters.Add("#classId", SqlDbType.NVarChar, -1,be.ClassId);
// cmd.Parameters.Add(new SqlParameter("#bookDate", System.Data.SqlDbType.Date));
//cmd.Parameters.Add(new SqlParameter("#buyDate", System.Data.SqlDbType.Date));
cmd.Parameters.Add("#bookPlace", SqlDbType.NVarChar, -1,be.bookPlace);
cmd.ExecuteNonQuery();
}
catch (SqlException e) {
string msg = "Insert error";
msg += e.Message;
}
finally
{
CloseDB();
}
and insertForm.cs part code is:
private void buConfirm_Click(object sender, EventArgs e)
{
Con_Database bookDB = new Con_Database();
BookEntity books = new BookEntity();
books.BookName = textName.Text;
books.Author = textAuthor.Text;
books.Company = textCompany.Text;
books.Price = Int32.Parse(textPrice.Text);
books.bookImage = textImage.Text;
books.ClassId = comboClass.Text;
// books.BookDate = bookDate.Text;
// books.BuyDate = buyDate.Text;
bookDB.GetBookInsert(books);
}
}
I think you are using SqlParameterCollection.Add Method (String, SqlDbType, Int32, String) overload in a wrong way.
The last parameter doesn't take the value of your parameter, it takes the source column (SourceColumn) if this SqlParameter is used in a call to Update. Because of that, your parameters doesn't have any values. And using -1 as a column length is meaningless.
Just use them like;
cmd.Parameters.Add("#author", SqlDbType.NVarChar).Value = be.Author;
Also use using statement to dispose your database connections and objects.

Checking SQL Table Value / Using return value as parameter

I am a little new to using ASP.NET with SQL and I am having trouble with this. The function CheckStatus() basically calls a stored procedure to check a table column value which is only going to be 1 or 0. The data type in the table is a bit so my idea was to convert it into a string and do a check from there. After that, it returns 1 or 0.
The second function ChangeFileStatus() is supposed to change the value to either 1 or 0 depending on what it is.
However, my question is can I use my return value as a parameter in another function? I wanted to make an if condition checking for the return value. Please help.
private void ChangeFileStatus()
{
CheckStatus(); // i wanna call this here but use return value as the paramter
using (SqlConnection con = new SqlConnection(CS))
{
cmd = new SqlCommand("spEcovaFilesChangeJobStats", con); //call your stored procedure within the ""
cmd.CommandType = CommandType.StoredProcedure; // this is saying that the command type is a stored procedure
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
private int CheckStatus()
{
int status = 0;
using (SqlConnection con = new SqlConnection(CS))
{
cmd = new SqlCommand("spEcovaGetFilesJobStats", con); //call your stored procedure within the ""
cmd.CommandType = CommandType.StoredProcedure; // this is saying that the command type is a stored procedure
con.Open();
cmd.ExecuteNonQuery();
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
while (rdr.Read())
{
string active = rdr["IsActive"].ToString();
if (active == "1" )
{
status = 0 ;
}
else
{
status = 1;
}
}
}
con.Close();
return status;
}
So a couple of things that I think you might want to reconsider with your application. You said your table stores a bit, Why not use the C# equivalent of bool? no conversion needed. Also about your call to your stored procedure, are you ever expecting more than 1 value? if not why not do something as follows for check status, This provides the user a clear picture versus a magic number that you have to figure out/remember
private bool CheckStatus()
{
bool wasSuccessful = false;
using (SqlConnection con = new SqlConnection(CS))
{
cmd = new SqlCommand("spEcovaGetFilesJobStats", con);
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
wasSuccessful = (bool)cmd.ExecuteScalar();
con.Close();
}
return status;
}
And if you wanted this to roll into the change status you would only have to change the signature of ChangeFileStatus like learningNew
mentioned. But if your status can only ever be a bit/boolean do you really need to pass it?
Change the method
private void ChangeFileStatus() {}
to
private void ChangeFileStatus(int Status)
{
//check staus value here
}
and you can call the method like
int Status=CheckStatus();
ChangeFileStatus(Status);
or
ChangeFileStatus(CheckStatus());

Categories

Resources