Theory
Reading और Writing, For Real
आप ADO.NET cast जानते हैं; अब इन्हें FestConnect के दो everyday jobs पर काम में लगाइए: एक page पर दिखाने के लिए events की list पढ़ना, और एक student sign up होने पर एक नई registration लिखना।
Reading एक Command के ExecuteReader को एक DataReader के साथ इस्तेमाल करती है; writing ExecuteNonQuery इस्तेमाल करती है। इस दौरान database code में सबसे important single habit आती है: किसी भी value के लिए parameters इस्तेमाल कीजिए जो एक user से आई। यह lesson दोनों operations दिखाता है और वह habit non-negotiable क्यों है।
Follow along
Connected Read Pattern
- एक Connection Open कीजिए Connection string के साथ एक SqlConnection create कीजिए और इसे open कीजिए (ideally एक using block के अंदर जो इसे automatically close करता है)।
- एक Command Create कीजिए आपका SELECT statement hold करता एक SqlCommand, connection से tied।
- ExecuteReader एक SqlDataReader पाने के लिए cmd.ExecuteReader() call कीजिए जो rows stream करता है।
- Read() से Loop कीजिए while (reader.Read()) एक बार में एक row advance करता है; columns को नाम से पढ़िए।
- Close कीजिए using block connection को close करता है जब आप done हों, यहाँ तक कि एक error occur हो तब भी।
Practical
Reading FestConnect's events
using (var conn = new SqlConnection(connectionString))
{
var cmd = new SqlCommand("SELECT Name, EventDate FROM Events", conn);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read()) // true while there is another row
{
string name = reader["Name"].ToString();
// ... add name to a list, or bind it to a control
}
} // connection closed automatically hereTheory
Writing: ExecuteNonQuery
Database बदलने के लिए, एक registration insert करना, एक detail update करना, एक row delete करना, आप एक Command के ExecuteNonQuery इस्तेमाल करते हैं। नाम का मतलब है 'एक command execute कीजिए जो एक query नहीं है (एक SELECT नहीं)'। यह statement चलाता है और affected rows की number return करता है।
तो एक single INSERT जो एक registration add करता है 1 return करता है। वह return value write के काम करने को confirm करने के लिए handy है: अगर आप एक row insert करने की expect करते थे और ExecuteNonQuery 1 return करता है, आप जानते हैं यह succeed हुआ।
Practical
Saving a registration, with parameters
using (var conn = new SqlConnection(connectionString))
{
// @name and @eventId are PLACEHOLDERS, not the actual values
var cmd = new SqlCommand(
"INSERT INTO Registrations (StudentName, EventId) VALUES (@name, @eventId)",
conn);
// Supply the user's values as parameters (safe)
cmd.Parameters.AddWithValue("@name", txtName.Text);
cmd.Parameters.AddWithValue("@eventId", selectedEventId);
conn.Open();
int rows = cmd.ExecuteNonQuery(); // returns 1 for one inserted row
lblStatus.Text = rows == 1 ? "Registered!" : "Something went wrong.";
}Watch out
User Input को कभी SQL में Glue मत कीजिए
Database code में सबसे dangerous single mistake user input को concatenating करके SQL build करना है, जैसे "... VALUES ('" + txtName.Text + "')"। एक visitor उस box में अपना खुद SQL type कर सकता है और आपकी query इसे चला देगी, एक attack जिसे SQL injection कहते हैं (आप इससे DBMS और PHP unit में मिले)।
हमेशा इसकी बजाय parameters (@name, cmd.Parameters के through added) इस्तेमाल कीजिए। ADO.NET फिर user के text को strictly एक value की तरह भेजता है, कभी executable SQL की तरह नहीं, तो injection हो नहीं सकता। हर user-supplied value को parameterise कीजिए, हर बार। और हमेशा connection close कीजिए (एक using block यह guarantee करता है)।
Quiz
एक नई registration row save करने के लिए, आप एक INSERT चलाते हैं। आप कौन सा method call करते हैं, और यह क्या return करता है?
- ExecuteReader, जो inserted row को एक DataReader की तरह return करता है
- ExecuteNonQuery, जो INSERT चलाता है और affected rows की number return करता है (एक single insert के लिए 1)
- ExecuteScalar, जो पूरी Registrations table return करता है
- DataBind, जो row insert करता है और page refresh करता है
Show the answer
ExecuteNonQuery, जो INSERT चलाता है और affected rows की number return करता है (एक single insert के लिए 1)
एक INSERT rows return करने की बजाय data बदलता है, तो आप ExecuteNonQuery call करते हैं, जो statement चलाता है और affected rows की number return करता है: एक inserted registration के लिए, वह 1 है। Option A wrong है: ExecuteReader SELECT queries के लिए है जो stream करने के लिए rows return करती हैं, एक INSERT के लिए नहीं। Option C wrong है: ExecuteScalar एक single value return करता है (एक cell, जैसे एक COUNT), पूरी table नहीं, और यह एक insert के लिए method नहीं है। Option D wrong है: DataBind एक UI method है जो data को एक control में render करता है; यह database को touch नहीं करता। Reading ExecuteReader इस्तेमाल करती है; changing (insert, update, delete) ExecuteNonQuery इस्तेमाल करता है और आपको बताता है कितनी rows बदलीं।
Think first
Parameters SQL Injection को Exactly कैसे रोकते हैं?
@name safe क्यों है जब "'" + txtName.Text + "'" dangerous है, भले ही दोनों आख़िरकार user के text इस्तेमाल करते हैं? फिर tap कीजिए।
Show the answer
क्योंकि parameters user के text को DATA रखते हैं, कभी command की structure का हिस्सा नहीं। जब आप concatenate करते हैं, user की typing SQL string का ही हिस्सा बन जाती है, तो अगर वे कुछ type करें जैसे एक quote उनके अपने SQL के बाद, database इसे commands की तरह parse और run करता है, यही injection है। एक parameter के साथ, आप database को दो separate चीज़ें भेजते हैं: एक placeholder (@name) वाला एक fixed SQL template, और इसे bind करने के लिए value। Database पहले template compile करता है, इसकी structure decide करते हुए, और सिर्फ़ तब value को strictly data की तरह slot in करता है, एक plain string जो store करनी है, कभी SQL की तरह re-interpreted नहीं। तो user जो भी type करे, quotes, semicolons, एक पूरा DROP TABLE statement, यह सिर्फ़ हमेशा text की तरह store हो सकता है, execute नहीं। यह वही protection है जो PHP के prepared statements की है: query की structure को इसकी values से separate कीजिए, और injection impossible हो जाता है। Structure और data को separately travel करना पड़ता है; parameters वह तरीका हैं जिससे ADO.NET इन्हें अलग रखता है।
Summary
Key takeaways
- Connection + Command + ExecuteReader से पढ़िए: एक DataReader पाइए और loop कीजिए जब तक reader.Read() rows के through advance करता रहे।
- Data बदलिए (insert, update, delete) ExecuteNonQuery से, जो affected rows की number return करता है: एक single insert के लिए 1।
- ExecuteScalar एक single value return करता है (जैसे एक COUNT); ExecuteReader rows return करता है; ExecuteNonQuery एक count return करता है।
- User input के लिए हमेशा parameterised queries (@name cmd.Parameters के through) इस्तेमाल कीजिए; input को कभी SQL में concatenate मत कीजिए।
- Concatenation SQL injection allow करता है; parameters user text को strictly data की तरह भेजते हैं, तो यह कभी SQL की तरह नहीं चल सकता।
- हमेशा connection close कीजिए; एक using block यह automatically करता है, error पर भी।
- Memory hook: ExecuteReader से पढ़िए, ExecuteNonQuery से बदलिए, और हर user value parameterise कीजिए।