I have following tables in my MS Access database.
Personal, Partner, ContactDetails, NativeAddress bla bla bal. I created a Wizard in Visual Studio 2012 for above tables. A screenshot is given below. Now I want to submit all data at once in all tables when user presses the submit button. So what syntax should I use now. Please guide.
My code is something like this. Its incomplete and just beginning of my script. So please don't get me wrong.
protected void dataWizard_FinishButtonClick(object sender, WizardNavigationEventArgs e)
{
OleDbConnection con = new OleDbConnection();
con.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\micronets\jobwork-2013\arunthathiyar-sangham\arunthathiyar-web-application\App_Data\arunthathiyar-db.accdb";
string personalDetails = "INSERT INTO PersonalDetails(FirstName, MiddleName, LastName, Sex, Age, DateOfBirth, PlaceOfBirth, EducationalQualification, EmploymentStatus, Profession, PhysicalStatus, BloodGroup) VALUES (#fnPD, #mmPD, #lnPD, #sexPD, #agePD, #dobPD, #pobPD, #eqPD, #esPD, #profPD, #phyicPD, #bgPD)";
string familyDetails = "INSERT INTO FamilyDetails(Relationship, FullName, Status, BloodGroup, EducationalQualification, Profession, EmploymentStatus) VALUES(#relFD, #fnFD, #statusFD, #bgFD, #eqFD, #profFD, #esFD)";
string contactDetails = "INSERT INTO ContactDetails(FlatBuildingStreet, Landmark, Area, City, Pincode, State, Country, Mobile, Telephone, Email) VALUES(#fbsCD, #landCD, #areaCD, #cityCD, #pinCD, #stateCD, #countryCD, #mobCD, #telCD, #emailCD)";
try
{
con.Open();
txtMemAmountReceived.Text = txtPDFirstName.Text;
}
catch
{
txtMemAmountReceived.Text = "Sorry";
}
You're heading in the right direction. Here's what you need to do next:
Create an OleDbCommand object
You have a connection, now you need to create and Command object that can store the SQL text and execute commands against the database. Something like this:
OleDbCommand cmd = new OleDbCommand(personalDetails, con);
Open, execute, close
Then, inside your try block, you want to open the connection, execute the query, and close the connection:
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Connection.Close();
Rinse and repeat
You can take it from there as far as executing all three queries. You just need to update the cmd.CommandText property with the text for the other queries you want to execute, and call ExecuteNonQuery again.
you could possibly concatenate all of your query strings into one separated by semicolons(;) So the one large string would be sent to the server and each query would be processed and you would then only have to make one connection to the database.
string myQuery = personalDetails+ "; " + familyDetails + "; " + contactDetails
However i would just make 3 separate connections just so it will be easier to manage all of your parameters. If you ever have to go back and change the query or a parameter it will be a mess.
Related
I have a few text boxes that on my page that can add data to my database. What I'm looking for is how to actually make it more secure. The basic error checking of empty text boxes I know how to check. What I'm really searching for is bad data being saved or special characters like " ' " or " - ". Its a simple application only myself and maybe two other people will use it but still want to make sure learning and coding correctly. Any suggestions. This is my save button.
Here is my code:
try
{
OleDbConnection conn = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=H:\Databases\AddressBook.mdb");
conn.Open();
DataSet ds = new DataSet();
string cmd = "SELECT * FROM tblAddressBook";
OleDbDataAdapter da = new OleDbDataAdapter(cmd, conn);
da.Fill(ds, "Display");
DataRow newRow = ds.Tables["Display"].NewRow();
newRow[1] = txtFirstName.Text;
newRow[2] = txtLastName.Text;
newRow[3] = txtEmail.Text;
newRow[4] = txtPhone.Text;
ds.Tables["Display"].Rows.Add(newRow);
OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
cb.DataAdapter.Update(ds.Tables["Display"]);
conn.Close();
GridView1.DataBind();
}
catch (Exception)
{
lblErrorSave.Text = "Your information did not save clear form and try again";
}
Your code as shown is secure, but does have problems.
What your question is about is SQL Injection. This arises where you use dynamic SQL, like so (air code):
string sql = "insert into tableA (cola, colb) values ("
+ "'" + txtBox1.Text + "',"
+ "'" + txtBox2.Text + "')";
...and then go and execute it. Depending on the contents of the text boxes you could have all sorts of things happening. Something like "');drop table tableA;--"
This does not happen when you use a DataSet as above, so that's OK
Hoewever, your code is very inefficient. The first thing you do is pull down the whole of the Address table. If this is any size it will be slow and add a lot of IO, memory, and computation to the procedure.
You are also not checking that the row to be entered is actually a new one, not a modification of an old one or a duplicate. This may or may not be important to your app, but usually is important (dup data can be a real pain). You can amend your read of the Address table to pull down e.g. a row with the same email address (or whatever is unique), and if it gets it then amend with new data as you do above.
However if the data is to be added, then you need to use parameters; Air Code again:
string sql = "insert into table (colA, colB) values (#colA, #colB)";
using (OleDbCommand com = new OleDbCommand(sql, conn))
{
com.Parameters.Add("#colA", txtBox1.Text);
com.Parameters.Add("#colB", txtBox2.Text);
com.ExecuteNonQuery();
}
(Note that different drivers have slightly different syntax on adding Parameters and I'm not sure that the OleDb command supports this syntax, but there will be something close.)
Using Parameters prevents SQL Injection, as the values of the parameters are transported not intermixed in the SQL string and so their content has no effect of the SQL eventually executed.
I need to update a row where there exists a certain value. The ExecuteNonQuery returns a 0 meaning the database is not being updated. When stepping through in debug, the update is hit and contains the correct values, however no updates are being done.
string verifySql = #"UPDATE UserInfo SET Verified='#Verified'
WHERE UserID='#UserID'";
using (var con = newSqlConnection(
ConfigurationManager.ConnectionStrings["UserInfoDB"].ConnectionString))
using (var cmd = new SqlCommand(verifySql, con))
{
con.Open();
cmd.Parameters.AddWithValue("#Verified", "Verified " + DateTime.Now);
cmd.Parameters.AddWithValue("#UserID", user.UserId);
Response.Write(cmd.ExecuteNonQuery());
con.Close();
}
Lose the single quotes around the parameter names in your sql statement. You don't need them, and they're making your code interpret your parameter placesholders as simple string literals.
Then remove the con.Close() line. You don't need that either; it's covered by the using block.
Finally, you might also consider changing your verified column to a simple DateTime type, rather than trying to store that data as text.
I've created a query to select the body of a message from the message database. I'm not sure how to execute it to get the body string back and store it. I've tried using ExecuteReader, ExecuteScalar, and ExecuteNonQuery. None of them work. ExecuteScalar was the closest to working but it only returned the body of the message of the first row no matter which row you were trying to view. Anyone know how to do this? It's gotta be a easy fix.
SqlCommand com = new SqlCommand("SELECT Body FROM Messages WHERE MessageID= MessageID", conn);
com.Connection = conn;
com.Connection.Open();
String body;
body = com.ExecuteScalar.ToString;
That's what I have now. Thanks in advance!
What is messageId in your query? You should be doing something like this
SqlCommand com = new SqlCommand("SELECT Body FROM Messages WHERE MessageID = #MessageId");
com.Parameters.AddWithValue("#MessageId", 1); //Replace 1 with messageid you want to get
string s = com.ExecuteScalar().ToString()
You can use SQLDataAdapter and Datatable for this :
SqlCommand com = new SqlCommand("SELECT MessageID,Body FROM Messages WHERE MessageID= MessageID", conn);
SqlDataAdapter dadapter=new SqlDataAdapter();
DataTable dt = new DataTable();
com.Connection = conn;
com.Connection.Open();
String body;
dadapter.SelectCommand=com;
dadapter.Fill(dt);
body = dr.Rows["Body"].toString();
you should try something like this.
SqlCommand com = new SqlCommand("SELECT Body FROM Messages WHERE MessageID= MessageID", conn);
com.Connection = conn;
com.Connection.Open();
String body;
SqlDataReader dr = com.ExecuteReader();
if(dr.HasRows){
while(dr.Read()){
body+=dr["Body"].ToString();
}
}
I hope this works for you.
Based on your reply to Nudier below, you're trying to pass in the messageID of the selected message by using WHERE MessageID = MessageID
The reason this won't work, and the reason you're always getting the first row returned is that SQL doesn't know that MessageID is a variable you're trying to pass in. As far as SQL knows, MessageID is a column name, so all you're asking SQL to do is select the column "Body" of the row where the column MessageID = the column MessageID, so where MessageID equals itself, which always equates to true. And since ExecuteScalar always returns the first cell of the first row, your query will always return all rows from the Messages table, and the executeScalar will grab the first cell.
Hopefully that made sense, if not, just copy your query and run it against your SQL database, you should see what I mean about it returning all rows as the where clause always equals true.
To fix it, you need to take into account what Anuraj said about adding a parameter.
To pass in a variable to a SQL string in code, you need to parameterise it, then add the relevant parameter, so your SQL should become:
SELECT Body FROM Messages WHERE MessageID=#MessageID
(Notice the addition of the # symbol before the parameter name?)
And directly below that line, you need to add the parameter in code using:
com.AddParameterWithValue("#MessageId", MessageId);
(I think that's right, I copied it from Anuraj, I normally do it slightly differently)
Again, to see this working, you can run it directly against the database with a parameter by using
DECLARE #messsageID AS INTEGER
SET #messageID = 1
SELECT Body FROM Messages WHERE MessageID=#messageID
Have a read of this for more details (or if I haven't managed to be entirely clear) http://www.csharp-station.com/Tutorial/AdoDotNet/lesson06
I have the following code snippet.
SqlCommand cmd = new SqlCommand("SELECT FName,LName FROM EMPLOYEE_TABLE WHERE EmployeeID = '" +TextBox1.Text + "' AND Password = '"+ TextBox2.Text +"'", con);
SqlDataReader x = cmd.ExecuteReader();
try
{
if (x.Read())
{
name = (string)x["FName"] +' '+ (string)x["LName"];
Session["NAME"] = name;
Session["ID"] = TextBox1.Text;
Response.Redirect("sample.aspx?action=On_Click");
}
else
{
errormsg.Text = "login failed.Please enter Valid UserID and Password";
errormsg.ForeColor = System.Drawing.Color.Red;
}
}
catch (Exception exp)
{
errormsg.Text = "Sorry,You dont have access to this portal.";
}
finally
{
x.Close();
con.Close();
}
Now, when i use a valid id (that exists) and password as abc' or 'x'='x then it logs in into the first account of the table in the database. Till this it's fine.
However when I try to debug the code, it throws an error Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack..
Also if it is throwing an error then why is it logging into this 1st account of the database. Note: the first account of the database has a different userid than that which i m providing.
Note: I am the developed of this application. So I'm not doing anything illegal. :)
Look at this part of your SQL:
"' AND Password = '"+ TextBox2.Text +"'"
With your password, it's
"' AND Password = ''x'='x'"
which is not the SQL you want.
Even if you are trying to do SQL injection, you have to result in valid SQL. Usually, it's by ending the statement with a semi-colon after closing the quote. See this:
http://xkcd.com/327/
OK, to provide an answer based on the primary issue you've got (as you've stated, you're new to the SQL Injection issue).
SQL Injection is caused by dynamically building a SQL query using user input as part of the construction. The simplest solution to this in .Net is to create a parameterized query.
I think Jeff Atwood has the most complete yet concise article providing an explanation and complete example here
Quoted from above link:
SqlConnection conn = new SqlConnection(_connectionString);
conn.Open();
string s = "SELECT email, passwd, login_id, full_name " +
"FROM members WHERE email = #email";
SqlCommand cmd = new SqlCommand(s);
cmd.Parameters.Add("#email", email);
SqlDataReader reader = cmd.ExecuteReader();
The issue at hand:
The reason it's still logging into the account is because the query is still "valid".
The statement will still be executed, and the relevant record will still be returned from the database, no exception will be thrown.
The only way you will stop the login process when invalid data is provided is to validate the input before executing the query. You should always validate user input before sending it off to the database. If the user were to provide:
username'; drop table users;--
as the username, you would be in a LOT of trouble.
The error you're encountering is a debugging error, not an actual program exception. That's why it works when you run it normally.
To remedy the error, I'd first make sure that everything is running with a Debug build. Also, make sure you're currently debugging in the function of the variable you want to inspect. Try stepping (F10) a few times past your breakpoint to refresh the context. There are a slew of other suggestions on the internet for that particular error, so if you're still having problems you might have to do some googling.
Suppose I am calling a query "SELECT name, city, country FROM People". Once I execute my SqlDataReader do columns come in the same order as in my sql query?
In other words can I rely that the following code will always work correctly:
SqlConnection connection = new SqlConnection(MyConnectionString);
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "SELECT [name], [city], [country] WHERE [id] = #id";
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader(System.Data.CommandBehavior.SingleRow);
if (reader.Read())
{
// Read values.
name = reader[0].ToString();
city = reader[1].ToString();
country = reader[2].ToString();
}
}
catch (Exception)
{
throw;
}
finally
{
connection.Close();
}
Also how much performance do I lose if I use column names instead of ordinals (reader["name"])?
Are there any official microsoft documents describing the behavior of column ordering in SqlDataReader?
Yes they do but you can also use SqlDataReader.GetName(ordinal) and SqlDataReader.GetOrdinal(name).
As for performance, I think it's probably extremely insignificant compared to the overhead of say, retrieving the next row of data.
I totally agree with Josh - the positions of the fields are indeed such as you specify them in your SQL query text.
BUT: I would still prefer to use the column names, since it's more robust. E.g. what if you need to add a field to your SQL query?
command.CommandText = "SELECT [name], [jobtitle], [city], [country] WHERE [id] = #id";
Now suddenly you have to rewrite all your code to change the positions....
What I normally do outside the loop that enumerates through all the rows returned by the data reader is determine the positions of each field I'm interested in:
int namePosition = reader.GetOrdinal("name");
int cityPosition = reader.GetOrdinal("city");
and then I use these positions inside my loop handling the data to get quick access to the individual fields. That way you determine the positions only once, but you're using positions in your looping over the data - the best of both worlds! :-)
Marc
This example is the most maintainable and easiest to read:
int? quantity = reader.Get<int?>("Quantity");
Guid token = reader.Get<Guid>("Token");
It relies on the following extension method I created. It performs DB null checks, provides an informative error message when field is not found, and does not break when columns are re-aligned.
internal static T Get<T>(this SqlDataReader reader, string fieldName)
{
int ordinal;
try
{
ordinal = reader.GetOrdinal(fieldName);
}
catch (IndexOutOfRangeException)
{
throw new IndexOutOfRangeException(string.Format("Field name '{0}' not found.", fieldName));
}
return !reader.IsDBNull(ordinal) ? (T)reader.GetValue(ordinal) : default(T);
}