How to use stored procedure for the CommandText of UpdateCommand? - c#

I have a DataGridView for editing a table. The following code is using to update the table.
using (SqlConnection con = new SqlConnection("...."))
{
con.Open();
SqlDataAdapter da = new SqlDataAdapter("select * from T", con);
SqlCommandBuilder cb = new SqlCommandBuilder(da);
cb.ConflictOption = ConflictOption.OverwriteChanges;
da.UpdateCommand = cb.GetUpdateCommand();
// da.UpdateCommand.CommandText = "exec sp1 #p1, #p2, #p3...";
da.InsertCommand = cb.GetInsertCommand();
da.DeleteCommand = cb.GetDeleteCommand();
da.Update(datatable.GetChanges());
}
I found that da.Update(datatable.GetChanges()) smartly generates minimal set clause according to the modified columns.
update T set c1 = #p1 where K = #p2 -- If only c1 is changed in the grid
update T set c1 = #p1, c2 = #p2 where K = #p3 -- if both c1 and c2 is changed
update T set c4 = #p1 where K = #p2 -- if only c4 is changed
......
How to write the stored procedure for the CommandText?

You will want to create a stored procedure on the server that receives the parameters. The method you are using is for generating SQL and does not use stored procedures, it sends SQL thru the connection to the server. If I named the sproc UpdateSomeUserTable:
oleDbCommand1.CommandText = "UpdateSomeUserTable";
oleDbCommand1.CommandType = System.Data.CommandType.StoredProcedure;
oleDbCommand1.Parameters["us_id"].Value = "668987";
oleDbCommand1.Parameters["us_lname"].Value = "White";
oleDbCommand1.Parameters["us_fname"].Value = "Johnson";
oleDbConnection1.Open();
oleDbCommand1.ExecuteNonQuery();
oleDbConnection1.Close();

This is a code smelling, I do not advise use, but it works.
dataAdapter.RowUpdating += (sender, e) =>
{
if (e.Command != null && !string.IsNullOrEmpty(e.Command.CommandText))
{
e.Command.CommandText = $"";
}
};

Related

Index out of range on column from inner join condition

I am getting a index out of range error when trying to get a string value from a datareader. The column USER_ROLE which is the only column from a INNER JOIN condition. It was working and for some reason has now started throwing this index out of range error. I've verified the actual stored procedure works via SSMS and the column is being returned.
Below is the code for the stored procedure
ALTER PROCEDURE [dbo].[usp_GetUsersLogonInformation]
(
-- inactive = 0, active = 1, all = 2
#active int = 2
)
AS
BEGIN
DECLARE #whereClauseNeeded bit = 1
DECLARE #whereClause nvarchar(100) = concat(' WHERE usr.ACTIVE = ', #active)
DECLARE #sqlCmd nvarchar(max)= 'SELECT
usr.USER_PK,
usr.PRINCIPAL_ID,
usr.AA_USER_FK,
usr.FIRST_NAME,
usr.LAST_NAME,
usr.[USER_NAME],
usr.EMAIL_ADDRESS,
usr.ACTIVE,
usr.LV_USER_ROLE_FK,
lvur.USER_ROLE,
usr.CREATED_BY,
usr.CREATED_SYSDATE
FROM dbo.USERS usr
INNER JOIN dbo.LV_USER_ROLES lvur ON lvur.LV_USER_ROLE_PK = usr.LV_USER_ROLE_FK'
IF #active = 0 OR #active = 1
BEGIN
set #sqlCmd = concat(#sqlCmd, #whereClause)
END
EXEC sp_executesql #sqlCmd
END
the c# code retrieving the data
using (SqlConnection dbConn = theVoiceSqlHelpers.GetDbConnection())
{
using (SqlCommand sqlCmd = new SqlCommand(USP_GET_USER_INFO, dbConn))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Parameters.AddWithValue("#whereClause",string.Format("USER_NAME = \'{0}\'", txbxUserName.Text));
SqlDataReader dr = sqlCmd.ExecuteReader();
while (dr.Read())
{
user = new Tbl_Users();
user.USER_PK = dr.GetInt32(dr.GetOrdinal("USER_PK"));
user.PRINCIPAL_ID = dr.GetInt32(dr.GetOrdinal("PRINCIPAL_ID"));
user.AA_USER_FK = dr.GetInt32(dr.GetOrdinal("AA_USER_FK"));
user.FIRST_NAME = dr.GetString(dr.GetOrdinal("FIRST_NAME"));
user.LAST_NAME = dr.GetString(dr.GetOrdinal("LAST_NAME"));
user.USER_NAME = dr.GetString(dr.GetOrdinal("USER_NAME"));
user.EMAIL_ADDRESS = dr.GetString(dr.GetOrdinal("EMAIL_ADDRESS"));
user.ACTIVE = dr.GetBoolean(dr.GetOrdinal("ACTIVE"));
user.LV_USER_ROLE_FK = dr.GetInt32(dr.GetOrdinal("LV_USER_ROLE_FK"));
user.USER_ROLE = dr.GetString(dr.GetOrdinal("USER_ROLE"));
user.CREATED_BY = dr.GetString(dr.GetOrdinal("CREATED_BY"));
user.CREATED_SYSDATE = dr.GetDateTime(dr.GetOrdinal("CREATED_SYSDATE"));
}
dr.Close();
}
}
I have ensure the column name is correct however I am now stuck at this new found exception.
Has anyone seen this behavior before. My apologies if I am overlooking and obvious but could use an extra set of eyes on this.
LV_USER_ROLES Table
USERS Table
Charlieface's comment resolved the exception. In this database there is 2 similiar named usp's and I was calling the wrong one.

Issue in update records in c#

Here is searchupdate_Click code:
protected void searchupdate_Click(object sender, EventArgs e)
{
SqlConnection con = Connection.DBconnection();
{
SqlCommand com = new SqlCommand("sp_searchupdate", con);
com.CommandType = CommandType.StoredProcedure;
com.Parameters.AddWithValue("#id", textstudentid.Text);
SqlDataAdapter adp = new SqlDataAdapter(com);
DataSet ds = new DataSet();
adp.Fill(ds);
txttamil.Text = ds.Tables[0].Rows[0]["Tamil"].ToString();
txtenglish.Text = ds.Tables[0].Rows[0]["English"].ToString();
txtmaths.Text = ds.Tables[0].Rows[0]["Maths"].ToString();
txtscience.Text = ds.Tables[0].Rows[0]["Science"].ToString();
txtsocialscience.Text = ds.Tables[0].Rows[0]"SocialScience"].ToString();
}
}
When I enter student id and search, it showed student marks in textboxes.
After that I want to edit and update.
For that I have added above code, and here its stored procedure:
ALTER PROCEDURE sp_searchupdate
(
#id int,
#Tamil varchar(50),
#English varchar(50),
#Maths varchar(50),
#Science varchar(50),
#SocialScience varchar(50)
)
AS
IF EXISTS (SELECT * FROM studentresult WHERE id_student='#id')
begin
SELECT id_student FROM studentresult WHERE id_student='#id'
END
When I edit marks, and hit update button, it shows error.
Here is my output screenshot
May I know, what my mistake in the code, I'm new to .net.
Can anyone help me?
update:
ALTER PROCEDURE sp_searchupdate
(
#id int,
#Tamil Varchar (100),
#English varchar (50),
#Maths Varchar (50),
#Science Varchar (50),
#SocialScience Varchar (50)
)
AS
IF EXISTS (SELECT * FROM studentresult WHERE id_student=#id)
BEGIN
UPDATE studentresult SET Tamil = #Tamil,English = #English, Maths = #Maths,Science = #Science,SocialScience = #SocialScience WHERE id = #id
END
Since your SP expects parameters.
You have to pass the expected parameters to the stored procedure from C#. Like below,
btnSearch_Click
com.Parameters.AddWithValue("#Tamil", "");
com.Parameters.AddWithValue("#English", "");
com.Parameters.AddWithValue("#Maths", "");
com.Parameters.AddWithValue("#Science", "");
com.Parameters.AddWithValue("#SocialScience", "");
btnUpdate_Click
// Add your Parameter
cmd.Parameters.AddWithValue("#id", txtstudentid.Text);
cmd.Parameters.AddWithValue("#tamil", txttamil.Text);
cmd.Parameters.AddWithValue("#english", txtenglish.Text);
cmd.Parameters.AddWithValue("#math", txtmaths.Text);
cmd.Parameters.AddWithValue("#science", txtscience.Text);
cmd.Parameters.AddWithValue("#socialScience", txtsocialscience.Text);
First at all you must understand how your current code work...
Let's say when you click searchupdate this should do what kind of job?
Now base on your code now is actually setting the TextBox to the Database value you retrieve.
Is this what you expect ? Answer is NO
I have separated to 2 button due to your function 1 is for search and another 1 is for update....
// This is First Button
protected void btnSearch_Click(object sender, EventArgs e)
{
// What does theis code here do ??
SqlConnection con = Connection.DBconnection();
{
SqlCommand com = new SqlCommand("PROCEDURE_SELECT", con);
com.CommandType = CommandType.StoredProcedure;
com.Parameters.AddWithValue("#id", txtstudentid.Text.Trim());
SqlDataAdapter adp = new SqlDataAdapter(com);
DataSet ds = new DataSet();
adp.Fill(ds);
// SEt value to TextBox & make sure your value below is not Null else it will throw you null exception due to you use .ToString()
txttamil.Text = ds.Tables[0].Rows[0]["Tamil"].ToString();
txtenglish.Text = ds.Tables[0].Rows[0]["English"].ToString();
txtmaths.Text = ds.Tables[0].Rows[0]["Maths"].ToString();
txtscience.Text = ds.Tables[0].Rows[0]["Science"].ToString();
txtsocialscience.Text = ds.Tables[0].Rows[0]["SocialScience"].ToString();
}
}
// This is second Button
protected void btnUpdate_Click(object sender, EventArgs e)
{
using (SqlConnection con = Connection.DBconnection())
{
using (SqlCommand cmd = new SqlCommand("PROCEDURE_UPDATE", con))
{
cmd.CommandType = CommandType.StoredProcedure;
// Add your Parameter
cmd.Parameters.AddWithValue("#id", txtstudentid.Text.Trim());
cmd.Parameters.AddWithValue("#tamil", txttamil.Text.Trim());
cmd.Parameters.AddWithValue("#english", txtenglish.Text.Trim());
cmd.Parameters.AddWithValue("#math", txtmaths.Text.Trim());
cmd.Parameters.AddWithValue("#science", txtscience.Text.Trim());
cmd.Parameters.AddWithValue("#socialScience", txtsocialscience.Text.Trim());
con.Open();
// Execute your Query
cmd.ExecuteNonQuery();
// Clear All The Data in Current TextBOx and press search again with the ID
txttamil.Text = string.Empty;
txtenglish.Text = string.Empty;
txtmaths.Text = string.Empty;
txtscience.Text = string.Empty;
txtsocialscience.Text = string.Empty;
Response.Write("You have updated the value... Try to Search again...");
}
// UPDATE Query as per below
// IF EXISTS (SELECT * FROM studentresult WHERE id_student='#id')
// BEGIN
// UPDATE TABLE SET tamil = #tamil and so on... WHERE id = #id
// END
// ELSE
// BEGIN
// INSERT DATA HERE
// END
}
}
SQL Proc
CREATE PROCEDURE proc_Upd_Student
-- Add the parameters for the stored procedure here
#id VARCHAR(100),
#tamil VARCHAR(100),
#english VARCHAR(100),
#maths VARCHAR(100),
#science VARCHAR(100),
#socialscience VARCHAR(100)
AS
BEGIN
-- CHECK IF THE TABLE CONTAIN THE SAME ID
IF (SELECT COUNT(1) FROM TABLE WHERE id = #id) > 0
BEGIN
-- I DO UPDATE TO THE TABLE
UPDATE TABLE
SET Column = VALUE
WHERE id = #id
END
ELSE
BEGIN
-- IF A NEW ID ? THEN I DO INSERT, IF YOUR ID IS IDENTITY THEN YOU CAN SKIP IT NO NEED TO INSERT
INSERT INTO TABLE (COLUMN)
VALUE
(PARAM)
END
END
The store procedure above is for Update & Insert... Your Select proc can remain due to is different procedure. If you want to combine you add one more parameter called #Action

How to pass parameter for sproc via C#

I have a MVC application that runs the following sproc named sp_GetEmployeeByID:
#ID int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT #ID, *
from tblEmployee
where ID = #ID
and the method that calls this needs to pass the int parameter however I cant seem to figure this out, here is what i have so far:
public Employee GetSingleEmployee(int ID)
{
string connectionString = ConfigurationManager.ConnectionStrings["KVKDb"].ConnectionString;
Employee emp = new Employee();
using (SqlConnection connect = new SqlConnection(connectionString))
{
SqlCommand sprocCmd = new SqlCommand("sp_GetEmployeeByID " + ID, connect); sprocCmd.CommandType = System.Data.CommandType.StoredProcedure;
connect.Open();
SqlDataReader rdr = sprocCmd.ExecuteReader();
while (rdr.Read() == true)
{
Employee employee = new Employee();
employee.ID = Convert.ToInt32(rdr["ID"]);
employee.City = rdr["City"].ToString();
employee.DateOfBirth = Convert.ToDateTime(rdr["DateOfBirth"]);
employee.Gender = rdr["Gender"].ToString();
employee.Name = rdr["Name"].ToString();
emp = employee;
}
}
return emp;
}
The obvious issue is that there is no sproc named sp_GetEmployeeByID int ID. I want to know how to call that sproc and pass a parameter for the sprocs #ID parameter.
Add a Parameter to the command:
SqlCommand sprocCmd = new SqlCommand("sp_GetEmployeeByID");
sprocCmd.CommandType = System.Data.CommandType.StoredProcedure;
sprocCmd.Parameters.AddWithValue("#ID", ID)

How can I determine if column of a stored procedure result can be null

I'm writing a code generator and am getting stuck on determining the nullable status of a stored procedure result set Column. I can query the DataType just fine but neither the datareader object nor a data table column contain the correct nullable value of my column.
public List<DataColumn> GetColumnInfoFromStoredProcResult(string schema, string storedProcName)
{
//build sql text
var sb = new StringBuilder();
sb.Append("SET FMTONLY OFF; SET FMTONLY ON; \n");//this is how EF4.1 did so I copied..not sure why the repeat
sb.Append(String.Format("exec {0}.{1} ", schema, storedProcName));
var prms = GetStoredProcedureParameters(schema: schema, sprocName: storedProcName);
var count = 1;
foreach (var param in prms)
{
sb.Append(String.Format("{0}=null", param.Name));
if (count < prms.Count)
{
sb.Append(", ");
}
count++;
}
sb.Append("\n SET FMTONLY OFF; SET FMTONLY OFF;");
var dataTable = new DataTable();
//var list = new List<DataColumn>();
using (var sqlConnection = this.SqlConnection)
{
using (var sqlAdapter = new SqlDataAdapter(sb.ToString(), sqlConnection))
{
if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
sqlAdapter.SelectCommand.ExecuteReader(CommandBehavior.KeyInfo);
sqlConnection.Close();
sqlAdapter.Fill(dataTable);
}
//using (var sqlCommand = new SqlCommand())
//{
// sqlCommand.CommandText = sb.ToString();
// sqlCommand.CommandType = CommandType.Text;
// sqlCommand.Connection = sqlConnection;
// if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
// var dr = sqlCommand.ExecuteReader(CommandBehavior.SchemaOnly);
// var whateva = dr.GetSchemaTable();
// foreach (DataColumn col in whateva.Columns)
// {
// list.Add(col);
// }
//}
}
var list = dataTable.Columns.Cast<DataColumn>().ToList();
return list;
}
I'm trying to end up with something similar to the the Entities Framework creation of a complex type from a stored procedure. Can I hijack that functionality?
On this example the Id column.. tblJobId (not my naming convention) would never be null.. But I selected null as ImNull and it has all the same properties so how does EF determine if the corresponding C# data type should be nullable or not?
Has anybody done this..
Ideas are appreciated.
The secret was to use Schema Only and fill a dataset not datatable. Now the AllowDbNull property on the datacolumn properly displays the nullable status of the return value.
This was it...
public List<DataColumn> GetColumnInfoFromStoredProcResult(string schema, string storedProcName)
{
//build sql text
var sb = new StringBuilder();
sb.Append("SET FMTONLY OFF; SET FMTONLY ON; \n");//this is how EF4.1 did so I copied..not sure why the repeat
sb.Append(String.Format("exec {0}.{1} ", schema, storedProcName));
var prms = GetStoredProcedureParameters(schema: schema, sprocName: storedProcName);
var count = 1;
foreach (var param in prms)
{
sb.Append(String.Format("{0}=null", param.Name));
if (count < prms.Count)
{
sb.Append(", ");
}
count++;
}
sb.Append("\n SET FMTONLY OFF; SET FMTONLY OFF;");
var ds = new DataSet();
using (var sqlConnection = this.SqlConnection)
{
using (var sqlAdapter = new SqlDataAdapter(sb.ToString(), sqlConnection))
{
if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
sqlAdapter.SelectCommand.ExecuteReader(CommandBehavior.SchemaOnly);
sqlConnection.Close();
sqlAdapter.FillSchema(ds, SchemaType.Source, "MyTable");
}
}
var list = ds.Tables[0].Columns.Cast<DataColumn>().ToList();
return list;
}
public List<SqlParamInfo> GetStoredProcedureParameters(string schema, string sprocName)
{
var sqlText = String.Format(
#"SELECT
[Name] = N'#RETURN_VALUE',
[ID] = 0,
[Direction] = 6,
[UserType] = NULL,
[SystemType] = N'int',
[Size] = 4,
[Precision] = 10,
[Scale] = 0
WHERE
OBJECTPROPERTY(OBJECT_ID(N'{0}.{1}'), 'IsProcedure') = 1
UNION
SELECT
[Name] = CASE WHEN p.name <> '' THEN p.name ELSE '#RETURN_VALUE' END,
[ID] = p.parameter_id,
[Direction] = CASE WHEN p.is_output = 0 THEN 1 WHEN p.parameter_id > 0 AND p.is_output = 1 THEN 3 ELSE 6 END,
[UserType] = CASE WHEN ut.is_assembly_type = 1 THEN SCHEMA_NAME(ut.schema_id) + '.' + ut.name ELSE NULL END,
[SystemType] = CASE WHEN ut.is_assembly_type = 0 AND ut.user_type_id = ut.system_type_id THEN ut.name WHEN ut.is_user_defined = 1 OR ut.is_assembly_type = 0 THEN st.name WHEN ut.is_table_type =1 Then 'STRUCTURED' ELSE 'UDT' END,
[Size] = CONVERT(int, CASE WHEN st.name IN (N'text', N'ntext', N'image') AND p.max_length = 16 THEN -1 WHEN st.name IN (N'nchar', N'nvarchar', N'sysname') AND p.max_length >= 0 THEN p.max_length/2 ELSE p.max_length END),
[Precision] = p.precision,
[Scale] = p.scale
FROM
sys.all_parameters p
INNER JOIN sys.types ut ON p.user_type_id = ut.user_type_id
LEFT OUTER JOIN sys.types st ON ut.system_type_id = st.user_type_id AND ut.system_type_id = st.system_type_id
WHERE
object_id = OBJECT_ID(N'{0}.{1}')
ORDER BY 2", schema, sprocName);
using (var sqlConnection = this.SqlConnection)
{
using (var sqlCommand = new SqlCommand())
{
if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open();
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandType = CommandType.Text;
sqlCommand.CommandText = sqlText;
var dr = sqlCommand.ExecuteReader();
var result = new List<SqlParamInfo>();
while (dr.Read())
{
if (Convert.ToString(dr["Name"]) != "#RETURN_VALUE")
{
result.Add(new SqlParamInfo(dr));
}
}
return result;
}
}
}
Assume, that every column which comes from SP can be null - this is a valid assumption because stored procedure - its a kind of data abstraction layer and thus its code can change but still produce valid results.
If column was non-nullable yesterday it means nothing for today. So - all the columns which come from SP resultsets are nullable by design.
Update.
Assuming that table t1 has column Id INT IDENTITY PRIMARY KEY
Your stored proc looks like this:
CREATE PROC p1
AS
BEGIN
SELECT Id FROM t1
END
So it will never return an Id = NULL, but this is the SP - an abstraction of data, so - tomorrow i'll modify it like this:
CREATE PROC p1
AS
BEGIN
SELECT Id FROM t1
UNION
SELECT NULL
END
So, now it returns NULL - think about this. The difference in understanding of data abstraction

SQL Server 2008 R2 stored procedure doesn't work (visual c#)

I have searched and tried different things for the past week or so and my problem is possibly to specific to find an answer through google.
If I execute this query in SQL Server Management Studio and replace the parameter #zoekterm with '%something%', it works fine and returns the result that I want. But when I call the same procedure from C# it returns nothing.
Is this a bug or am I just that stupid?
Here's code of stored procedure and function in C#, (I know I should have used switch case...)
Stored procedure:
-- =============================================
-- Author: Daan
-- Create date:
-- Description:
-- =============================================
ALTER PROCEDURE [dbo].[quick_bedrijf]
-- Add the parameters for the stored procedure here
#zoekterm varchar(100) = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT bedrijf.bedrijf_nr, bedrijf.zoeknaam, bedrijf.plaats
FROM bedrijf
WHERE zoeknaam LIKE #zoekterm AND NIETactief = 0
ORDER BY bedrijf.zoeknaam, bedrijf.plaats
END
C#:
private void snel_zoek2()
{
listView1.Items.Clear();
con.Open();
if (type == 1)
{
command1 = new SqlCommand("quick_project", con);
colnum = 5;
}
else if (type == 2)
{
command1 = new SqlCommand("quick_bedrijf", con);
colnum = 3;
}
else if (type == 3)
{
command1 = new SqlCommand("quick_persoon", con);
colnum = 4;
}
command1.CommandType = CommandType.StoredProcedure;
SqlParameter zoekterm = command1.Parameters.Add("#zoekterm", SqlDbType.VarChar, 100);
zoekterm.Direction = ParameterDirection.Input;
//command1.Parameters.Add(new SqlParameter("#zoekterm", SqlDbType.VarChar)).Value = " '%zee%'";// + textBox2.Text.ToString()+
zoekterm.Value = "'%"+textBox2.Text.ToString()+"%'";
// MessageBox.Show(zoekterm.Value.ToString());
SqlDataAdapter adapt = new SqlDataAdapter();
DataTable dt = new DataTable();
adapt.SelectCommand = command1;
adapt.Fill(dt);
dataGridView1.BindingContext = new BindingContext();
dataGridView1.DataSource = dt;
con.Close();
}
You don't put the quotes into the parameter (those are to signify a literal); it should be:
zoekterm.Value = "%"+textBox2.Text+"%";
It is failing currently because if the text is "abc", it is looking for a string that starts and ends with a single quote and includes "abc". In SQL terms, you were asking it for:
LIKE '''%abc%'''

Categories

Resources