Browsed some answers and doesn't appear to be working for me.
I need the ID field of a table to be returned so I can use it in a different part of the program, I've tried using
Convert.ToInt32(sqlComm.ExecuteScalar());
But no luck, and same for
Convert.ToInt32(sqlComm.Parameters["ID"].Value);
And both return 0, even though the record does get inserted into the table.
I'll dump the code below, can anyone see what I'm doing wrong?
using (SqlConnection sqlConnect = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
using (SqlCommand sqlComm = new SqlCommand("up_Insert_Address", sqlConnect))
{
sqlComm.CommandType = CommandType.StoredProcedure;
sqlComm.Parameters.Add("#AddressID", SqlDbType.BigInt).Direction = ParameterDirection.Output;
sqlComm.Parameters.Add("#AddressLineOne", SqlDbType.NVarChar, 40).Value = address.AddressLineOne;
try
{
sqlComm.Connection.Open();
return Convert.ToInt32(sqlComm.ExecuteScalar());
}
catch (SqlException)
{
}
finally
{
sqlComm.Connection.Close();
}
}
}
And Stored Procedure:
#AddressID Bigint OUTPUT,
#AddressLineOne NVarChar(40)
AS
BEGIN
BEGIN TRY
INSERT INTO Address
(
AddressLineOne
)
VALUES
(
#AddressLineOne
)
SET #AddressID = SCOPE_IDENTITY();
END TRY
BEGIN CATCH
DECLARE #Err nvarchar(500)
SET #Err = ERROR_MESSAGE()
RAISERROR(#Err, 16, 1)
END CATCH
END
You should be using
Convert.ToInt64(sqlComm.Parameters["#AddressID"].Value);
after you execute the command using ExceuteNonQuery. For future reference, ExecuteScalar returns the first column of the first row in the result set returned by the query. You're not returning anything, just setting the value of an OUTPUT parameter.
Also you should DEFINITELY not swallow any SqlException. Since your command and connection are already in using blocks you don't need to add another try/catch/finally. Change it to to:
//try
//{
sqlComm.Connection.Open();
sqlComm.ExecuteNonQuery();
return Convert.ToInt64(sqlComm.Parameters["#AddressID"].Value);
// using Int64 since the SQL type is BigInt
//}
//catch (SqlException)
//{
//}
//finally
//{
// sqlComm.Connection.Close();
//}
var connectionstring = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
var addressId = 0L; // long value (64bit)
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand("up_Insert_Address", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#AddressID", addressId);
command.Parameters.AddWithValue("#AddressLineOne", address.AddressLineOne);
command.Parameters["#AddressID"].Direction = ParameterDirection.Output;
try
{
if(connection.State != ConnectionState.Open)
connection.Open();
var rowsAffected = command.ExecuteNonQuery();
addressId = Convert.ToInt64(command.Parameters["#AddressID"].Value);
}
catch (SqlException)
{
// Handle SQL errors here.
}
}
Related
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!
I have the following stored procedure inside a package:
PROCEDURE DELETE_RECORD(serial IN NUMBER, code IN NUMBER, brand IN VARCHAR2, response OUT VARCHAR2) IS
BEGIN
DELETE FROM CWPESME.cwpesme_campaign_subsc
WHERE
sku = serial
AND id = code
AND mfg = brand;
COMMIT;
response := 'SUCCESS';
EXCEPTION WHEN OTHERS THEN
response := 'FAILURE '||SQLERRM;
END DELETE_RECORD;
This is how I call it from a web service:
[WebMethod]
public string del_record(int serial, int code, string brand)
{
using (OracleConnection conn = new OracleConnection(ConfigurationManager.ConnectionStrings["cs"].ConnectionString))
{
using (OracleCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "MyPackage.DELETE_RECORD";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add("sku", OracleType.Number, 38).Direction = System.Data.ParameterDirection.Input;
cmd.Parameters["sku"].Value = serial;
cmd.Parameters.Add("id", OracleType.Number, 38).Direction = System.Data.ParameterDirection.Input;
cmd.Parameters["id"].Value = code;
cmd.Parameters.Add("mfg", OracleType.VarChar, 250).Direction = System.Data.ParameterDirection.Input;
cmd.Parameters["mfg"].Value = brand;
cmd.Parameters.Add("response", OracleType.VarChar, 550).Direction = System.Data.ParameterDirection.Output;
try
{
conn.Open();
cmd.ExecuteNonQuery();
string result = cmd.Parameters["response"].Value.ToString();
return result;
}
catch (Exception ex)
{
string result = cmd.Parameters["response"].Value.ToString();
return (result != string.Empty) ? result : "[FAILURE] " + ex.ToString();
}
}
}
}
I'm able to remove the record if I call the stored procedure directly, but unable to do so when calling from a web service. I can however SELECT and UPDATE in this way. What's happening?
There was nothing wrong with the code; the user was re-checked and granted all privileges over the table and now is able to delete rows.
I'm trying to raise a user defined error from one stored procedure to a c# code. I have a stored procedure that assign employees to users, if you already assigned an employee to user 1 for example and you try to do the same thing again, the stored procedure should raise an error saying, "This association already exists" and return a code, for example 1 and a description of the error. The problem is that in c sharp is not passing by the catch part. here is the code:
public bool InsertUser(Guid userId, string[][] IDs, AppParams myParams)
{
bool flag = false;
SqlTransaction transaction = null;
using (var dbConn = new SqlConnection(this.ConnectionString))
{
using (var cmd = new SqlCommand())
{
try
{
dbConn.Open();
cmd.Connection = dbConn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "InsertUser";
transaction = dbConn.BeginTransaction();
cmd.Transaction = transaction;
for (int i = 0; i < IDs.Length; i++)
{
flag = false;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#UserId", userId);
cmd.Parameters.AddWithValue("#EmployeeId", IDs[i][0]);
cmd.Parameters.AddWithValue("#CompanyId", myParams.ApplicationId);
cmd.Parameters.AddWithValue("#ModifiedUserId", myParams.User.UserId);
//add output parameter
cmd.Parameters.Add("#ID", SqlDbType.Int, 4);
cmd.Parameters["#ID"].Direction = ParameterDirection.Output;
cmd.Parameters.Add("#ReturnValue", SqlDbType.Int, 4);
cmd.Parameters["#ReturnValue"].Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add("#ErrorMsg", SqlDbType.VarChar, 300);
cmd.Parameters["#ErrorMsg"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
var returnValue = Convert.ToInt32(cmd.Parameters["#ReturnValue"].Value);
flag = returnValue.Equals(0);
if (!flag)
{
if (cmd.Parameters["#ErrorMsg"].Value != DBNull.Value)
{
this.ErrorModel.HasError = true;
this.ErrorModel.ErrorMessage = cmd.Parameters["#ErrorMsg"].Value.ToString();
}
transaction.Rollback();
return false;
}
}
transaction.Commit();
}
catch (SqlException sqlex)
{
this.ErrorModel.HasError = true;
this.ErrorModel.ErrorMessage = sqlex.Message;
}
catch (Exception ex)
{
this.ErrorModel.HasError = true;
this.ErrorModel.ErrorMessage = ex.Message;
}
}
}
return flag;
}
Am I doing something wrong?
here is my SQL code:
ALTER PROCEDURE [dbo].[InsertUser]
(
#UserId uniqueidentifier,
#EmployeeId int,
#CompanyId uniqueidentifier,
#SystemStatusId int = 1,
#ModifiedByUserId uniqueidentifier,
#ID int output,
#ErrorMsg nvarchar(300) = NULL output
)
AS
BEGIN
DECLARE #ReturnVal int
SET NOCOUNT ON;
SET #ReturnVal = 0;
SET #ErrorMsg = null;
-- check for existing combination
IF EXISTS(SELECT 1 FROM [dbo].[UserRel]
WHERE [UserId] = #UserId
AND [EmployeeId] = #EmployeeId
AND [CompanyId] = #CompanyId
AND [SystemStatusId] = 1)
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'Item already Exist in Database'
RAISERROR #ErrorMsg, 0, 0
GOTO ProcedureExit;
END
-- Insert statement
INSERT [dbo].[UserRel]
(
[UserId],
[EmployeeId],
[CompanyId],
[SystemStatusId],
[ModifiedByUserId],
[ModifiedDate]
)
VALUES
(
#UserId,
#EmployeeId,
#CompanyId,
#SystemStatusId,
#ModifiedByUserId,
sysdatetimeoffset()
)
IF( ##ERROR <> 0 )
BEGIN
SET #ReturnVal = 2;
SET #ErrorMsg = 'Failed to INSERT [dbo].[InsertUser]'
GOTO ProcedureExit;
END
ELSE
SET #ID = SCOPE_IDENTITY();
ProcedureExit:
RETURN #ReturnVal;
END
The problem is that in c sharp is not passing by the catch part.
Because in the C#, there was no exception. You simply checked the return value of the stored procedure, and then decided to rollback and return false;. If you want an exception: throw an exception.
Alternatively, if the SQL issues a raiserror with severity 16 or higher, an exception will be observed automatically.
Incidentally, it is not clear that your transaction is correctly terminated in the case of a genuine exception. I would suggest moving the rollback code into the catch block. Since you do the exact same thing regardless of which exception you catch, there is no benefit in the catch (SqlException sqlex) block - so you might as well remove that.
I'm using visual studios 2010 to create a c# web application with a database. My goal is to have default.aspx call a c# class which runs a stored procedure that selects an entry from the table and returns it. Here's the code:
'The stored procedure. I want it to send back the name it gets from doing
'the query to the c# class.
ALTER PROCEDURE getName (#id int)
AS
BEGIN
SET NOCOUNT ON;
--
SELECT name FROM tableA where id = #id;
END
Return
//Here's the c# class I'm using.
public class student
{
public string name;
public int id;
public student()
{ }
public String doQuery(int id)
{
SqlConnection conn = null;
try
{
conn = new SqlConnection("Server =(local); Database = Database1.mdf;
Integrated Security = SSPI");
conn.Open();
SqlCommand cmd = new SqlCommand("getName", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#id", SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.Value = id;
cmd.Parameters.Add(param);
//This is some code from when I tryed return value
//SqlParameter reVal = cmd.Parameters.Add("#name", SqlDbType.VarChar);
//reVal.Direction = ParameterDirection.ReturnValue;
//before using ExecuteScalar I tried ExcuteNonQuery with the commented
//out code
name = (string)cmd.ExecuteScalar();
//name = (String)cmd.Parameters["#name"].Value;
conn.Close();
}
catch(Exception)
{}
return name;
}
}
Running my program does not return errors it simply doesn't place any value in name. What am I missing to get the name that is selected in the sql procedure into the name variable in my c# class. I hope I'm conveying my problem clearly.
edit1:I didn't put anything in the catch cause hadn't decided what to use to see that it had errored out. I changed it to make name = "error" when it fails the try and that's what I get and that's what I get.
I also tried running "exec getName 5, otherstuff"in sql server management. I'm a little unclear about what to use as the second parameter when running exec getName since the second parameter is suppose to be just output but still seems to be required to run it. It just says the commands are executed successfully but doesn't display the name that goes with id 5
I would recommend using the async/await pattern for SQL statements. Fortunately, it doesn't require much refactoring.
See if this works for you:
public async Task<string> QueryGetNameAsync(int id)
{
using (var dbConn = new SqlConnection("..."))
using (var command = new SqlCommand("getName", dbConn))
{
try
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#id", id);
await dbConn.OpenAsync();
var result = await command.ExecuteScalarAsync();
dbConn.Close();
var name = result as string;
return name;
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
You'd call it with something like:
private async void DoLookup_Clicked(object sender, EventArgs e)
{
var id = int.Parse(idText.Text);
var name = await QueryGetNameAsync(id);
}
Alternatively, can use OUTPUT parameters in SQL but you would have to adjust your stored procedure to something like this:
ALTER PROCEDURE getName
(
#id int,
#name varchar(100) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
SELECT #name = name FROM tableA where id = #id;
END
Then your C# function would be something like:
public async Task<string> QueryGetNameAsync(int id)
{
using (var dbConn = new SqlConnection("..."))
using (var command = new SqlCommand("getName", dbConn))
{
try
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#id", id);
command.Parameters.Add("#name", SqlDbType.VarChar, 100);
command.Parameters["#name"].Direction = ParameterDirection.Output;
await dbConn.OpenAsync();
await command.ExecuteNonQueryAsync();
dbConn.Close();
var name = command.Parameters["#name"].Value as string;
return name;
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
The problem is in your connection string: unless you have strange naming conventions, you are specifying the database file name and not the name of the database itself.
Try changing this part of the connection string: Database = Database1.mdf; to Database = Database1;.
If you are confused about what is or is not valid in the connection string, you can always use the SqlConnectionStringBuilder which will create the appropriate connection string for you after you have set the correct properties.
You can also use the list of properties specified in the SqlConnection.ConnectionString documentation as a reference that contains examples.
Finally, I strongly recommend the following best practices:
1) Use using blocks with the connection and commands to ensure they are properly closed and disposed.
2) Do not assign name directly to the result of ExecuteScalar in case it is return as DBNull.Value
3) Never ignore exceptions unless you have documented why you are doing so in the code.
Here is a quick rewrite with all of the above recommendations:
try
{
using (var conn = new SqlConnection("Server =(local); Database = Database1; Integrated Security = SSPI"))
{
conn.Open();
using (var cmd = new SqlCommand("getName", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
var param = new SqlParameter("#id", SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.Value = id;
cmd.Parameters.Add(param);
var oResult = cmd.ExecuteScalar();
if ((oResult != null) && (oResult != DBNull.Value))
{
name = (string)oResult;
}
}
conn.Close();
}
}
catch (Exception)
{
// Do something with the exception here, don't just ignore it
}
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.