I have a DateTime record in my table in a database and I write a query to get it from the database:
string command2 = "select Last_Modified from Company_Data where Company_Name='" + DescriptionEntryForm.SelectedItem.ToString() + "'";
SqlCommand search_company2 = new SqlCommand(command2, con_string.con);
SqlDataReader company_reader2 = search_company2.ExecuteReader();
dateform.Text = company_reader2.GetValue(0).ToString();
company_reader2.Close();
But the penultimate statement throw an exception saying "Invalid attempt to read when no data is present".
How can I solve it?
Well, the immediate problem with your code is that you haven't called company_reader2.Read() to move the cursor onto the first row.
From the docs for SqlDataReader.Read:
The default position of the SqlDataReader is before the first record.
Therefore, you must call Read to begin accessing any data.
You should also note the return value of Read() which indicates whether or not you've read to the end of the record set.
Other problems:
You should put using statements around the SqlCommand and SqlDataReader, otherwise if there's an exception you won't be closing the connection. You may be able to get away with disposing the command and letting the reader just disappear automatically - but I typically dispose of both just so I don't need to think too carefully.
You're assuming that the format of date/time will be appropriate by just calling ToString with no format specifier.
You should use a parameterised SQL query to avoid SQL injection attacks.
It looks like you've executed the command, but haven't actually read from it.
company_reader2 = search_company2.ExecuteReader();
if (company_reader2 != null && company_reader2.HasRows) {
company_reader2.Read();
dateform.Text = company_reader2[0].ToString();
}
Related
I have a long set of SQL scripts. They are all update statements. It's for an access database. I want to validate the script before I run it. Firstly, I'd like to make sure that the query can be parsed. I.e. that the SQL is at least syntactically correct. Secondly, I'd like to make sure that the query is valid in terms of database structure - i.e. there are no missing columns or the columns are of the wrong type etc. But, I don't want the query to be actually executed. The aim of this is to do a quick validation before the process kicks off because the process takes several hours and one syntactical error can waste a day of someone's time.
I will probably write the tool in C# with .net but if there's a pre-built tool that would be even better. I will probably use the Access API. In SQL Server this is very straight forward. You can just validate the query in SQL Server management studio before running it. It will give you a good indication of whether the SQL will complete or not.
How would I go about doing this?
Edit: an answer below solves the issue of checking syntax. However, I'd still like to be able to validate the semantic content of the query is OK. However, I think this might be impossible in Access without actually running the query. Please tell me I'm wrong.
I'm not 100% sure if Access works the same way as a traditional database, but with a mainstream RDMBS, there are actually three distinct steps that happen when you run a query:
Prepare
Execute
Fetch
Most are oblivious to the distinction because they just hit "run" and see results come back.
It's the "Execute" that actually compiles the statement before going off and pulling data.
When you use ADO, you can actually see the three events as three separate calls to the database. What this means is you can trap the execute step to see if it fails, and if it succeeds, there is nothing requiring you to actually get the results.
OleDbConnection conn = new OleDbConnection();
conn.ConnectionString = String.Format("{0}{1}",
#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=", #"c:\Access\MyDb.accdb");
conn.Open();
bool valid;
using (OleDbCommand cmd = new OleDbCommand("select [Bad Field] from [Table]", conn))
{
try
{
OleDbDataReader reader = cmd.ExecuteReader();
valid = true;
reader.Close(); // Did not ever call reader.Read()
}
catch (Exception ex)
{
valid = false;
}
}
And now valid indicates whether or not the statement compiled.
If you want to get really fancy, you can parse the exception results to find out why the command failed.
Access supports transactions on its Connection object. Try to execute your SQL statement inside a transaction and always call Rollback. Wrap the whole attempt in a Try/Catch block to assess whether the statement executed successfully or not.
Sorry for not putting the question in the title, the real problem is that I can't see an issue with my code but it's not producing what I want it to.
So I've written some code which is meant to take the database's Password field and inspect its value then store that value as a string within a password variable. In SQL Management studio the value is 'MyPass', that's what I'm expecting to get when I print out the password variable as a MessageBox. Instead I get two boxes, 'password' and then 'password123'. The only column within the table: Login, is one called Password.
Here is the using statement:
using(SqlConnection con = new SqlConnection(connectionString))
{
string query = "SELECT Password FROM LoginInfo";
con.Open();
//SqlCommand to be executed against my DB with the query text and the connection object.
SqlCommand myCommand = new SqlCommand(query, con);
//Sends CommandText to connection. Creates a
SqlDataReader read = myCommand.ExecuteReader();
while(read.Read())
{
//Convert read to IDataRecord.
ReadSingleRow((IDataRecord)read);
}
read.Close();
}
I apologize if my comments are incorrect, I wrote them down because they help me think.
You can read my interpretation of the code below and tell me what's wrong with my thinking process or you can just scour the code samples for errors.
My Interpretation (If you'll indulge me):
So, in the using statement we create a connection, a query string, an SqlCommand and an SqlDataReader. Passing the SqlCommand the query string and the connection object we then set its instance equal to an SqlDataReader, named 'read' with the method ExecuteReader() which takes the command text to wherever the connection is through the connection object.
Next we use a while loop, we use the .Read() to advance the reader on to the first record, since my db only has one record 'Password' this just moves it on to that. Within while we have the method ReadSingleRow which converts the reader instance into an IDataRecord: ((IDataRecord)read).
The ReadSingleRow method:
public void ReadSingleRow(IDataRecord record)
{
UserPass = record.GetString(record.GetOrdinal("Password"));
MessageBox.Show(UserPass);
}
the IDataRecord retrieves the string value of the oridinal(ID) password. My understanding is that it looks at the value of Password: Mypass, and then stores that within the UserPass string variable. The MessageBox of course blurts out the variable as a test to see if it worked. It didn't.
So my question is the annoying: why didn't this work? Perhaps I am miss-using the IDataRecord.
I was getting this error: "Input string was not in a correct format."
Here is my Code:
private void UpdatePOdetailBalance(int Qty)
{
int newbal;
SqlCommand com = new SqlCommand();
com.Connection = cn;
newbal = Convert.ToInt16(txtQtyOrdered.Text) - Qty;
com.CommandText =
"UPDATE PODetail SET BalanceQty="+ newbal +" WHERE OrderID=" +
Convert.ToInt16(txtPONumber.Text) + "AND ItemID=" +
Convert.ToInt16(txtItemNo.Text);
com.ExecuteNonQuery();
}
private void btnOK_Click(object sender, EventArgs e)
{
UpdatePOdetailBalance(Convert.ToInt16(txtQuantity.Text));
}
I want to compute the newbal which is equal to txtQtyOrdered minus Qty but i'm getting this error please help me with this. Thanks.
The problem stated by your error message is probably on one of the lines that try to convert the value in the textboxes to a short integer. Without any check, the value typed by your user could be anything but a number and you get this error message (for example, if you user leaves the textboxes empty).
You should try to check if the textboxes content could be converted to a valid short integer using TryParse before attempting to execute the query
int ordered;
if(!int16.TryParse(txtQtyOrdered.Text, out ordered))
{
MessageBox.Show("Invalid number for Ordered quantity");
return;
}
int orderID;
if(!int16.TryParse(txtPONumber.Text, out orderID))
{
MessageBox.Show("Invalid number for OrderId");
return;
}
int itemID;
if(!int16.TryParse(txtItemNo.Text, out itemID))
{
MessageBox.Show("Invalid number for ItemID");
return;
}
At this point you could execute your calculation using the converted short integers and then write your query in this way (adding a space before the AND)
com.CommandText =
"UPDATE PODetail SET BalanceQty="+ newbal.ToString() +
" WHERE OrderID=" + orderID.ToString() +
" AND ItemID=" + itemID.ToString();
But the string concatenation of query text and user input is never advised as a good practice (in your case is harmless because if the conversion is successful you don't have to worry about Sql Injection, but don't take the habit to do it).
So the perfect way to write this query is through the use of a parametrized query
com.CommandText =
"UPDATE PODetail SET BalanceQty=#newbal " +
" WHERE OrderID=#orderID " +
" AND ItemID= #itemID"
com.Parameters.AddWithValue("#newbal", newBal);
com.Parameters.AddWithValue("#orderID", orderID);
com.Parameters.AddWithValue("#itemID", itemID);
com.ExecuteNonQuery();
As a good article on Parameterized query and why to use them, I suggest to read these old words from Jeff Atwood
You need to put a space before your "AND" and that you are trying to convert a string to an integer that isn't an integer.
I'd recommend making changes according to the following code review suggestions based on the code (listed in order of value (cost/benefit of "fixing")):
This method, which is accessing a database should not be reading controls to get its values. Instead there should be an event handler, such as a button click, that parses the values of other controls, using TryParse, as gregjer answered. By segregating the UI and Data code, the data access layer is easier to test and by parsing at the surface (the UI layer) exceptions dealing with bad user input will be caught as soon as possible.
Dynamic SQL via strings in the database or in the data access layer w/i .NET is open to SQL injection. You are resolving that issue by parsing the text, so awesome job by you. BUT, this was already handled by the .NET team by providing parameterized commands. Refer to the MSDN SqlCommand.Parameters or see here for a brief, including how a consuming developer groks this topic: When should "SqlDbType" and "size" be used when adding SqlCommand Parameters?
Variable naming. Instead of Qty, standard .NET naming conventions would call for quantity, camelCased since it is a parameter and the full human language name, not a shorthand or abbreviation, especially for publicly visible bits. IntelliSense makes long variable names not a problem. Since .NET is unwieldy using just Notepad, it should be assumed that other developers are using an IDE such as VisualStudio or SharpDevelop, so use meaningful names.
Stored procedures should be used. Every time this SQL is executed, SQL Server needs to check its command cache minimally, but if the command has been flushed from cache, the SQL command needs to be interpreted and encached (put into cache). This as well as the fact that using a stored procedure requires "shipping" less bytes on every call to the database.
That error means that the string you're trying to convert is not an integer.
Try to use int.TryParse
int newbal;
if(int.TryParse(txtQtyOrdered.Text, out newbal))
newbal = newbal - Qty;
the same with other texts you are trying to convert
... and add space before " AND which will generate next error
I think you need to debug your code. During debugging copy your query from "com.CommandText" and paste in SQL Server you find the error
There is only a query error nothing else...
May be txtQtyOrdered value is not integer, there is also need blank space "AND ItemID=" to " AND ItemID="
Thanks,
Taha
First - You are missing a space before "AND"
You should try to parse the values before the update statement.
You should decide what you want to do in case the input from the textbox wasn't in the correct format rather then just get an exception when you try to update.
This isn't the right way to format strings, You should use string.Format
you can sometimes run into this problem when you have multiple parameters and are using Oracle or DB2 databases. They dont's support named parameters or it's not turned on.
Oracle:
Dim cmd As OracleCommand = DirectCast(connection.CreateCommand, OracleCommand)
cmd.BindByName = True
Make sure you parameters are added to the command object in the same order as the sql statement
I have a string coming from a stored procedure looks like '001234567'.
sqlCommand = new SqlCommand("csp_Bbp_OBN_GetBasePageList", BBConnection);
sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
sqlCommand.Connection.Open();
// Run the SQL statement, and then get the returned rows to the DataReader.
accReader = sqlCommand.ExecuteReader();
while (accReader.Read())
{
BasePage basePage = new BasePage();
basePage.GroupNum= accReader.GetValue(0).ToString().Trim();
basePageList.Add(basePage);
accReader.Close();
accReader.Dispose();
}
return basePageList;
In my case, from the stored procedure I am returning the varchar, after executing and reading it I am getting the value to basePage.GrouNum which is a string. So, I don't see where it is trimming the leading zeros.
Example: GroupNumber in the table is : "001234567"
BasePage.GroupNum after reading from DataReader : "1234567"
But, I do not want the leading zeros being trimmed.
Can any one help me how to solve this problem?
You can convert it to an integer then format the string going out:
basePage.GroupNum = Convert.ToInt32(accReader.GetValue(0)).ToString("000000000")
Using the 0's as format specifiers should pad the result string appropriately.
If you have access to the stored procedure, make sure it returns a string. So:
select GroupNumber
instead of, for example:
select cast(GroupNumber as int) as GroupNumber
What happens if instead of
GetValue(0).ToString()
you use
GetString(0)
?
One other thing - get into the habit of wrapping things like commands and readers in using statements. You can avoid the close and dispose stuff and be assured even if your code throws you don't leave anything around.
I'm working with a datagrid and adapter that correspond with an MSAccess table through a stored query (named "UpdatePaid", 3 paramaters as shown below) like so:
OleDbCommand odc = new OleDbCommand("UpdatePaid", connection);
OleDbParameter param;
odc.CommandType = CommandType.StoredProcedure;
param = odc.Parameters.Add("v_iid", OleDbType.Double);
param.SourceColumn = "I";
param.SourceVersion = DataRowVersion.Original;
param = odc.Parameters.Add("v_pd", OleDbType.Boolean);
param.SourceColumn = "Paid";
param.SourceVersion = DataRowVersion.Current;
param = odc.Parameters.Add("v_Projected", OleDbType.Currency);
param.SourceColumn = "ProjectedCost";
param.SourceVersion = DataRowVersion.Current;
odc.Prepare();
myAdapter.UpdateCommand = odc;
...
myAdapter.Update();
It works fine...but the really weird thing is that it didn't until I put in the odc.Prepare() call.My question is thus: Do I need to do that all the time when working with OleDb stored procs/queries? Why? I also have another project coming up where I'll have to do the same thing with a SqlDbCommand... do I have to do it with those, too?
This is called, oddly enough, a prepared statement, and they're actually really nice. Basically what happens is you either create or get a sql statement (insert, delete, update) and instead of passing actual values, you pass "?" as a place holder. This is all well and good, except what we want is our values to get passed in instead of the "?".
So we prepare the statement so instead of "?", we pass in parameters as you have above that are going to be the values that go in in place of the place holders.
Preparing parses the string to find where parameters can replace the question marks so all you have to do is enter the parameter data and execute the command.
Within oleDB, stored queries are prepared statements, so a prepare is required. I've not used stored queries with SqlDB, so I'd have to defer to the 2 answers previous.
I don't use it with SqlDbCommand. It seems as a bug to me that it's required. It should only be nice to have if you're going to call a procedure multiple times in a row. Maybe I'm wrong and there's a note in documentation about providers that love this call too much.
Are you using the JET OLEDB Provider? or MSDASQL + JET ODBC?
You should not need to call Prepare(), but I believe that's driver/provider dependent.
You definitely don't need to use Prepare() for System.Data.SqlClient.