Stored Procedure is not updating database C# - c#

This is my first time posting, so please let me know if I made a mistake or if my question is poorly worded. I am currently working on an utility app that allows data from several different csv files to be uploaded into a database for reporting purposes.The App takes the files, combines them, and removes duplicates. Once the database is generated any new file can be uploaded to update the database or insert new records.
I looked around and I couldn't find a solution for my problem. At the moment I am having trouble updating the database with new data.
For some reason, whenever I try to update the data using the following stored procedure, none of the changes are saved in the database.
CREATE PROCEDURE [dbo].[UpdateDeviceReport]
#SerialNumber AS VARCHAR(32),
#DeviceType AS VARCHAR(8),
#InstalledOn AS VARCHAR(32),
#PortStatus AS CHAR(6)
AS
BEGIN
UPDATE computer
SET deviceType = #DeviceType, deviceInstalledOn = #InstalledOn
WHERE serialID = #SerialNumber
UPDATE computer_localblocker
SET portStatus = #PortStatus
WHERE computer_serialid = #SerialNumber;
END
GO
Here is the C# code that executes the procedure.
public void updateOrgDevices()
{
using (var conn = new SqlConnection(PopulateDatabase.connectionString))
{
int debug = 0;
conn.Open();
foreach (KeyValuePair<string, DictionaryObjs> element in GlobalVariables.UpdateData)
{
OrgDevRepObj obj = element.Value as OrgDevRepObj;
SqlCommand sqlCommand = new SqlCommand("UpdateDeviceReport", conn);
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.Add("#SerialNumber", SqlDbType.VarChar, 32).Value = obj.SerialNumber.Trim().ToUpper();
sqlCommand.Parameters.Add("#DeviceType", SqlDbType.VarChar, 8).Value = obj.DeviceType;
sqlCommand.Parameters.Add("#InstalledOn", SqlDbType.VarChar, 32).Value = obj.InstalledOn;
sqlCommand.Parameters.Add("#PortStatus", SqlDbType.Char, 6).Value = obj.PortStatus;
debug = sqlCommand.ExecuteNonQuery();
}
}
}
The parameters come from a dictionary/hashmap, GlobalVariables.UpdateData, that stores data rows as objects where the key is the serial number from that row of data(e.g. Dictionary<string[serial Number], DictionaryObjs>) and the values of the fields for each data row are stored as strings, in a class called DictionaryObjs. This is the parent class for all record types. In this case the record type is OrgDevRepObj.
I was able to get the code to work once, but I haven't been able to replicate it. It doesn't throw any errors, but I know its not working because sqlCommand.ExecuteNonQuery(); returns a -1 for each dictionary key value pair. I've also tried using
sqlCommand.Parameters.AddWithValue('#Param', [value]);
Any ideas?

Could you possibly have set NOCOUNT ON globally? See msdn.microsoft.com/en-us/library/ms176031.aspx
Try adding SET NOCOUNT OFF to your stored procedure.

An idea to test according to your situation, create a table called test with a single varchar type field and inside the procedure you make an insert to that table by inserting the SerialNumber. This way you can tell if it is filtering by the correct serial and if the procedure is executed. Luckā€¦

Related

C# + Reading output result from stored procedure + merge into + OUTPUT

I am trying to upload an Excel file in C# to SQL Server database table.
The table looks like this:
Companies
ID(PK) Int AutoIncrement
CompanyName Varchar(256)
Logo Varchar(256)
WebsiteURL Varchar(256)
Description Varchar(256)
I have read the Excel into a DataTable object and passed it to a stored procedure. The stored procedure uses MERGE INTO to insert new records and update existing.
I need to know how many records are inserted and how many are updated.
For this, I used OUTPUT like this:
CREATE PROCEDURE [dbo].[Update_Companies]
#tblCompanies CompanyType READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #summary CompanySummaryType;
MERGE INTO companies c1
USING #tblCompanies c2 ON c1.CompanyName = c2.CompanyName
WHEN MATCHED THEN
UPDATE
SET c1.Logo = c2.Logo,
c1.WebsiteURL = c2.WebsiteURL,
c1.CompanyDescription = c2.CompanyDescription,
WHEN NOT MATCHED THEN
INSERT (CompanyName, Logo, WebsiteURL, Description)
VALUES (c2.CompanyName, c2.Logo, c2.WebsiteURL, c2.Description)
OUTPUT
$action as ActionType,
INSERTED.CompanyName as CompanyName INTO #summary;
END
CompanyType is a user-defined table type containing table columns
CompanySummaryType is a user-defined table type containing two columns:
ActionType Varchar(256),
CompanyName Varchar(256)
The code runs fine and insert or update working perfectly.
I want to read the #summary variable back in my C# code.
Right now, I am using ExecuteNonQuery to execute stored procedure like this:
private void AddRecords(DataTable dataTable)
{
string constr = ConfigurationManager.ConnectionStrings["CMSConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand("Update_Companies"))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
cmd.Parameters.AddWithValue("#tblCompanies", dataTable);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
Also, is there a way by which I can find how many records failed to insert or update?
You can make your #Summary variable an OUTPUT parameter of the procedure.
In your C# code, you can then compare the returned OUTPUT parameter, with the original input parameter, to see which rows were not inserted or updated.

how to get the value of certain selected value out from Stored Procedure

i just got started with ASP.Net, i only knew PHP so which means im a little stubborn. i need real examples.
heres my problem, i got this class in a class library called Class1, it connects to the database and ask for verification of user login.
public string userlogin(string loginuser,string loginpass)
{
string type = null;
myCon.Open();
SqlCommand logincheck = new SqlCommand("CheckID", myCon);
logincheck.CommandType = CommandType.StoredProcedure;
logincheck.Parameters.Add("#userid", SqlDbType.NVarChar).Value = loginuser;
logincheck.Parameters.Add("#password", SqlDbType.NVarChar).Value = loginpass;
SqlDataReader dr;
dr = logincheck.ExecuteReader();
while (dr.Read())
{
type = //here i want to get the value of type in my database
break;
}
myCon.Close();
return type;
}
here's my stored procedure
ALTER PROCEDURE dbo.logincheck
#userid nchar(10),
#password nchar(20)
AS
Select * from users Where userid = #userid and password = #password
RETURN
i need a set of examples please.
Without knowing how your users table is structured, the following is a guess:
while (dr.Read()) {
...
}
should be changed to:
if (dr.Read()) {
type = dr["type"].ToString();
}
A couple of recommendations.
Use using clauses around your connection and command objects. This will save you a lot of pain later. See: Calling stored procedure with return value for an example. And SqlConnection SqlCommand SqlDataReader IDisposable for the reasons why. Hint: if the code as you have it now was released into production it is highly likely you will begin to see random database related exceptions start popping up in various places. So this is pretty important.
The name of the proc in your SqlCommand ("checkid") doesn't match the actual name of your stored procedure ("logincheck"). Change one of them. What you have right now will result in a sql error when executed.
Consider changing the name of the variable type. Type is a class in the System namespace and the way that reads is a bit confusing. Maybe accountType, loginType, userType or something similar. You can certainly leave it as type; just people following you will question it.
Change your select statement to actually name the columns you want back. As it stands it's brittle. See: What is the reason not to use select *?
I used an if statement instead of a while because you really only want the first row.
Assuming "UserType" is the column you are looking for (can't tell because you are using a Select *) that line would be
type = dr["UserType"] as string

C# ComboBox Select Error: "Incorrect Syntax Near '.'"

Note: my office doesn't allow me to view YouTube and several other sites that probably have the answer to this question on them (they are blocked), which is why Googling the answer hasn't yielded results.
ComboBox code reference: found here
On my C# Form, I have filled a ComboBox with tables from a database (see below code), which returns the appropriate values and functions correctly:
public Form1()
{
InitializeComponent();
// Connection
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "CONNECTION STRING" // shortened for security and convenience
// Fill ComboBox with SQL Values
conn.Open();
SqlCommand cmbTables = new SqlCommand("SELECT name FROM sys.tables", conn);
SqlDataReader read = cmbTables.ExecuteReader();
DataTable cmbData = new DataTable();
cmbData.Columns.Add("name", typeof(string));
cmbData.Load(read);
cmb1.DisplayMember = "name";
cmb1.DataSource = cmbData;
conn.Close();
}
After the ComboBox loads the tables (which works), the application then selects a table and clicks a button that loads the table, which is selected. This is where the code errors:
private void button1_Click(object sender, EventArgs e)
{
using (var connection = Utilities.GetConnection())
{
string table = Convert.ToString(txt1.Text);
string cmb1Value = Convert.ToString(cmb1.SelectedItem);
// Stored Procedure
SqlCommand select = new SqlCommand("EXECUTE STOREDPROCEDURE" + cmb1Value, connection); // shortened for security and convenience
select.Parameters.Add(new SqlParameter(cmb1Value, table));
// Data View
SqlDataAdapter ad= new SqlDataAdapter(select);
ad.SelectCommand = select;
DataTable dt = new DataTable();
ad.Fill(dt); // this generates the error "Incorrect Syntax Near '.'"
BindingSource b = new BindingSource();
b.DataSource = dt;
dGrid.DataSource = b;
ad.Update(dt);
connection.Close();
}
}
Even though the ComboBox loads the appropriate values, from the above code, I may be missing something which attaches those values to the SELECT stored procedure (all it does is call SELECT statement through a variable passed to it). The error, "Incorrect Syntax Near '.'" looks like a SQL Server error that I've seen, but can't remember how I generate it (this is how I usually troubeshoot where the TSQL code went wrong).\
Stored Procedure Related code:
C#:
SqlCommand select = new SqlCommand("EXECUTE STOREDPROCEDURE " + cmb1Value, connection);
TSQL:
CREATE PROCEDURE [STOREDPROCEDURE]
#TableName VARCHAR(250)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX)
SET #sql = N'SELECT TOP 100 *
FROM ' + #TableName
EXECUTE(#sql)
END
-- Note this works in SSMS without a problem.
The above code is incorrect, and when I tweak the TSQL code, I generate similar errors, telling me that somewhere I am missing a conversion, or another variable because SQL Server isn't seeing these table values returned by the SELECT (first block of code). I can ascertain this because I have a second ComboBox that uses similar code EXCEPT that I populated the ComboBox with manual values, and it connects to the tables in the database with no error. So, the ComboBox, which grabs values from the database, that you see above, does not function correctly.
For instance, if I only add the below line of code to the code, I receive an error that it can't find the database "EXECUTE STOREDPROCEDURE System'
select.CommandType = CommandType.StoredProcedure;
However, System isn't a part of anything, so where did that come from? It never errored with this code on the manual ComboBox, as it had no trouble finding the database (using the same connection string, server and database!).
If I try to use a TSQL parameter, such as:
SqlCommand select = new SqlCommand("EXECUTE stp_ReturnTable #p", scon);
select.Parameters.Add(new SqlParameter("#p", cmb1Value));
Suddenly, it can't find the stored procedure. Again, the connection strings are identical for the manual ComboBox and the dynamic ComboBox.
I think the code behind the dynamic ComboBox is wrong. When I'm out of the office, I'll review some videos with detailed demonstrations on how to create a dynamic ComboBox from a database and I have a hunch that a system object is in the way (based on the System error, which exists nowhere in my code, as well as it suddenly being unable to find the database or procedure).
The missing key point in your code is the CommandType.
Without the proper set of this property the default is CommandText and thus the Framework expects a statement that starts with SELECT/INSERT/UPDATE/DELETE etc....
using (var connection = Utilities.GetConnection())
{
string table = Convert.ToString(txt1.Text);
string cmb1Value = Convert.ToString(cmb1.SelectedItem);
// Stored Procedure
SqlCommand select = new SqlCommand("STOREDPROCEDURE", connection);
select.Parameters.Add(new SqlParameter("#TableName", cmb1Value));
// That's the key to let ADO.NET accept the previous CommandText as valid.
// If you omit this the CommandText is assumed to be a SELECT/UPDATE/DELETE etc..
select.CommandType = CommandType.StoredProcedure;
// Data View
SqlDataAdapter ad= new SqlDataAdapter(select);
DataTable dt = new DataTable();
ad.Fill(dt);
BindingSource b = new BindingSource();
b.DataSource = dt;
dGrid.DataSource = b;
}
EDIT Having seen the code of the SP then you could simply set the SqlParameter name to the constant #TableName and pass the value extracted from the combobox as the value to be used inside the SP
EDIT I have looked again at your code and I suspect that the culprit is the line
string cmb1Value = Convert.ToString(cmb1.SelectedItem);
Looking at how you have filled your combo, this line, doesn't return the tablename as you expect, but the generic string System.Data.DataRowView because the DataSource of the combo is a DataTable and not a string collection. You should try to change that line in this way
DataRowView rw = cmb1.SelectedItem as DataRowView;
if(rw != null)
{
string cmbValue1 = rw["name"].ToString();
....
And yes, your code should work also without the CommandType.StoredProcedure line because the text EXECUTE sp param is recognized as a valid sql commandtext (but why do you use it when a direct call to the storedprocedure could be optimized for reuse?)

calling a stored procedure from C# using SqlDataAdapter

I have a stored procedure which has been well tested and works perfectly from SQL Server Management Studio. All the procedure does is check for the existence of a record in a table, return it if it exists, or create it and then return it if it doesn't.
The procedure looks like this:
CREATE proc [dbo].[spInsertSerialBatch]
#MOS_JOB varchar(12), --PASSED COMMAND LINE
#MOS_LOT varchar(4) = NULL, --PASSED COMMAND LINE
#MES_USER varchar(12) = 'NOT PASSED',--PASSED COMMAND LINE
#COMPUTERNAME varchar(100) = 'NOT PASSED' --ENVIRONMENT VARIABLE
as ....
I use a SqlDataAdapter, which I have used repeatedly without any problems. The setup looks like this:
using (SqlCommand sqlComm = new SqlCommand("dbo.spInsertSerialBatch", serialBatchDataConnection))
{
if (serialBatchDataConnection.State != ConnectionState.Open)
{
serialBatchDataConnection.Open();
}
sqlComm.CommandType = CommandType.StoredProcedure;
sqlComm.Parameters.AddWithValue("#MOS_JOB", options.jobNumber);
sqlComm.Parameters.AddWithValue("#MOS_LOT", options.lotNumber);
sqlComm.Parameters.AddWithValue("#MES_USER", options.userId);
sqlComm.Parameters.AddWithValue("#COMPUTERNAME", System.Environment.MachineName);
SqlDataAdapter sda = new SqlDataAdapter(sqlComm);
DataTable dt = new DataTable();
int rowsAffected = sda.Fill(dt);
}
I then examine the results of the table after Fill is executed. It works fine when the row exists in the table, but if it doesn't, and the stored proc needs to generate it, Fill returns 0 rows and the data table remains empty. No errors/exceptions are thrown, I just get no results.
I suppose I could change the code to use ExecuteNonQuery and not use the DataAdapter, but I see no reason why this shouldn't just work; I prefer having a data table (which may result in more than a single row in some cases) than using a data reader and looping over the data to get the results.
Any suggestions as to why this might fail? I've looked over several posts on this and other sites that are similar, but haven't found a satisfactory answer. Any suggestions are greatly appreciated.
Thanks,
Gary
The entrire sp is quite large and probably too proprietary to publish...
--return inserted rows
SELECT 'CREATED' as [spRESULT], o.*
FROM #output o
END
/*
* Return existing SerialBatch(s)
*/
BEGIN
SELECT 'RETRIEVED' as [spRESULT], s.*
FROM SerialBatch s
WHERE SerialBatchId = #formattedId
UNION
/*
* pull in products that have components that need labels as well
*/
SELECT 'RETRIEVED' as [spRESULT],s.*
FROM SerialBatch s
WHERE SerialBatchParentId = #formattedId
END
This is the end of the stored procedure. I tried executing a DataReader instead and the result is the same...I get no results for the case when the sp has to create it, but again it runs perfectly stand-alone in SQL Server Management Studio.
Problem solved. Turns out that the OpenQuery string passed to Oracle was converting an empty string to a NULL and preventing the new row from being returned. All I need to add was a check for both NULL and empty string:
if #MOS_LOT IS NULL or #MOS_LOT = ''
set #MOS_LOT = ' ' --EMPTY STRINGS BEING EQUIVALENT TO NULLS

Why does IDataReader lose a row?

I am facing this problem. I have a stored procedure which returns 6 rows when I execute it.
But when I am retrieving the rows in my app by using ExecuteReader, it only returns only 5 rows. Why is it losing a row??
My stored procedure consists of 5 union statements which are getting filled from a single table:
dbase.AddInParameter(cmd, "#LoginUser", DbType.String, UserID);
try
{
using (IDataReader dr = dbase.ExecuteReader(cmd))
if (dr.Read())
{
dt = new DataTable("DashBoard");
dt.Load(dr);
}
}
dbase is my database object. And cmd is the SqlCommand used to call the stored procedure.
UserID is parameter is passing
Stored procedure code is:
ALTER PROCEDURE [dbo].[USP_ViewAdminDashBoard](#LoginUser varchar(75))
-- Add the parameters for the stored procedure here
AS
BEGIN
SET NOCOUNT ON;
SET DATEFORMAT DMY;
DECLARE #LastLoginDate as DateTime
Select #LastLoginDate = dbo.UDF_GetLastLoginByUser(#LoginUser)
Select 'Last Login Date', convert(varchar(12),#LastLoginDate,105)
Union
Select 'Nos. Records pending for Upload' as Title, convert(varchar(5),COUNT(s.BatchID)) Total from dbo.BREGISTRATIONENTRY s, Dbo.TBL_iBATCH B
where B.BatchID = s.BatchID And b.Forwarded = 0 and b.isBatchClosed = 1
END
Your first dr.Read is advancing to the first row. The DataTable.Load is reading the remaining rows but not the first row
Use HasRows to test for existence and don't use Read before the DataTable.Load
Edit:
Just load the DataTable without testing first: then test on the DataTable. There is no HasRows in the IDataReader interface.
IDataReader is a forward reader,it means when you read a row,that row will be deleted from the reader and you can't get it from the reader any more.
joshua i also has this problem while using enterprise library more than two time even i use same code written down but both time i found a problem in my store procedure there must be any wrong selection in query or any think else which db server does not detect and i solve it and
my IDataReader select all rows and also use same way as suggest by 'gbn'
using (IDataReader dr = oDb.ExecuteReader(p_oDbCommand))
{
if (dr != null)
{
ds.Tables[0].Load(dr);
}
}
return dt;

Categories

Resources