What is a trick procedure does? - c#

I need to update the value in MySQL DB, so in order to do it I use procedure.
There is my DB
CREATE TABLE `sessions` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`manager_name` VARCHAR(1024) NOT NULL COLLATE 'utf8_general_ci',
`created_date` DATETIME NOT NULL,
`num_works` INT(10) NOT NULL DEFAULT '0',
`num_jobs` INT(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=306
;
There is my procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `session_update_works_num`(
IN `id` INT,
IN `num_works` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'use this procedure to update works num'
BEGIN
UPDATE `gmdb`.`sessions` SET `num_works`=num_works WHERE `id`=id;
END
There is how I use it in my C# code
protected override void Execution()
{
using (MySqlConnection conn = DBConnection.Instance.Connection)
{
try
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.CommandText = PROCEDURE_UPDATE_WORKS_NUM;
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue($"#{Constants.SESSION_TABLE_ID}", SessionSingleton.Instance.SessionId);
cmd.Parameters.AddWithValue($"#{Constants.SESSION_TABLE_NUM_WORKS}", m_worksNum);
cmd.ExecuteNonQuery();
SetTaskStatus(State.COMPLETED);
};
}
catch (Exception e)
{
ErrorMsg += $"ERROR: while execute procedure : {PROCEDURE_UPDATE_WORKS_NUM}, error: {e.ToString()}";
SetTaskStatus(State.FAILED);
}
}
}
The problem is that if for example, I have a few lines in my DB
After the procedure will be executed num_works field will be changed for all the lines.
But if I change the code and use a query instead of a procedure all works fine:
protected override void Execution()
{
using (MySqlConnection conn = TV_DP_DBConnection.Instance.Connection)
{
try
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand())
{
string query = $"UPDATE `gmdb`.`sessions` SET `num_works`={m_worksNum} WHERE `id`={SessionSingleton.Instance.SessionId};";
cmd.CommandText = query;
cmd.Connection = conn;
cmd.ExecuteNonQuery();
SetTaskStatus(State.COMPLETED);
};
}
catch (Exception e)
{
ErrorMsg += $"ERROR: while execute procedure : {PROCEDURE_UPDATE_WORKS_NUM}, error: {e.ToString()}";
SetTaskStatus(State.FAILED);
}
}
}
What is the problem here? How is it possible that I pass to procedure exact id WHERE 'id'=id but all the lines have an effect?

I think CrowCoder is right, variable names in stored procedure are the same as column names. Change variable names both in SP and code, and try again.

Related

Meru executenonquery doesn't work, it's not giving the update when I try to run the code

I'm trying to update some parameters, but my executenonquery n is giving the update. It even returns a changed row. But in my database it is the same thing.
I've already tested my Stored Procedure directly from the database and it works.
My code:
public virtual void Atualiza_Status(EmpresasDB empresas)
{
int i = 0;
using (MySqlConnection connection = new MySqlConnection("Server=test; AllowLoadLocalInfile=true; DataBase=test; Uid=test;Pwd=test"))
{
MySqlCommand command = new MySqlCommand("SP_AtualizaStatusEmpresas", connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("Pcnpj", empresas.cnpj);
command.Parameters.AddWithValue("Pstatus", empresas.status);
command.Parameters.AddWithValue("Pmotivo", empresas.motivo);
try
{
connection.Open();
command.ExecuteNonQuery();
}
catch
{
throw;
}
//connection.Close();
}
return;
}
My SP:
CREATE DEFINER=`sql10511870`#`%` PROCEDURE `SP_AtualizaStatusEmpresas`(Pcnpj varchar(100), Pstatus varchar(20), Pmotivo longtext)
BEGIN
START TRANSACTION;
BEGIN
IF (Pcnpj != "")
THEN
UPDATE `Empresas` SET `status` = Pstatus, `motivo` = Pmotivo WHERE `cnpj` = Pcnpj;
END IF;
END;
END

What is the best way to account for duplicate entries in datagridview with C# and SQL?

This windows application displays student information in a datagridview. When the table is double clicked, another form appears for editing the data.
When duplicates for StudentName exist, when doubleclicking the latest entry, only the earliest entry for StudentName appears. Also, when editing any data for a particular student, all duplicated entries get edited as well!
I was trying modify the code so that it would load the data based on StudentId instead of StudentName. An alternative idea was to implement a change in the code so that the user cannot enter duplicate entries.
What would be the best approach to solving this?
ViewStudentForm.cs
private void StudentDataGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
if(StudentDataGridView.Rows.Count > 0)
{
string studentName = StudentDataGridView.SelectedRows[0].Cells[1].Value.ToString();
StudentForm studentForm = new StudentForm();
studentForm.StudentName = studentName;
studentForm.IsUpdate = true;
studentForm.ShowDialog();
LoadDataIntoDataGridView();
}
}
private void LoadDataIntoDataGridView()
{
using (SqlConnection con = new SqlConnection(AppConnection.GetConnectionString()))
{
using (SqlCommand cmd = new SqlCommand("usp_Students_LoadDataIntoDataGridView", con))
{
cmd.CommandType = CommandType.StoredProcedure;
if (con.State != ConnectionState.Open)
con.Open();
DataTable dtStudents = new DataTable();
SqlDataReader sdr = cmd.ExecuteReader();
dtStudents.Load(sdr);
StudentDataGridView.DataSource = dtStudents;
}
}
}
StudentForm.cs
public string StudentName { get; set; }
public bool IsUpdate { get; set; }
private void SaveButton_Click(object sender, EventArgs e)
{
//Do Insert Process
using (SqlConnection con = new SqlConnection(AppConnection.GetConnectionString()))
{
using (SqlCommand cmd = new SqlCommand("usp_Students_InsertNewStudent", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#StudentName", StudentNameTextBox.Text.Trim());
cmd.Parameters.AddWithValue("#Age", AgeTextBox.Text.Trim());
cmd.Parameters.AddWithValue("#Gender", GenderTextBox.Text.Trim());
cmd.Parameters.AddWithValue("#Description", DescriptionTextBox.Text.Trim());
if (con.State != ConnectionState.Open)
con.Open();
cmd.ExecuteNonQuery();
MessageBox.Show("Student is successfully added in the database.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
ResetFormControl();
}
}
}
Stored Procedure for usp_Students_InsertNewStudent
ALTER PROCEDURE [dbo].[usp_Students_InsertNewStudent]
(
#StudentName NVARCHAR(200)
,#Age NVARCHAR(50)
,#Gender NVARCHAR(50)
,#Description NVARCHAR(MAX)
,#Image IMAGE
,#CreatedBy NVARCHAR(50)
)
AS
BEGIN
INSERT INTO [dbo].[Students]
([StudentName]
,[Age]
,[Gender]
,[Description]
,[Image]
,[CreatedBy]
,[CreatedDate])
VALUES
(
#StudentName
,#Age
,#Gender
,#Description
,#Image
,#CreatedBy
,GETDATE()
)
END
A few things:
You need to identify something in the real world that constitutes uniqueness for your student. Even a combination of student name, age and gender would not be unique. This is why schools give out a student id that uniquely identifies a student. Once you know what that is, add it to your table and put a unique constraint on it.
Once this is set, then you need validation on the front end to prevent an error from occurring in your database if a user sends a duplicate in. This looks to be a desktop app with no tiers between the front end and back end so should be speedy to check the DB and give user feedback immediately.
If for whatever reason, you can't do #2, then in your stored procedure, you can before you insert, check for existence because if you don't check, your constraint will kick out an error and if unhandled will crash your app.
ALTER PROCEDURE [dbo].[usp_Students_InsertNewStudent]
(
#StudentName NVARCHAR(200)
,#Age NVARCHAR(50)
,#Gender NVARCHAR(50)
,#Description NVARCHAR(MAX)
,#Image IMAGE
,#CreatedBy NVARCHAR(50)
)
AS
BEGIN
if not exists (select 'x' from dbo.Students where uniquekey=#passedinuniquekey)
BEGIN
INSERT INTO [dbo].[Students]
([StudentName]
,[Age]
,[Gender]
,[Description]
,[Image]
,[CreatedBy]
,[CreatedDate])
VALUES
(
#StudentName
,#Age
,#Gender
,#Description
,#Image
,#CreatedBy
,GETDATE()
)
END
END
Lastly, I would do this in the actual code that calls this stored procedure
int rowsAffected = cmd.ExecuteNonQuery();
if (rowsAffected>0)
{
MessageBox.Show("Student is successfully added in the database.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
ResetFormControl();
}
else
// Failed message here
I changed some code for the doubleclick handler within the ViewStudentForm.cs
In this context I think it better to perform the load operation based on Id instead of student name.
private void StudentDataGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
if(StudentDataGridView.Rows.Count > 0)
{
int StudentId = Convert.ToInt32(StudentDataGridView.SelectedRows[0].Cells[0].Value);
StudentForm studentForm = new StudentForm();
studentForm.StudentId = StudentId;
studentForm.IsUpdate = true;
studentForm.ShowDialog();
LoadDataIntoDataGridView();
}
}

Returning SQL Server Output parameter to C# by Stored Procedure

I have a stored procedure in SQL Server 2012 with an OUTPUT parameter. When I call the c# decrypt function, at the point when I hit ExecuteNonQuery(), I always get the error:
Procedure or function 'DecryptCCode' expects parameter '#decryptedStr', which was not supplied.
How do I get the OUTPUT value of my stored procedure in code? Thanks.
Stored procedure:
ALTER PROCEDURE [dbo].[DecryptCCode]
#decryptedStr nchar(5) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS
(SELECT * FROM sys.symmetric_keys WHERE symmetric_key_id = 101)
CREATE MASTER KEY ENCRYPTION BY
PASSWORD = 'rfsdffsssdfsdfwerefeses'
IF NOT EXISTS
(SELECT * FROM sys.certificates WHERE name='ClientCert')
CREATE CERTIFICATE ClientCert
WITH SUBJECT = 'My ClientCode Certificate';
IF NOT EXISTS
(SELECT * FROM sys.symmetric_keys WHERE name='ClientCode_K1')
CREATE SYMMETRIC KEY ClientCode_K1
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE ClientCert;
OPEN SYMMETRIC KEY ClientCode_K1
DECRYPTION BY CERTIFICATE ClientCert;
SELECT
#decryptedStr = CONVERT(nvarchar, DecryptByKey(ClientCode, 1 , HashBytes('SHA1', CONVERT(varbinary, InstitutionID))))
FROM
dbo.lu_Institution
END
C# Code
public string Decrypt()
{
using (var cn = new SqlConnection(((EntityConnection) ObjectContext.Connection).StoreConnection.ConnectionString))
{
try
{
var sqlcmd = new SqlCommand("EXEC [dbo].[DecryptCCode]", cn);
sqlcmd.Parameters.Add("#decryptedStr", SqlDbType.NChar, 5);
sqlcmd.Parameters["#decryptedStr"].Direction = ParameterDirection.Output;
cn.Open();
sqlcmd.ExecuteNonQuery();
cn.Close();
return sqlcmd.Parameters["#decryptedStr"].Value != DBNull.Value ? (string)sqlcmd.Parameters["#decryptedStr"].Value : string.Empty;
}
catch (Exception e)
{
cn.Close();
Console.WriteLine(e.Message);
return string.Empty;
}
}
}
Your code looks fine, but you need to specify to the Command the CommandType property, that the sql you are trying to execute is a Stored Procedure.
public string Decrypt()
{
using (var cn = new SqlConnection(((EntityConnection) ObjectContext.Connection).StoreConnection.ConnectionString))
{
try
{
cn.Open();
var sqlcmd = new SqlCommand("[dbo].[DecryptCCode]", cn);
// specify the command is a Stored Procedure
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.Parameters.Add("#decryptedStr", SqlDbType.NChar, 5);
sqlcmd.Parameters["#decryptedStr"].Direction = ParameterDirection.Output;
sqlcmd.ExecuteNonQuery();
return sqlcmd.Parameters["#decryptedStr"].Value != DBNull.Value ? (string)sqlcmd.Parameters["#decryptedStr"].Value : string.Empty;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return string.Empty;
}
finally
{
cn.Close();
}
}
}

Executing SQL Stored Procedure

ALTER PROCEDURE dbo.SP_InsertTicket
/*
(
#parameter1 int = 5,
#parameter2 datatype OUTPUT
)
declare #i as numeric
exec SP_InsertTicket 'asd','cd#y.com','232323','dasasd','sdasdas','01-jan-2010',#i output,'sdas','sdasd','02-jan-2010'
select #i*/
#Client_FullName varchar(30),
#Client_EmailAdd varchar(50),
#Client_Telephn varchar(15),
#Ticket_Subject varchar(50),
#Ticket_Source varchar(15),
#Ticket_CreateDate Datetime,
#Ticket_Id integer output,
#Que_Message varchar(100),
#Que_Attachment varchar(max),
#Que_UpdateDate Datetime
AS
declare #TickID integer;
/* SET NOCOUNT ON */
BEGIN
INSERT INTO tbl_Ticket (Client_FullName,Client_EmailAdd,Client_Telephn,Ticket_Subject,Ticket_Source,Ticket_CreateDate)
VALUES (#Client_FullName, #Client_EmailAdd ,#Client_Telephn,#Ticket_Subject,#Ticket_Source,#Ticket_CreateDate)
Select #TickID = MAX(Ticket_Id) from tbl_Ticket
set #Ticket_Id=#TickID
INSERT INTO tbl_TicketQuestion (Ticket_Id,Que_Message,Que_Attachment,Que_UpdateDate)
VALUES (#TickID,#Que_Message,#Que_Attachment,#Que_UpdateDate)
END
RETURN
This is my store procedure in which i need to return Ticket_Id to send it via email app
It insert records well bt not able to retirn value in DAL
Below is the code for executing stored procedure which returns value
public class cls_DAL
{
public cls_DAL()
{
//
// TODO: Add constructor logic here
//
}
static string strConn = System.Configuration.ConfigurationManager.ConnectionStrings["conString"].ConnectionString.ToString();
SqlConnection con = new SqlConnection(strConn);
SqlCommand cmd = new SqlCommand();
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
public int insert_NewTicket(string fullname, string emailadd, string telephone, string subject, string source, DateTime date,string Message, string attachment, DateTime updatedate)
{
try
{
con.Open();
cmd = new SqlCommand("SP_InsertTicket", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Client_FullName", fullname);
cmd.Parameters.AddWithValue("#Client_EmailAdd", emailadd);
cmd.Parameters.AddWithValue("#Client_Telephn",telephone);
cmd.Parameters.AddWithValue("#Ticket_Subject", subject);
cmd.Parameters.AddWithValue("#Ticket_Source",source);
cmd.Parameters.AddWithValue("#Ticket_CreateDate",date);
cmd.Parameters.AddWithValue("#Ticket_Id",0);
cmd.Parameters.AddWithValue("#Que_Message", Message);
cmd.Parameters.AddWithValue("#Que_Attachment", attachment);
cmd.Parameters.AddWithValue("#Que_UpdateDate",updatedate);
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.InputOutput;
return cmd.ExecuteNonQuery();
int i = (int)cmd.Parameters["#Ticket_Id"].Value;
}
catch
{
throw;
}
finally
{
cmd.Dispose();
con.Close();
con.Dispose();
}
}
}
Its just a guess, not sure. You can give a try the following:
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.InputOutput;
TO
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.Output;
That won't compile you'll get unreachable code
cmd.Parameters["#Ticket_Id"].Direction = ParameterDirection.InputOutput; cmd.ExecuteNonQuery();
return (int)cmd.Parameters["#Ticket_Id"].Value;
or #Matt's solution below...
That cast is iffy as well...
And in a multi user scenario, ticketid will race.
Think about what could (will!!!) happen if you run two of these at the same time
Should be wrapped in a transaction.
And you don't need Max, either, Use Scope_Identity
You could run Select Scope_Identity() after the Insert statement. Then in your DAL Method return Convert.ToInt32(cmd.ExecuteScalar())
Change this:
return cmd.ExecuteNonQuery();
to
Int i = cmd.ExecuteScalar();
If you are only returning one integer from that procedure.
ExecuteNonQuery() isnt the method you want to be using here

Calling Stored procedure with output

I have been trying to retrieve some information from my database, and also retrieve the return value. I know the Stored Procedure works fine.
The code I use is a modified piece I use for registering the user. It's going wrong at the cmd.ExecuteReader part of my code.
protected void btn_login_Click(object sender, ImageClickEventArgs e)
{
//Actions after Submit button is clicked
Page.Validate(((ImageButton)sender).ValidationGroup);
if (Page.IsValid)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnectString"].ConnectionString))
{
SqlCommand cmd = new SqlCommand("usp_validateUsers", conn);
//Input Values
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("username", Uname.Text);
cmd.Parameters.AddWithValue("password", pwd.Text);
//Return Values
SqlParameter retParam = cmd.Parameters.Add("#RetVal", SqlDbType.Int);
retParam.Direction = ParameterDirection.ReturnValue;
SqlParameter acsParam = cmd.Parameters.Add("#ac_status", SqlDbType.Int);
acsParam.Direction = ParameterDirection.Output;
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar);
nikParam.Direction = ParameterDirection.Output;
try
{
// Open Connection and execute Stored Proc
conn.Open();
///////////SOMETHING GOES WRONG HERE///////////////
cmd.ExecuteReader();
//Retrieve Data
int retVal = (int)retParam.Value;
string nickname = nikParam.Value.ToString();
string ac_stats = acsParam.Value.ToString();
if (retVal != 0)
{
//Invalid Username or password
}
else
{
//Login User
}
}
catch (Exception Error)
{
lbl_login.Text = "An error occured, please try again later";
debug.Text = Error.Message;
}
finally
{
debug.Text = "\n Clossing Connection";
if (conn.State == System.Data.ConnectionState.Open)
{
conn.Close();
}
}
}
}
}
When I just want to receive the return value I simply use cmd.ExecuteScalar(); I know how to receive data when I'm passing the SQL query to the SQL database, but it seems to be different when using Stored Procedures..
EDIT
Probably could improve this code further but it really does what it should do.
ALTER PROCEDURE dbo.usp_validateUsers
#username varchar(10),
#password varchar(10),
#ac_status char(1) OUTPUT,
#memb_name varchar(15) OUTPUT
AS
IF EXISTS(SELECT * FROM MEMB_INFO WHERE (memb___id = #username))
BEGIN
SELECT #ac_status = ac_status, #memb_name = memb_name
FROM MEMB_INFO
WHERE (memb___id = #username) AND (memb__pwd = #password)
RETURN 0
END
ELSE
BEGIN
return 1
END
When I use break points to catch possible exceptions in Visual Studio, It gives me:
String[4]: The Size property has an invalid size of 0
The error you mentioned may be caused by the fact that you're not specifying the size of your VarChar parameters. Instead of having lines like this:
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar);
Try this:
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar, 15);
You need to create a SqlDataReader.
From Ins and Outs of using Stored Procedures in C#
The SqlDataReader class is used to
read a forward only stream of records
returned from the database. The
SqlDataReader object is not
instantiated directly through a
constructor (hence the lack of the New
key word) but rather through the
ExecuteReader method of the SqlCommand
object. Before calling the
ExecuteReader method the connection to
the database is established using the
Open method of the SqlConnection
object.
Try
SqlDataReader drLogins;
Conn.Open();
drLogins = cmd.ExecuteReader();
Your #ac_status is defined as integer in parameter. change it character or string.
SqlParameter acsParam = cmd.Parameters.Add("#ac_status", SqlDbType.Int);

Categories