There are so many questions on SO on this exception. But none of them are useful to me.
Here is my Stored Procedure :
CREATE PROCEDURE HolidayStandardMappingInsert
#HolidayID bigint,
#StandatdID smallint
AS
BEGIN
INSERT INTO HolidayStandardMapping VALUES(#HolidayID,#StandatdID)
END
GO
And here is my Code:
int StdId = 0;
SqlCommand cmdS = new SqlCommand("HolidayStandardMappingInsert", conn);
cmdS.CommandType = CommandType.StoredProcedure;
for (int i = 0; i < cblStandard.Items.Count; i++)
{
if (cblStandard.Items[i].Selected == true)
{
if (StdId == 0)
StdId = Convert.ToInt32(cblStandard.Items[i].Value);
else
StdId = Convert.ToInt32(cblStandard.Items[i].Value);
cmdS.Parameters.AddWithValue("#HolidayID", NewRecordID);
cmdS.Parameters.AddWithValue("#StandatdID", StdId);
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn.Open();
int res = cmdS.ExecuteNonQuery();
if (res > 0)
{
}
}
}
Tell me what is missing?
You are using same SqlCommnad object for multiple insertions so previously added parameters are also present.
So either create a new SqlCommnad object inside loop or clear prevoius parameters.
This is how you can Clear Previously added parameters.
cmdS.Parameters.Clear();
You are adding parameters in a loop.
So after second iteration, your command has 4 parameters.
Each time You are adding cmdS.Parameters.AddWithValue in a loop. So after the first iteration, it has already 2 parameters.
You need to clear the command parameters by cmdS.Parameters.Clear() before entering the loop.
Related
I seem to be having problems with my method that will return an Integer. I am attempting to modify the rows of a particular column with this returning Integer. The database will update the pre-existing column values with this new returned value. However, it appears that every row is being modified to the LAST row's value, regardless of what the specific row held previously. I am sure my code is just overwriting the variable, but I am wondering where. Here is my method; would appreciate feedback.
private int extractValue()
{
if (connection.State != ConnectionState.Open)
{
this.connection.Open();
}
ParsingHelper helper = null // different class - no issues with this.
String query = "SELECT device FROM dLogger";
OdbcCommand command = new OdbcCommand(query, this.connection);
List<Int32> list = new List<Int32>();
OdbcDataReader reader = null;
reader = command.ExecuteReader();
while (reader.Read())
{
list.Add(reader.GetInt32(0));
for (int i = 0; i < reader.FieldCount; i++)
{
helper = new ParsingHelper();
helper.assemble(list[i]);
}
}
return helper.getFirst();
}
No problem with the ParsingHelper here, it does the correct work. My problem is the overwriting. I thought a List would alleviate this issue but I am missing something, evidently.
EDIT: Would this approach work better?
while(reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
list.Add(reader.GetInt32(i));
//....
}
If my table originally looked like this:
ColA
1
2
3
4
And my function, for example, multiplied each number by 2. The new column would look like
ColA
8 // rather than 2
8 // rather than 4
8 // rather than 6
8 // 8 is the last value - therefore, correct.
So you see, I am running into some overwriting issues here. It appears the reader will read effectively and to the last row but it is not modifying values correctly, it is only assigning each value to the last value.
EDIT:
Here is where I am updating my database:
private void update()
{
String query = "UPDATE dLogger SET device = ?";
OdbcCommand command = new OdbcCommand(query, this.connection);
if (this.connection.State != ConnectionState.Open)
{
this.connection.Open();
}
command.Parameters.AddWithValue("?", extractValue());
}
Also, here is my simple Parsing Helper Class assemble()
private void assemble(int value)
{
setFirst(value);
}
private void setFirst(int value)
{
value = value * 2;
}
Just change your
String query = "SELECT device FROM dLogger";
to
String query = "UPDATE dLogger SET device=device*2";
thus:
private void extractValue()
{
if (connection.State != ConnectionState.Open)
{
this.connection.Open();
}
String query = "UPDATE dLogger SET device=device*2";
OdbcCommand command = new OdbcCommand(query, this.connection);
command.Execute();
}
I've recently started learning some databases things in .NET (C#) and I have a problem with inserting list of objects into local database. I don't know the reason but, after executing query, base is still empty and there is no error or warning, can you tell me if there is any problem with the code, or is there any other reason why it does not work properly.
I've tried to debug, but code seems to work, it passes if statement, and adds parameter's value, I've also removed thread start method and did it synchronously but still nothing.
public static void SaveData()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
using (SqlConnection conn = new SqlConnection(conString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("INSERT INTO Przestoje(Urzadzenie, Czas, Data) VALUES(#nazwa, #czas, #data)", conn))
{
cmd.Parameters.AddWithValue("#nazwa", SqlDbType.NVarChar);
cmd.Parameters.AddWithValue("#czas", SqlDbType.Int);
cmd.Parameters.AddWithValue("#data", SqlDbType.NVarChar);
int count = allEncounters.Count;
for (int i = 0; i < count; i++)
{
if (i >= NextIndex)
{
cmd.Parameters["#nazwa"].Value = allEncounters[i].Name;
cmd.Parameters["#czas"].Value = allEncounters[i].timeOnTimeout * 10;
cmd.Parameters["#data"].Value = allEncounters[i].startDate.ToString();
}
}
NextIndex = count;
}
}
}).Start();
}
At some point you have to actually execute the SQL command, which you currently don't do:
cmd.ExecuteNonQuery();
If you're looking to insert one record, this would be near the end. Though it looks like you're trying to insert multiple records in a loop, so this would of course happen inside the loop. (Once per record.)
You have to execute the SqlCommand. Put this before "NextIndex = count;":
cmd.ExecuteNonQuery();
You forgot to run in using block:
cmd.ExecuteNonQuery();
You can trim that down
for (int i = NextIndex; i < allEncounters.Count; i++)
{
cmd.Parameters["#nazwa"].Value = allEncounters[i].Name;
cmd.Parameters["#czas"].Value = allEncounters[i].timeOnTimeout * 10;
cmd.Parameters["#data"].Value = allEncounters[i].startDate.ToString();
cmd.ExecuteNonQuery();
}
NextIndex = allEncounters.Count;
I'm trying to add data to my master/detail table by using a stored procedure.
I tried this:
private void ekle()
{
MySqlCommand cmd = new MySqlCommand("invinputfrompo", bag);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("?trnotes", MySqlDbType.Text).Value = tb1.Text;
for (int i = 0; i <= dg1.Rows.Count - 1; i++)
{
if (Convert.ToBoolean(dg1.Rows[i].Cells[0].Value) == true)
{
cmd.Parameters.Add("?docno", MySqlDbType.VarChar).Value = dg1.Rows[i].Cells[9].Value.ToString();
cmd.Parameters.Add("?idpodetails", MySqlDbType.Int32).Value = Convert.ToInt32(dg1.Rows[i].Cells[5].Value.ToString());
cmd.Parameters.Add("?idmat", MySqlDbType.Int32).Value = Convert.ToInt32(dg1.Rows[i].Cells[6].Value.ToString());
cmd.Parameters.Add("?sn", MySqlDbType.VarChar).Value = dg1.Rows[i].Cells[2].Value;
cmd.Parameters.Add("?qty", MySqlDbType.Decimal).Value = Convert.ToDecimal(dg1.Rows[i].Cells[1].Value);
//if (dg1.CurrentRow.Cells[4].Value.ToString() == "") { cmd.Parameters.Add("?shelflife", MySqlDbType.Date).Value = DBNull.Value; }
//else { cmd.Parameters.Add("?shelflife", MySqlDbType.Date).Value = dg1.CurrentRow.Cells[4].Value.ToString(); }
cmd.Parameters.Add("?shelflife", MySqlDbType.VarChar).Value = dg1.Rows[i].Cells[4].Value;
}
}
cmd.Connection = bag;
cmd.ExecuteNonQuery();
}
Only the first parameter "trnotes" will be added to the master table, other parameters will be added to the detail table.
I made loop like this but the way is not successful. it says you already defined "docno" (the first parameter in detail table)
Could you help for the data entry?
When your if statement runs for the second time, it tries to add docno to the list of parameters that was created on the first iteration. As docno was added in that first iteration, it already exists.
There are a couple of ways you could get around this:
Execute your stored procedure for each row. The downside is that you
have the overhead of additional DB calls.
Use User-Defined Table Types. You would build up your table in C#
and then pass that as a parameter to your stored procedure to
insert into your table.
I think this is overkill but want to ask. The code below is being run, is there a need to assign the values of tempList to a totally separate array? I feel that you could iterate the values of tempList but want to verify before I make a change:
List<string> tempList = new List<string>();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
try
{
if (!reader.IsDBNull(0))
{
tempList.Add(reader[0].ToString());
}
}
catch
{
}
}
reader.Close();
crMy = tempList.ToArray();
for (int q = crMy.GetLowerBound(0); q <= crMy.GetUpperBound(0); q++)
{
return crMy[q];
}
No you don't need the array, your code could be changed to:
return tempList.First();
or FirstOrDefault depending on the code that follows.
No there is no need to initialize a new array. You can simply iterate over the List<string>.
Although you even need a List, because you are simply returning the first element in list. That will prevent you from reading every row, for example if you have 1 million entries you'll have to read them all, before returning the first one.
This code reads and directly returns the first non null value:
string result = null;
using(var reader = cmd.ExecuteReader()){
while (reader.Read()) {
if (!reader.IsDBNull(0)) {
return resultreader[0].ToString();
}
}
I believe you're correct that there's no reason to copy the List to an array in this particular context.
I would recommend using a using statement with your SqlDataReader however as it's a disposable object and I'm not seeing any dispose code here.
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
try { if (!reader.IsDBNull(0)) { tempList.Add(reader[0].ToString()); } }
catch { }
}
}
As written, your code returns the first column of the first row where that column is non-null. You could simply write this and avoid the list altogether.:
static string GetDataValue()
{
using ( SqlConnection conn = new SqlConnection("your-connect-string-here"))
using ( SqlCommand cmd = conn.CreateCommand() )
{
InitializeSqlCommand(cmd) ;
conn.Open() ;
using ( SqlDataReader reader = cmd.ExecuteReader() )
{
while ( reader.Read() && reader.IsDBNull( 0 ) )
{
string value = reader[0].ToString() ;
return value ;
}
}
}
throw new InvalidOperationException("no [useful] data returned");
}
That approach seems a little specious to me: you're returning potentially a lot of data, reading it all across the network and then throwing everything away except for the first column of the first row where that column is non-null.
You should modify the query to return just what you want, so instead something like this (rather open-ended) :
select t.col_1, t.col_2, ... , t.col_n
from foo t
change it to the more specific:
select top 1 t.col_1
from foo t
where t.col_1 is not null
Your RDBMS will thank you. Your network will thank you. Your code will run faster, your customers will be happier and and you can replace the query logic with the eminently simple and more intentional:
static string GetDataValue()
{
string value ;
using ( SqlConnection conn = new SqlConnection("your-connect-string-here"))
using ( SqlCommand cmd = conn.CreateCommand() )
{
InitializeSqlCommand(cmd) ;
conn.Open() ;
value = (string) cmd.ExecuteScalar() ;
conn.Close() ;
}
if ( value == null ) throw new InvalidOperationException("no data read") ;
return value ;
}
Can someone help me figure out why this is not working.
Here's the SP:
CREATE PROCEDURE sGetPeriods #PeriodTypeID INT, #ParentPeriodID INT
AS
BEGIN
SET NOCOUNT ON
SELECT PeriodID, Period
FROM Periods
WHERE CASE WHEN #PeriodTypeID IS NULL THEN 1
WHEN #PeriodTypeID = PeriodTypeID THEN 1
ELSE 0
END = 1
AND CASE WHEN #ParentPeriodID IS NULL THEN 1
WHEN #ParentPeriodID = ParentPeriodID THEN 1
ELSE 0
END = 1
ORDER BY Period
END
GO
Here's the C# code that calls the SP:
private DataTable CmdExecuteToDataTable(SqlCommand objSQLCmd)
{
DataTable dtReturn = new DataTable();
using (SqlConnection objSQLConn = new SqlConnection(c_szSQLConnectionString))
{
objSQLConn.Open();
objSQLCmd.Connection = objSQLConn;
if (bLog)
{
//WriteLog("Connection: " + objSQLConn.ConnectionString);
WriteLog("CmdExecuteToDataTable(" + objSQLCmd.CommandText + ")");
for (int zz = 0; zz < objSQLCmd.Parameters.Count; zz++)
{
WriteLog(objSQLCmd.Parameters[zz].ParameterName + ": " + objSQLCmd.Parameters[zz].Value.ToString());
}
}
SqlDataReader _r = objSQLCmd.ExecuteReader();
if (_r.HasRows)
{
DataReaderAdapter _dra = new DataReaderAdapter();
_dra.FillFromReader(dtReturn, _r);
}
_r.Close();
}
if (bLog)
{
WriteLog("\t" + "Returned: " + dtReturn.Rows.Count.ToString() + " rows.");
WriteLog(("-").PadRight(80, '-'));
}
return dtReturn;
}
And, here's the code that calls the above method:
public DataTable GetPeriods(int iPeriodTypeID, int iParentPeriodID)
{
SqlCommand objSQLCmd = new SqlCommand("sGetPeriods");
objSQLCmd.Parameters.Clear();
if (iPeriodTypeID != 0)
objSQLCmd.Parameters.Add(new SqlParameter("#PeriodTypeID", iPeriodTypeID));
if (iParentPeriodID != 0)
objSQLCmd.Parameters.Add(new SqlParameter("#ParentPeriodID", iParentPeriodID));
return CmdExecuteToDataTable(objSQLCmd);
}
Finally, here's the page code that calls the method above:
int iPeriodTypeID = 1;
int iParentPeriodID = 1;
DataTable dtPeriods = clsDataLayer.GetPeriods(iPeriodTypeID, iParentPeriodID);
When the page loads, I get
Procedure or function 'sGetPeriods' expects parameter '#PeriodTypeID', which was not supplied.
Yet, the parameter IS supplied as verified by the LOG file entry:
8/19/2011 8:44:16 AM: CmdExecuteToDataTable(sGetPeriods)
8/19/2011 8:44:16 AM: #PeriodTypeID: 1
8/19/2011 8:44:16 AM: #ParentPeriodID: 1
Somehow, the stored procedure is not getting the values passed with the SQL Command object.
Any ideas?
FYI, when I defined the SP Like this:
CREATE PROCEDURE sGetPeriods #PeriodTypeID INT = NULL, #ParentPeriodID INT = NULL
the Procedure executes but returns ALL rows as if it had not received any parameters.
I tried wrapping the code within a using(sqlcommand sqlcmd = objSLQCmd) but that did not help.
Thanks for any pointers,
John
Try setting the CommandType of the SqlCommand to StoredProcedure.
At the moment, it interpreting it as a textual query (literally sGetPeriods). For a text query, you have to include the appropriate parameters in the text of the query, which you're not doing.
It's unfortunate that, if you give SQL a query batch that starts with a stored procedure name, it treats it as if an exec is present before that. It would have been a far more obvious error message if this wasn't so.