I'm new to C#, and write this code for calling a SQL Server stored procedure:
using (SqlConnection con = new SqlConnection(Connection))
{
using (SqlCommand cmd = new SqlCommand("CheckValidbehzad", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#p_bank", SqlDbType.VarChar).Value = p_bank;
cmd.Parameters.Add("#p_pay_date", SqlDbType.VarChar).Value = p_pay_date;
cmd.Parameters.Add("#p_bill_id", SqlDbType.VarChar).Value = p_bill_id;
cmd.Parameters.Add("#p_payment_id", SqlDbType.VarChar).Value = p_payment;
cmd.Parameters.Add("#p_ref_code", SqlDbType.VarChar).Value = p_ref_code;
cmd.Parameters.Add("#p_branch", SqlDbType.VarChar).Value = p_branch;
cmd.Parameters.Add("#p_channel_type", SqlDbType.VarChar).Value = p_channel;
cmd.Parameters.Add("#p_send_date", SqlDbType.VarChar).Value = p_send_date;
con.Open();
reader = cmd.ExecuteReader();
while (reader.Read())
{
//TempCode = reader["PaymentID"].ToString();
}
}
}
That stored procedure sometimes return ErrorNumber in result and sometimes it returns PaymentID. How can I check this scenario?
if( reader has ErrorNumber field) then
do something
else
do something else
Thanks all.
Not sure how exactly you can distinguish these two columns returned - if the column is present or missing (depending on the situation), then you can check for the presence of the column:
while (reader.Read())
{
try
{
int paymenIdPos = reader.GetOrdinal("PaymentID");
// if found --> read payment id
int paymentID = reader.GetInt32(paymenIdPos);
}
catch(IndexOutOfRangeException)
{
// if "PaymentID" is not found --> read the "ERrorNumber"
int errorCode = reader.GetInt32("ErrorNumber");
}
}
you can check with GetOrdinal, as marc_s suggested, or like this:
if (reader.GetSchemaTable().Select("ColumnName = 'PaymentId'").Length > 0)
{
//do something here with pamynet
}
else if (reader.GetSchemaTable().Select("ColumnName = 'ErrorNumber'").Length > 0)
{
//do your stuff here with error number
}
Related
I'm trying to create a generic function, using SqlClient in C#, which will accept a value for any column name and return the valid objects. My approach is to create a stored procedure which accepts values for every column, and where a value has not been provided it will be assume that any value is accepted. My code is as below (code is a little unfinished with regards the final requirement):
public async Task<List<OrganisationInstance>> GetOrganisationInstances(string name, int id)
{
List<OrganisationInstance> responses = new List<OrganisationInstance>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
try
{
await connection.OpenAsync();
using (SqlCommand command = new SqlCommand(OrganisationInstancesSP.READ_ORGANISATION, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("Id", SqlDbType.BigInt).Value = 2;
command.Parameters.Add("CreationDate", SqlDbType.DateTime).Value = "2021-12-14 23:59:25.837";
command.Parameters.Add("CompanyName", SqlDbType.VarChar).Value = "xxxxxxxxxx";
command.Parameters.Add("Postcode", SqlDbType.VarChar).Value = "xxxxxxxxxxx";
command.Parameters.Add("FormationDate", SqlDbType.DateTime).Value = "2021-12-14 23:59:25.837";
command.Parameters.Add("NodeId", SqlDbType.Int).Value = id;
SqlDataReader reader = await command.ExecuteReaderAsync();
while (reader.Read())
responses.Add(new OrganisationInstance { CompanyName = reader["CompanyName"].ToString(), Id = Convert.ToInt32(reader["Id"]) });
}
}
catch (DbException ex)
{
return null;
}
finally
{
connection.Close();
}
}
return responses;
}
The query returns the expected object, when all values are set to the values of an instance/row in the database. However, I cannot find a way to set the column to allow any value.
In SQL, it would be possible to say WHERE CreationDate = CreationDate , which would translate to:
command.Parameters.Add("CreationDate", SqlDbType.DateTime).Value = "CreationDate";
However, this doesn't work in SqlClient. Can anyone suggest how I might achieve this?
I would like to find a way to exit out of datareader after the if statement so that I can execute the insert query in else statement. Is there a way to do it?
I am getting the error that dr is still open and hence cannot perform the below query.
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery();
Here is the code:
public class VendorDetails
{
int _VendorID;
string _VendorName;
public int VendorID
{
set { _VendorID = value; }
get { return _VendorID; }
}
public string VendorName
{
set { _VendorName = value; }
get { return _VendorName; }
}
}
public VendorDetails VendorCheck(string sVendorName)
{
SqlCommand cmd = new SqlCommand("dbo.usp_GetVendorByVendorName", myConnection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
cmd.Parameters["#VendorName"].Value = sVendorName;
VendorDetails sVendorDetails = null;
try
{
myConnection.Open();
SqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = ((int)dr["VendorID"]);
sVendorDetails.VendorName = ((string)dr["VendorName"]).ToUpper().Trim();
}
}
else if (dr.HasRows!= true)
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + sVendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true);
SqlCommand insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection);
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = sVendorName;
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery();
sVendorDetails.VendorName = sVendorName;
}
dr.Close();
return sVendorDetails;
}
catch (SqlException err)
{
throw new ApplicationException("DB usp_GetVendorByVendorName Error: " + err.Message);
}
finally
{
myConnection.Close();
}
}
You will need to close/dispose of your DataReader prior to reusing the connection, as it's still being used.
Maybe something like this?
var readerHasRows = false;
using (var dr = cmd.ExecuteReader())
{
readerHasRows = dr.HasRows;
if(readerHasRows)
{
while (dr.Read())
{
sVendorDetails = new VendorDetails();
sVendorDetails.VendorID = ((int)dr["VendorID"]);
sVendorDetails.VendorName = ((string)dr["VendorName"]).ToUpper().Trim();
}
}
}
if(!readerHasRows)
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + sVendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true);
SqlCommand insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection);
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = sVendorName;
sVendorDetails = new VendorDetails();
VendorDetails.VendorID = insertcmd.ExecuteNonQuery();
sVendorDetails.VendorName = sVendorName;
}
There are a few things I would like to mention
Your main issue is that you are not closing your DataReader. You can use the using statement for it
You don't need to explicitly open and close the SqlConnection. The SqlCommand object will do it as needed.
You don't need to check with if (dr.HasRows) and then check again in while (dr.Read()). Also, you don't need to loop to pick up only one row of data.
Ideally, I would put the "Fetch" part in a separate function and the "insert" in a separate function, so the functions stay small and reusable.
Your pattern is superfluous if (flag) {TakeAction();} else if (!flag) {TakeAction2();}. Every time the code hits theelse, it will also hit theif (!flag)`
sVendorDetails.VendorID = insertcmd.ExecuteNonQuery(); line looks fishy. If your Stored Procedure returns the VendorId, then you should use ExecuteScalar. Currently it is just storing 1 in all case since you are presumably inserting one row.
Don't discard the original SqlException when creating a custom ApplicationException. Upstream system might want to know more details than you are passing. Pass it along as the InnerException
I have also changed some stylistic aspects:
The variable names changed to the more commonly used camelCase, instead of the incorrectly used Hungarian Notation (sVendorDetails instead of oVendorDetails)
Brace in K&R style
Used var when the right side is a new statement
Use Object Initializers instead of creation+assignment
Below is the code
public VendorDetails VendorCheck(string vendorName, SqlConnection myConnection) {
try {
return GetVendor(vendorName, myConnection) ?? InsertVendor(vendorName, myConnection);
} catch (SqlException err) {
throw new ApplicationException("DB usp_GetVendorByVendorName Error: " + err.Message, err);
}
}
VendorDetails GetVendor(string vendorName, SqlConnection myConnection) {
using (var cmd = new SqlCommand("dbo.usp_GetVendorByVendorName", myConnection)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
cmd.Parameters["#VendorName"].Value = vendorName;
using (SqlDataReader dr = cmd.ExecuteReader()) {
ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('VendorName:" + vendorName + " not found. Inserting Vendor details into Vendor and Invoice table.')", true); // TODO: Does this really belong here!?!?
if (dr.Read()) {
return new VendorDetails {
VendorID = ((int)dr["VendorID"]),
VendorName = ((string)dr["VendorName"]).ToUpper().Trim()
};
}
}
}
return null;
}
VendorDetails InsertVendor(string vendorName, SqlConnection myConnection) {
using (var insertcmd = new SqlCommand("dbo.InsertVendorName", myConnection)) {
insertcmd.CommandType = CommandType.StoredProcedure;
insertcmd.Parameters.Add(new SqlParameter("#VendorName", SqlDbType.VarChar));
insertcmd.Parameters["#VendorName"].Value = vendorName;
return new VendorDetails {
VendorID = (int)insertcmd.ExecuteScalar(),
VendorName = vendorName
};
}
}
I am trying to query the MySQL database from a c# application. Below is the code , here I am using parameterized query
public static void ValidateName(MySqlConnection conn,List<Employee> EmpList, string Group)
{
string selectQuery = "Select Name from Employee where Group = #Group AND #Name in (FirstName, LastName);";
using (MySqlCommand cmd = new MySqlCommand(selectQuery, conn))
{
for (int i = 0; i < EmpList.Count; i++)
{
cmd.Parameters.Add("#Group", MySqlDbType.VarChar).Value = Group;
cmd.Parameters.Add("#Name", MySqlDbType.VarChar).Value = EmpList[i].Name;
var reader = cmd.ExecuteReader();
List<string> lineList = new List<string>();
while (reader.Read())
{
lineList.Add(reader.GetString(0));
}
if (lineList.Count <=0)
{
WriteValidationFailure(EmpList[i], "Failed");
}
}
}
But the above code is throwing error in below line saying
cmd.Parameters.Add("#Group", MySqlDbType.VarChar).Value = Group;
An unhandled exception of type 'MySql.Data.MySqlClient.MySqlException'
occurred in MySql.Data.dll' #Group has already been defined
This is happening because you are adding the same set of parameters in each iterations. You can either clear then in each iteration or else add them before starting the loop and change the value of existing parameter during each iteration. I think second option would be great. One more thing I have to specify here is about the reader, you have to use reader as an using variable so that each time it will get disposed at the end of the using block and your code works fine. Which means you can try something like this:
using (MySqlCommand cmd = new MySqlCommand(selectQuery, conn))
{
cmd.Parameters.Add(new MySqlParameter("#Group", MySqlDbType.VarChar));
cmd.Parameters.Add(new MySqlParameter("#Name", MySqlDbType.VarChar));
for (int i = 0; i < EmpList.Count; i++)
{
cmd.Parameters["Group"].Value = group;
cmd.Parameters["Name"].Value = EmpList[i].Name;
// rest of code here
using (MySqlDataReader reader = cmd.ExecuteReader())
{
// Process reader operations
}
}
}
The situation is following, i have a stored procedure in SQL Server , this has a few output parameters, one of them is a bit type, what I want is to take the value of that parameter, but I have a conversion error, InvalidCastException.
This is my code:
public void exec()
{
String strConnString = "Server=.\\SQLEXPRESS;Database=recalls;Integrated Security=true";
SqlConnection con = new SqlConnection(strConnString);
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "findVinCamp";
int c = Int32.Parse(campaing.Text);
cmd.Parameters.Add("#camp", SqlDbType.Int).Value = c;
cmd.Parameters.Add("#vin", SqlDbType.VarChar, 100).Value = vin.Text;
cmd.Parameters.Add("#desc", SqlDbType.NVarChar, 255).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#st", SqlDbType.VarChar, 50).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#bit", SqlDbType.Bit).Direction = ParameterDirection.Output;
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
bit = (int)cmd.Parameters["#bit"].Value; //Exception Here
if (bit == 1)
{
desc.Text = cmd.Parameters["#desc"].Value.ToString();
stt.Text = cmd.Parameters["#st"].Value.ToString();
camp = cmd.Parameters["#camp"].Value.ToString();
if (stt.Text.Equals("APPLIED"))
{
stt.ForeColor = System.Drawing.Color.LawnGreen;
}
else
{
stt.ForeColor = System.Drawing.Color.Firebrick;
label3.Enabled = true;
newstatus.Enabled = true;
update.Enabled = true;
}
}
else
{
MessageBox.Show("Doesn't exits!");
}
}
I'm trying to assign the bit parameter to a int variable. Any question post on comments.
I change the (int) to this, now works perfectly:
Boolean lol = Convert.ToBoolean(cmd.Parameters["#bit"].Value);
Use this following Line
bool isConfirmed = (bool)cmd.Parameters["#bit"].Value;
if(isConfirmed ){
desc.Text = cmd.Parameters["#desc"].Value.ToString();
stt.Text = cmd.Parameters["#st"].Value.ToString();
camp = cmd.Parameters["#camp"].Value.ToString();
if (stt.Text.Equals("APPLIED"))
{
stt.ForeColor = System.Drawing.Color.LawnGreen;
}
else
{
stt.ForeColor = System.Drawing.Color.Firebrick;
label3.Enabled = true;
newstatus.Enabled = true;
update.Enabled = true;
}
}
else{
MessageBox.Show("Doesn't exits!");
}
**UPDATE: **if the bit column allows nulls -- many ways you can do this
bool isConfirmed = cmd.Parameters["#bit"].Value as bool? ?? null;
and also read this- SQL Server Data Types and Their .NET Framework Equivalents
I believe a bit will convert to a boolean. Which should make your code a bit simpler too.
i.e.
...
var bit = (bool)cmd.Parameters["#bit"].Value;
if (bit)
{
...
You are trying to convert your boolean output to INT, please convert it in Boolean:
bool bitValue= Convert.ToBoolean(cmd.Parameters["#bit"].Value)
I am trying to split a string of values actually individually and trying to send it into database via Store procedure but i can't figure out that how ?
String to be Split: "2013-03-31,1299,2013-03-31,1099,9888, 0"
CODE:
public bool SqlInsert(String parametersString)
{
//It should be split here
SqlConnection sqlCon = new SqlConnection(conStr);
SqlCommand sqlCom = new SqlCommand("AddCoordinates", sqlCon);
sqlCom.CommandType = CommandType.StoredProcedure;
sqlCom.Parameters.Add("#AddedDateTime", SqlDbType.VarChar).Value = "";
sqlCom.Parameters.Add("#IMEI", SqlDbType.VarChar).Value = "";
sqlCom.Parameters.Add("#RecordedDateTime", SqlDbType.VarChar).Value = "";
sqlCom.Parameters.Add("#Latitude", SqlDbType.VarChar).Value = "";
sqlCom.Parameters.Add("#Longitude", SqlDbType.VarChar).Value = "";
sqlCom.Parameters.Add("#IsParking ", SqlDbType.Bit).Value = true;
try
{
sqlCon.Open();
int NoRows = (int)sqlCom.ExecuteNonQuery();
}
catch (Exception ex) { }
finally
{
sqlCon.Close();
}
return true;
}
Looks to me like it's just comma delimited, in which case try:
string[] parts = "2013-03-31,1299,2013-03-31,1099,9888, 0".Split(',');
However, that makes working with parts a bit awkward, so we can go a step further:
string[] fields = new string[] {"date","imei","recorded_date","lat","lon","is_parking"};
List<string,string> dict = Dictionary<string,string>();
for(var i = 0; i < parts.Length; i++) {
var key = fields[i];
var value = parts[i].Trim(); // You may or may not want to trim the value
dict.Add(key,value);
}
You can then pull the individual fields you want and convert them to the type you need, for example:
int imei = int.Parse(dict["imei"]);
Of course, I'm sidestepping a couple of issues like type conversion failure and disparities between parts and fields, but you get the general idea.
The approach mentioned by #Lloyd is the easiest one.Here is the code for your understanding.
string[] parts = "2013-03-31,1299,2013-03-31,1099,9888, 0".Split(',');
sqlCom.Parameters.Add("#AddedDateTime", SqlDbType.VarChar).Value = parts[0].ToString();
sqlCom.Parameters.Add("#IMEI", SqlDbType.VarChar).Value = parts[1].ToString();
sqlCom.Parameters.Add("#RecordedDateTime", SqlDbType.VarChar).Value = Convert.ToDateTime(parts[2].ToString());
sqlCom.Parameters.Add("#Latitude", SqlDbType.VarChar).Value = parts[3].ToString(); ;
sqlCom.Parameters.Add("#Longitude", SqlDbType.VarChar).Value = parts[4].ToString(); ;
sqlCom.Parameters.Add("#IsParking ", SqlDbType.Bit).Value = ((parts[5].ToString().Trim()).Equals("0"))? true: false;
Hope it helps!