I'm running into an issue that is mind boggling to me. I've noticed that IDataReader.Read() is acting differently depending on how the IDbCommand.CommandText is set.
In the code below - If 'AID' is set and passed to EntAgencyId(), reader.Read() returns true and the program is able to enter into the while loop. If I just set 'query' in EntAgencyId() using the same value I'm passing to the function ('455'), the program is never able to enter the while loop (the same behavior happens when passing in 'AID' from a textbox.text).
public string EntAgencyId(string AID)
{
cmd = uasConnection.CreateCommand();
//query = "select * from EnterpriseAgencyTbl where AOCId = " + AID; //<--Works
query = "select * from EnterpriseAgencyTbl where AOCId = 455"; //<--Causes issue
cmd.CommandText = query;
reader = cmd.ExecuteReader();
while (reader.Read())
{
EntAgId = reader["Id"].ToString();
AgencyName = reader["Name"].ToString();
}
reader.Close();
return AgencyName;
}
When debugging, 'query' always has the same value, so why is this making a difference with .Read().
Food for thought - .Read() returns true if there are more rows; otherwise, false. In this case I tried just reading the first single row using the Item property and GetValue(), both result in an 'Object not set to an instance of an object' error.
I've completely run out of ideas, so any help will be appreciated!
If AID may come from an untrusted source, then you should really be using parameters.
Use the following code instead:
query = "select * from EnterpriseAgencyTbl where AOCId = #AOCId";
cmd.CommandText = query;
cmd.Parameters.Add(new SqlParameter("#AOCId", SqlDbType.Int) { Value = int.Parse(AID) });
Then see if this behaves the same using static inputs, such as:
cmd.Parameters.Add(new SqlParameter("#AOCId", SqlDbType.Int) { Value = 455 });
Note: I'm making the assumption, based on 'Id' in the name, that AOCId is an int type.
Related
I'm trying to return all rows of a single column in my database to populate a list. When I execute the stored procedure in SQL, it works fine, but nothing gets returned when I try to do it in C#.
public static List<string> GetRows(string filter_one, string filter_two)
{
var retrievedRows = new List<string>();
var storedProc = "dbo.MyStoredProc";
using (SqlConnection connection = new SqlConnection(MY_CONNECTION_STRING))
using (SqlCommand command = new SqlCommand(storedProc, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
retrievedRows.Add(reader.GetString(0));
}
}
}
return retrievedRows;
}
Any ideas? I get no errors in the console or when I run it on IIS Express either. When I watch retrievedRows, the size stays at 0 even though when I run the same query in SQL with the same passed parameters, it returns results for me.
EDIT: Please excuse me, my brain must be running a bit slow today. One of the parameters I was passing was pointed at the (empty) value of the wrong webcontrol. I don't know how I missed this.
There is only one issue with your posted snippet that I can see, which could pose a problem:
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
In this section, you're adding two VARCHAR parameters but not specifying a length for them. Try changing your code to add a length specification:
var filterOne = new SqlParameter("FilterOne", System.Data.SqlDbType.VarChar, 50);
The constructor in use here is SqlParameter(string, SqlDbType, int):
Parameters
-
parameterName (String): The name of the parameter to map.
dbType (SqlDbType): One of the SqlDbType values.
size (Int32): The length of the parameter.
When working with VARCHAR you must specify a length or anything outside of the default length (which is 1 byte for definitions and variables, and 30 bytes for CAST and CONVERT) will be truncated:
When n isn't specified in a data definition or variable declaration statement, the default length is 1. If n isn't specified when using the CAST and CONVERT functions, the default length is 30.
I use SQL Server to build my database and SqlDataReader to read data from it.
command.Connection = cn;
command.CommandText = "SELECT * FROM test";
SqlDataReader rd = command.ExecuteReader();
while(rd.Read())
{
double d = (double) rd.GetValue(0);
}
The column (0) I am trying to get value from is a 'float' type and has value '3.5' . As mapping data type from this MSDN link, the type of the object returned by rd.GetValue(0) must be 'double'. But the code above returns to variable 'd' value '0.0'. I tried this line:
double d = Convert.ToDouble(rd.GetValue(0));
But it still returns '0.0' to variable 'd'.
I tried searching on Google and StackOverflow but there is no result.
What am I missing? Help me!
As it is now, your code iterates over all the records (if there are many) an takes the last entry, which since you have no order by clause, may differ in every query execution. If indeed you want to only take 1 value, use ExecuteScalar together with an order by clause:
command.Connection = cn;
command.CommandText = "SELECT TOP 1 * FROM test order by myfield desc"; //or asc
double result = (double)command.ExecuteScalar();
Otherwise have all the result saved in a list:
...
List<double> result = new List<doulbe>();
while(rd.Read())
{
result.Add(double.Parse(rd[0].ToString());
}
Finally, if you need only the 1st field, for performance reasons, is far better not to use * but explicit set the field you want:
"SELECT TOP 1 myfield FROM test order by myfield desc"; //or asc
you can try it;
double d = (double) rd.GetValue(0);
to
double d = 0;
double.TryParse(rd["ColumnName"].ToString().Replace('.',','),out d);
OR:
double d = double.Parse(rd["ColumnName"].ToString(), CultureInfo.InvariantCulture);
This here works fine for me, im getting 3,5 in my list
List<double> columnData = new List<double>();
using (SqlConnection connection = new SqlConnection("Server=EGC25199;Initial Catalog=LegOgSpass;Integrated Security=SSPI;Application Name=SQLNCLI11.1"))
{
connection.Open();
string query = "SELECT * FROM [dbo].[floattable]";
using (SqlCommand command = new SqlCommand(query, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
columnData.Add(reader.GetDouble(0));
}
}
}
}
Oh I have found the answer. Nothing wrong with the code I wrote. The problem is that I place the breakpoint on the 'double d = (double) rd.GetValue(0)' line. That is, 'd' value is not assigned yet so that on the debug screen it returns '0.0'.
Sorry for this mistake and thank you all Stack-Over-flowers for spending your time helping me!!!!
What I am expecting my code to do is gather the top ten records that have an 'I' in the SQLMigrationFl field, and then process those ten records by removing that 'I' value. Below is the portion of code that handles this.
string inProgressQuery = "SELECT TOP 10 IDType, ID, Program, Date, Body,
AcctNo, ANPACAcctID, ANPACClientID, TEAMID,
ImportDate, AnnualReview, TeamGUID,
ANPACClientLastName, ANPACClientFirstName, " +
"PolicyNumber, AccountOwnerLastName,
AccountOwnerFirstName, SCRACF, SCDateTime, NoteID
FROM NoteTable WHERE SQLMigrationFl = ?";
command = new OleDbCommand(inProgressQuery, connection);
command.Parameters.AddWithValue("SQLMigrationFl", "I");
reader = command.ExecuteReader();
if(reader.HasRows)
{
while(reader.Read())
{
//clear the In Progress flag
query = "UPDATE NoteTable SET SQLMigrationFl = ? WHERE
NoteTable.NoteID = " + reader[19].ToString();
command = new OleDbCommand(query, connection);
command.Parameters.AddWithValue("SQLMigrationFl", DBNull.Value);
reader = command.ExecuteReader();
}
}
What I am finding is that the query returns one value, and processes it. Then in five seconds it finds another record and reprocesses that one. *Five seconds is just a delay we have set in code to check for more records to be processed. It processes one record at a time, rather than grabbing ten and processing those at once within the same while loop. Is something wrong with my code or query?
Thanks for the help.
The culprit is your reassigning of the data reader using reader = command.ExecuteReader();. That will now return 1 result and your next loop will be over that 1 result. Regardless for non SELECT queries use ExecuteNonQuery instead.
Other places that you can update your code to be "better" are
Use parameters whenever you have values that you want to use in your sql statements.
Always specify the type for all your parameters.
Always wrap your types instances that implement IDisposable in using blocks to ensure resources are cleaned up.
I also recommend you not share connection instances, below it seems that there might be a static connection somewhere. It would be best to not share one and create/open one when you need it and then close/dispose it.
string inProgressQuery = "SELECT TOP 10 IDType, ID, Program, Date, Body,
AcctNo, ANPACAcctID, ANPACClientID, TEAMID,
ImportDate, AnnualReview, TeamGUID,
ANPACClientLastName, ANPACClientFirstName, " +
"PolicyNumber, AccountOwnerLastName,
AccountOwnerFirstName, SCRACF, SCDateTime, NoteID
FROM NoteTable WHERE SQLMigrationFl = ?";
using(var command = new OleDbCommand(inProgressQuery, connection))
{
// I guessed on the type and length
command.Parameters.Add(new OleDbParameter("SQLMigrationFl", OleDbType.VarChar, 10)).Value = "I";
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
//clear the In Progress flag
const string UpdateQuery = "UPDATE NoteTable SET SQLMigrationFl = ? WHERE NoteTable.NoteID = ?";
using(var commandUpdate = new OleDbCommand(UpdateQuery, connection))
{
commandUpdate.Parameters.Add(new OleDbParameter("SQLMigrationFl", OleDbType.VarChar, 10)).Value = DBNull.Value;
commandUpdate.Parameters.Add(new OleDbParameter("NoteId", OleDbType.Int)).Value = reader[19];
commandUpdate.ExecuteNonQuery();
}
}
}
}
I'm trying to execute a query in C# which sums the view count of a user. I get returned a NULL value. Using the same statement in Server Management Studio gives me the correct result.
here's my code:
public static int Count_views(string username)
{
int views = 0;
StringBuilder query = new StringBuilder();
query.Append("SELECT Sum(views) FROM videos WHERE username = #username");
using (SqlConnection con = new SqlConnection(Config.ConnectionString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand(query.ToString(), con))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter("#username", username));
views = Convert.ToInt32(cmd.ExecuteScalar());
}
}
return views;
}
I have debugged the code and the parameters are correct. I get this error :
System.InvalidCastException: Object cannot be cast from DBNull to other types.
which means I'm getting a Null value in return.
The ConnectionString is alright. Every other function works fine except for this one. can anyone tell me what might me the issue here?
Edit:
Below are the screen shots of what I'm encountering. The first screenshot shows the value "Administrator" is being passed inside the function. the second screenshot shows this value is also in the database.
You can change the SUM query to return 0 instead of NULL:
query.Append("SELECT COALESCE(Sum(views),0) FROM videos WHERE username = #username");
You could also use the as operator to cast it to the desired nullable type:
int? views = cmd.ExecuteScalar() as int?;
Using C# on VS13 with a connected Access database and am receiving the error "No value given for one or more required parameters" when executing certain SQL.
Here is my code. Thanks for your time!
// ID accessors for an itemLine object
public void setID(string Value) { ID = Value; }
public string getID() { return ID; }
...
// Code snippet where error originates
foreach (CartItem itemLine in parBasket)
{
cmd.CommandText = "SELECT Instock FROM tblProducts WHERE ProductID = " + itemLine.getID() + "";
OleDbDataReader reader = cmd.ExecuteReader();
reader.Read();
int stock = Convert.ToInt32(reader["Instock"]);
stock = stock - itemLine.getQuanity();
reader.Close(); //Close the reader
cmd.CommandText = "UPDATE tblProducts SET InStock =" + stock + " WHERE ProductID = " + itemLine.getID() + "";
updated = updated + cmd.ExecuteNonQuery();
}
cn.Close();
return updated;
}
If CartItem.getID() returns an integer, not a string, then you need to remove the single quotes around it in the SELECT statement you are building.
Even better - read up on using SqlParameter and use this when building queries like this, as it helps avoid this sort of error, and also prevents SQL injection attacks, if any of the parameter data comes directly from user input.
To fix those errors yourself, you should:
Run with the debugger;
When the SQL command throws an exception the debugger should break (at least if it's unhandled. If you catch it, the debugger may still break but you have to tweak its config to do so);
Use a Watch or something to look at the CommandText of your SqlCommand (i.e. the SQL text that actually gets executed).
This should make pretty obvious what is wrong.
Now using my Crystal ball rather than a debugger, I think your problem is that getId() returns a string (per your comment on the question) and you end up with something like: WHERE ProductID = FortyTwo in both the first and second SQL queries.
The bad solution to this would be to enclose the string in quotes: WHERE ProductID = 'FortyTwo' but you should be careful that your ID doesn't contain a quote itself (which you should escape).
The good solution is to use a SQL parameter. Assuming SQL Server syntax: WHERE ProductID = #id and cmd.Parameters.AddWithValue("id", item.GetId()). (Note: you use the same command repeatedly, you should not add the parameter repeatedly. Rather, add it once and then change its value at each iteration.)