How to insert list of items Using Ado.net C#? - c#

I have a store procedure which is taken 2 parameter 1st projectId and 2nd Userid
But i have to mapped the list to that particular project using single connection but i could not figure out how to insert whole list.
internal bool MapEmployeesToProject(int projectId, List<Users> lstUserToMap)
{
int value = 0;
bool check = true;
SqlConnection conn = new SqlConnection(VSTMConfigurations.ConnectionString);
using (conn)
{
SqlParameter[] param = new SqlParameter[2];
param[0] = new SqlParameter("#ProjectId",projectId );
param[1] = new SqlParameter("#ProjectDetails", );
// if there is a single user than i could pass userid here
// i am phasing a problem how to pass whole listOfUsers
using (SqlCommand cmd1 = CreateCommand(conn, param))
{
cmd1.CommandText = "sp_MapProject";
try
{
conn.Open();
SqlTransaction transaction = conn.BeginTransaction();
cmd1.Transaction = transaction;
try
{
value = Convert.ToInt32(cmd1.ExecuteNonQuery());
if (value > 0)
{
transaction.Commit();
check = true;
}
else
{
transaction.Rollback();
check = false;
}
}
catch (Exception ex1)
{
transaction.Rollback();
check = false;
}
finally
{
transaction.Dispose();
}
}
catch (Exception ex)
{
check = false;
}
}
}
return check;
}
the store procedure is
sp_MapProject
(#ProjectId int,#UserId int)
as
begin
Insert into ProjectUsers values (#ProjectId,#UserId)
end
help please!!

You can define a user define table type in your database with projectId and Userid columns. Then you can use DataTable to pass data to stored procedure.
e.g. http://www.codeproject.com/Articles/412802/Sending-a-DataTable-to-a-Stored-Procedure
Thanks!

Related

C# Stored Procedure Return Scalar

I have a stored procedure that will return either a 1 or 0. I cannot seem to properly wrap it in a C# function. Any help is appreciated.
Here is my stored procedure (that I've tested in SQL Server and it works):
CREATE PROCEDURE VerifyAccount
#Email VARCHAR(50),
#Pass VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Salt CHAR(25);
DECLARE #PwdWithSalt VARCHAR(125);
DECLARE #PwdHash VARBINARY(20);
SELECT #Salt = Salt, #PwdHash = Pass
FROM users
WHERE EMAIL = #Email;
SET #PwdWithSalt = #Salt + #Pass;
IF (HASHBYTES('SHA1', #PwdWithSalt) = #PwdHash)
RETURN 1;
ELSE
RETURN 0;
END;
If I open up a new SQL query and run this code, it works:
DECLARE #Result INT;
EXEC #Result = VerifyAccount
#Email = 'myemail#email.com', #Pass = 'Str0ngP#ssw0rd!';
SELECT #Result;
When I try to wrap it in C# code, It returns a -1 value, which is not possible with this procedure. It should return a "1". What am I doing wrong?
public static int ValidateUser(User user)
{
int result = 0;
using (SqlConnection conn = new SqlConnection(SQLQuery.connDb))
{
using (var command = new SqlCommand("VerifyAccount", conn)
{
CommandType = CommandType.StoredProcedure,
Parameters =
{
new SqlParameter("#Email", user.Email),
new SqlParameter("#Pass", user.Password)
}
})
{
try
{
conn.Open();
result = command.ExecuteNonQuery();
conn.Close();
}
catch (Exception e)
{
result = -15;
Console.WriteLine(e.Message);
}
finally
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
}
}
}
return result;
}
ExecuteNonQuery returns the number of rows affected
You need
result = (int)command.ExecuteScalar();
Here is how you read the return value fro Stored Procedures.
public static int ValidateUser(User user)
{
int result = 0;
using (SqlConnection conn = new SqlConnection(SQLQuery.connDb))
{
using (var command = new SqlCommand("VerifyAccount", conn)
{
CommandType = CommandType.StoredProcedure,
Parameters =
{
new SqlParameter("#Email", user.Email),
new SqlParameter("#Pass", user.Password)
}
})
{
try
{
// STEP 01: **** SETUP UP RETURN VALUE STORED PROCEDURES *****
var returnParameter = command.Parameters.Add("#ReturnVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
conn.Open();
result = command.ExecuteNonQuery();
// STEP 02: **** READ RETURN VALUE *****
var result = returnParameter.Value;
conn.Close();
}
catch (Exception e)
{
result = -15;
Console.WriteLine(e.Message);
}
finally
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
}
}
}
return result;
}

C# - Microsoft sql database | Can't add new user on my e-contact app

I tried to make an e-contact app with C# on Visual Studio 2019 connected to a Miscrosoft SQL database (local) following a youtube tutorial.
The app is not complete yet, anyway the btnAdd should work, but it doesn't add the user and the return of the method (Insert).
It always returns false - Can anyone help me?
private void BntAdd_Click(object sender, EventArgs e) {
//Get the value from the imput fields
c.Nome = txtBoxName.Text;
c.Cognome = txtBoxSurname.Text;
c.Telefono1= txtBoxPhone1.Text;
c.Telefono = txtBoxPhone.Text;
c.Email = txtBoxEmail.Text;
//Inserting Data into Database uing the method we created is previous episode
bool success = c.Insert(c);
if (success == true)
{
//Successfully Inserted
MessageBox.Show("New contact added!");
//Call the clear Method Here
Clear();
}
else
{
//Failed to add Contact
MessageBox.Show("ERROR!)");
}
//load Data on Data GRidview
DataTable dt = c.Select();
dgvRubrica.DataSource = dt;
}
public void Clear()
{
txtBoxName.Text = "";
txtBoxSurname.Text = "";
txtBoxPhone1.Text = "";
txtBoxPhone.Text = "";
txtBoxEmail.Text = "";
}
public bool Insert (rubricaClass c) {
bool isSuccess = false;
SqlConnection conn = new SqlConnection(myconnstrng);
try
{
string sql = "INSERT INTO tbl_Rubrica (Nome, Cognome, Telefono1, Telefono, Email) VALUES (#Nome, #Cognome, #Telefono1, #Telefono, #Email)";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("#Nome", c.Nome);
cmd.Parameters.AddWithValue("#Cognome", c.Cognome);
cmd.Parameters.AddWithValue("#Telefono1", c.Telefono1);
cmd.Parameters.AddWithValue("#Telefono", c.Telefono);
cmd.Parameters.AddWithValue("#Email", c.Email);
conn.Open();
int rows = cmd.ExecuteNonQuery();
if (rows > 0)
{
isSuccess = true;
}
else
{
isSuccess = false;
}
}
catch (Exception ex)
{
}
finally
{
conn.Close();
}
return isSuccess;
}
It doesn't give any errors, it work but when i type the ata into txtBoxes and then i press the add button it says Error (message box inserte in the else)
Step 1 is to remove the catch-all exception handling from the Insert method. Most of the ADO.NET database classes implement IDisposable, so you just need a using(...) block to make sure the command is disposed automatically (which will also close and dispose the connection instance):
public bool Insert (rubricaClass c)
{
bool isSuccess = false;
SqlConnection conn = new SqlConnection(myconnstrng);
string sql = "INSERT INTO tbl_Rubrica (Nome, Cognome, Telefono1, Telefono, Email) VALUES (#Nome, #Cognome, #Telefono1, #Telefono, #Email)";
using(SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#Nome", c.Nome);
cmd.Parameters.AddWithValue("#Cognome", c.Cognome);
cmd.Parameters.AddWithValue("#Telefono1", c.Telefono1);
cmd.Parameters.AddWithValue("#Telefono", c.Telefono);
cmd.Parameters.AddWithValue("#Email", c.Email);
conn.Open();
int rows = cmd.ExecuteNonQuery();
if (rows > 0)
{
isSuccess = true;
}
else
{
isSuccess = false;
}
}
return isSuccess;
}
Once that's squared away, Step 2 is to move your exception handling into the application. I don't recommend this "catch everything"-style code, but it works for now, I suppose:
private void BntAdd_Click(object sender, EventArgs e)
{
//Get the value from the imput fields
c.Nome = txtBoxName.Text;
c.Cognome = txtBoxSurname.Text;
c.Telefono1= txtBoxPhone1.Text;
c.Telefono = txtBoxPhone.Text;
c.Email = txtBoxEmail.Text;
try
{
//Inserting Data into Database uing the method we created is previous episode
bool success = c.Insert(c);
if (success == true)
{
//Successfully Inserted
MessageBox.Show("New contact added!");
//Call the clear Method Here
Clear();
}
else
{
//Failed to add Contact
MessageBox.Show("ERROR!)");
}
//load Data on Data GRidview
DataTable dt = c.Select();
dgvRubrica.DataSource = dt;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This will likely tell you that you either have an error in your SQL syntax, or that the command itself could not be run (i.e. the connection string is invalid or the server can't be reached).

how to return a count value with sqllite and c#

I am trying to see if a record exists using c# and sqllite..
My code is as follows
public static bool RecordExists(string fileName)
{
SetConnection();
bool returnValue = false;
try
{
using (SQLiteCommand sqlCommand = new SQLiteCommand("select count(FileName) from downloadedfiles where fileName = '#Filename'", sql_con))
{
sql_con.Open();
sqlCommand.Parameters.Add(new SQLiteParameter("#FileName", fileName));
long userCount = (long)sqlCommand.ExecuteScalar();
if (userCount > 0)
{
returnValue = true;
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return returnValue;
}
Two things,
first point - Originally I had my userCount as int, but it kept saying cast error... why would it have been doing that?
Second point - userCount always returns 0, even when there is data with the same fileName in the table..
Thanks
select count(FileName)
from downloadedfiles
where fileName = '#Filename'
'#Filename'=>#Filename
1st you should not use quotes in parameter. 2nd you name the parameter #Filename in your query where as you use #FileName when you add it. Those 2 have as result your query to always return null. Try:
using (SQLiteCommand sqlCommand = new SQLiteCommand("select count(FileName) from downloadedfiles where fileName = #Filename", sql_con))
{
sql_con.Open();
sqlCommand.Parameters.Add(new SQLiteParameter("#Filename", fileName));
int userCount = (int)sqlCommand.ExecuteScalar();
if (userCount > 0)
{
returnValue = true;
}
}

How to convert this method to catch the inserted id?

I have a windows form and I'm inserting values in the button click event like this
Candidate CanObj = new Candidate(txtName.Text);
if (new CandidateOP().saveCandidate(CanObj))
{
MessageBox.Show("NEW candidate details added");
}
this is my business layer method.
public Boolean saveCandidate(Candidate CanObj)
{
string query6 = "EXEC insertToCand01'" + CanObj.NIC + "'";
return (new DataAccessLayer().executeNonQueries(query6));
}
This is my data access layer method
public Boolean executeNonQueries(string query02)
{
Boolean flag = false;
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query02, con);
com.ExecuteNonQuery();
flag = true;
}
catch (Exception ex)
{
flag = false;
throw ex;
}
finally
{
com.Dispose();
con.Close();
}
return flag;
}
This is the query inside my stored procedure to insert.
In my table the ID is set to auto increment.
INSERT INTO Candidate (User_Name) VALUES (#Uname);
Now I want to display the inserted ID to be displayed when it's inserted.
So I changed the query like this.
INSERT INTO Candidate (User_Name) OUTPUT INSERTED.User_ID VALUES (#Uname);
I want to change my data access layer and business layer to get the value back
How to change my data access layer to achieve this?
Thanks in advance.
Just a quick but important note: you should really use parameterized queries to avoid SQL injection problems, and also using a proper ORM system.
About your concrete question: call your procedure with ExecuteScalar, instead of ExecuteNonQuery, and return the generated id from your stored procedure.
You don't actually need an SP, you can just do a select scope_identity() for example. Or you could use an output parameter in your SP. But just returning a scalar is the simplest way.
Something like this:
Candidate CanObj = new Candidate(txtName.Text);
int id = new CandidateOP().saveCandidate(CanObj);
/* You have **id** here, and you can use it. */
if (id >= 0)
{
MessageBox.Show("NEW candidate details added");
}
Business layer:
public Boolean saveCandidate(Candidate CanObj)
{
string query6 = "EXEC insertToCand01'" + CanObj.NIC + "'";
return new DataAccessLayer().executeNonQueries(query6);
}
and your access layer:
public int executeNonQueries(string query02)
{
long id = -1;
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query02, con);
SqlParameter returnParameter = com.Parameters.Add("RetVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
com.ExecuteNonQuery();
id = (int) returnParameter.Value;
}
catch (Exception ex)
{
id = -1;
throw ex;
}
finally
{
com.Dispose();
con.Close();
}
return id;
}

Problem Error Handling in Stored Statement?

Here is my stored procedure on updating records :
ALTER
PROCEDURE [dbo].[sp_UpdatetoShipped]
(
#Date datetime,
#SerialNumber
varchar(50),
#User
varchar(50),
#WorkWeek
varchar(50)
)
AS
BEGIN
UPDATE dbo.FG_FILLIN SET Status='SHIPPED',DateModified=#Date,ModifiedBy=#User,WorkWeek=#WorkWeek where (Status='KITTED')and SerialNumber=#SerialNumber
END
Then this is my DAL:
public int UpdatetoShipped(FillinEntity fin)
{
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
SqlCommand cmd = new SqlCommand("sp_UpdatetoShipped", conn);
cmd.CommandType =CommandType.StoredProcedure;
try
{
cmd.Parameters.Add("#SerialNumber", SqlDbType.VarChar,50).Value = fin.SerialNumber;
cmd.Parameters.Add("#WorkWeek", SqlDbType.VarChar, 50).Value = fin.WorkWeek;
cmd.Parameters.Add("#Date", SqlDbType.DateTime).Value = DateTime.Now.ToString();
cmd.Parameters.AddWithValue("#User", fin.ModifiedBy);
return cmd.ExecuteNonQuery();
}
catch
{
throw;
}
finally
{
cmd.Dispose();
conn.Close();
conn.Dispose();
}
}
My BLL:
public int UpdatetoShipped(FillinEntity fin)
{
DAL pDAL = new DAL();
try
{
return pDAL.UpdatetoShipped(fin);
}
catch
{
throw;
}
finally
{
pDAL = null;
}
}
And MY UI:
string filepath2 = txtPath2.Text;
Stream stream2 = new FileStream(filepath2, FileMode.Open, FileAccess.Read, FileShare.Read);
ExcelMapper<FillinEntity> exceltoshipped = new ExcelMapper<FillinEntity>();
IExcelParser excelParser2 = new ExcelReaderExcelParser(stream2);
IExcelRowMapper<FillinEntity> mapper2 = new ShippedRowMapper();
IEnumerable<FillinEntity> fillin2 = exceltoshipped.ListAll(excelParser2, mapper2);
int intResult = 0;
BAL pBAL = new BAL();
try
{
foreach (FillinEntity fin in fillin2)
{
fin.ModifiedBy = loggedUser;
intResult = pBAL.UpdatetoShipped(fin);
}
if (intResult > 0)
MessageBox.Show("Record Updated Successfully.");
else
MessageBox.Show("Record couldn't Updated Check Serial");
}
catch (Exception ee)
{
MessageBox.Show(ee.Message.ToString());
}
finally
{
pBAL =null;
}
My problem is it always says updated succussfully. But if i updated it again as duplicate update i want to show serial is already updated.
The key change you need to make is to the following line of SQL from your stored procedure:
UPDATE dbo.FG_FILLIN
SET Status='SHIPPED',
DateModified=#Date,
ModifiedBy=#User,
WorkWeek=#WorkWeek
WHERE (Status='KITTED')
AND SerialNumber=#SerialNumber
You need to return a value that allows you to determine if this UPDATE has already happened or not, for example:
DECLARE #iUpdateAlreadyComplete INT
SET #iUpdateAlreadyComplete = 0;
IF EXISTS
(
SELECT 1
FROM dbo.FG_FILLIN
WHERE Status='SHIPPED'
AND SerialNumber=#SerialNumber
)
BEGIN
SET #iUpdateAlreadyComplete = 1
END
ELSE
BEGIN
UPDATE dbo.FG_FILLIN
SET Status='SHIPPED',
DateModified=#Date,
ModifiedBy=#User,
WorkWeek=#WorkWeek
WHERE (Status='KITTED')
AND SerialNumber=#SerialNumber
END
SELECT #iUpdateAlreadyComplete AS Result
You can then change your DAL from return cmd.ExecuteNonQuery(); to:
var result = Convert.ToInt32(cmd.ExecuteScalar());
return result;
The return value will now be 0 for a record that has been updated, and 1 for one that didn't need updating as it was already processed.
Other Notes
There are a couple of other things that you should consider changing:
sp_UpdatetoShipped is a bad name for a stored procedure. Do not use the sp_ prefix.
Your DAL deliberately catches and re-throws an exception (admittedly in the "best" way), do you really need to?
Rather than explicitly calling Dipose(), use the using() {} syntax instead, as this ensures that Dispose() is called, even in the event of an exception.
using syntax:
using(SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("sp_UpdatetoShipped", conn))
{
}
}
This seems more like a business rule issue than anything to do with an error. What you might want to do is to create a dictionary to hold serial numbers that have already been updated.
e.g.
Dictoinary<string,string> updatedSerialNumbers = new Dictionary<string, string>();
foreach (FillinEntity fin in fillin2)
{
fin.ModifiedBy = loggedUser;
if (updatedSerialNumbers.Contains(fin.SerialNumber) == false)
{
intResult = pBAL.UpdatetoShipped(fin);
updatedSerialNumbers.Add(fin.SerialNumber,fin.SerialNumber);
}
Something like this should sort out your problem.

Categories

Resources