בגרות במדעי המחשב תכנות באינטרנט 899282/899372 - קיץ 2021
נבחנים אקסטרניים · שאלון 899282/899372 · 9 שאלות · 2 שעות · פתרון מלא לכל סעיף
בחינת הבגרות במדעי המחשב, תכנות באינטרנט, קיץ 2021, כל 9 השאלות כמו בשאלון. לכל סעיף יש תשובה ופתרון מלא: הרעיון, הקוד או המעקב, ובדיקה קצרה. כל קוד מופיע ב-Java וב-C#. נסו לפתור לבד, ואז פתחו את הפתרון.
הוראות הבחינה (כמו בשאלון)
שים לב: בבחינה זו יש הנחיות מיוחדות.
יש לענות על השאלות על פי הנחיות אלה.
הוראות לנבחן
א. משך הבחינה: שעתיים.
ב. מבנה השאלון ומפתח ההערכה:
בשאלון זה שלושה פרקים.
יש לבחור בחמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות (20×5). סך הכול — 100 נקודות.
ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנוּת.
כתוב במחברת הבחינה בלבד. רשוֹם "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.
ההנחיות בשאלון זה מנוסחות בלשון זכר ומכוונות לנבחנות ולנבחנים כאחד.
בהצלחה!
(בראש עמוד השאלות מודפס:) בשאלון זה שלושה פרקים.
עליך לענות על חמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות.
פרק ראשון
עליך לענות על חמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות.
שאלה 1 · שאילתת SQL עם LIKE ו־OR (20 נקודות)
במסד נתונים של בית ספר יש טבלה ששמה AllStudents , ובה נתוני התלמידים.
בטבלה יש 4 עמודות:
firstName (שם פרטי), lastName (שם משפחה), age (גיל) ו־ phoneNumber (מספר טלפון).
כתוב שאילתה בשפת SQL המאחזרת את מספרי הטלפון של התלמידים שהשם הפרטי שלהם מתחיל באות "A" או ששם המשפחה שלהם מתחיל באות "B".
כתוב שאילתה בשפת SQL המאחזרת את מספרי הטלפון של התלמידים שהשם הפרטי שלהם מתחיל באות "A" או ששם המשפחה שלהם מתחיל באות "B".
◂ פתרון מלא לשאלה 1
פתרון
SELECT phoneNumber FROM AllStudents WHERE firstName LIKE 'A%' OR lastName LIKE 'B%';
מה נדרש: לשלוף רק את העמודה phoneNumber, עבור תלמידים שמקיימים לפחות אחד משני תנאים על תחילת השם.
רעיון:
SELECT phoneNumber.LIKE ותבנית: 'A%' – הסימן % מייצג רצף כלשהו של תווים (גם ריק) אחרי ה־A.OR: מספיק שאחד התנאים יתקיים.פתרון:
SELECT phoneNumber
FROM AllStudents
WHERE firstName LIKE 'A%' OR lastName LIKE 'B%';בדיקה: טבלה עם 5 תלמידים:
| firstName | lastName | phoneNumber | נבחר? |
|---|---|---|---|
| Avi | Cohen | 050-1111111 | כן (A) |
| Dana | Barak | 050-2222222 | כן (B) |
| Amit | Ben-David | 050-3333333 | כן (שני התנאים – מופיע פעם אחת) |
| Moshe | Levi | 050-4444444 | לא |
| Bella | Abutbul | 050-5555555 | לא (B בשם הפרטי ו־A בשם המשפחה – הפוך מהנדרש) |
השאילתה החזירה בדיוק שלושה מספרים: 050-1111111, 050-2222222, 050-3333333.
הערה על הניב: ב־SQL Server התו הכללי הוא %. במסד Access (בתוך Access עצמו) התו הכללי הוא *: LIKE 'A*'. בשאילתה שנשלחת מקוד ASP.NET עם ADO.NET משתמשים ב־%. ב־SQL Server (בהגדרת ברירת המחדל) ההשוואה ב־LIKE אינה תלויה בגודל האות, ולכן גם שם כמו avi ייבחר.
טעות נפוצה: לכתוב AND במקום OR – אז יוחזרו רק תלמידים שמקיימים את שני התנאים יחד. טעות נוספת: firstName = 'A' – זו השוואה לשם שהוא בדיוק האות A, ולא "מתחיל ב־A".
שאלה 2 · העצם Application (20 נקודות)
איזה מן המשפטים שלפניך נכון בנוגע לעצם (יישום) Application ?
העתק למחברתך את התשובה הנכונה מבין האפשרויות א–ד שלפניך.
א. Application נועד לשמור על נתונים של משתמש מסוים.
ב. Application שומר על הנתונים כל עוד היישום פתוח אצל משתמש אחד לפחות.
ג. Application נועד לשמור על נתונים במצבים שבהם השרת נופל.
ד. Application מאפשר שליחה ישירה של נתונים ללא שימוש ב־ form .
העתק למחברתך את התשובה הנכונה מבין האפשרויות א–ד.
◂ פתרון מלא לשאלה 2
פתרון
ב. Application שומר על הנתונים כל עוד היישום פתוח אצל משתמש אחד לפחות.
מה נדרש: לבחור את המשפט הנכון על העצם Application.
רעיון: בשרת ASP.NET יש שני "מחסנים" עיקריים לשמירת נתונים בין בקשות:
הנתונים שב־Application נשמרים בזיכרון של השרת.
בדיקת האפשרויות:
| אפשרות | נכון? | הסבר |
|---|---|---|
| א | לא | נתונים של משתמש מסוים נשמרים ב־Session. Application משותף לכולם. |
| ב | כן | Application הוא משותף ונשאר בחיים כל עוד היישום פעיל בשרת – הוא לא נמחק כשמשתמש מסוים עוזב, כל עוד האתר "חי" (יש בו משתמשים). בשרת IIS, אם אין באתר משתמשים זמן מה, היישום נסגר והנתונים שב־Application נמחקים. |
| ג | לא | Application נשמר בזיכרון השרת; אם השרת נופל או מופעל מחדש – הנתונים אובדים. לשמירה קבועה משתמשים במסד נתונים. |
| ד | לא | Application הוא אחסון בצד השרת; הוא לא שולח נתונים מהדפדפן לשרת. נתונים מהמשתמש נשלחים בטופס (form) או בכתובת (QueryString). |
תשובה: ב.
הערה: בדיוק רב יותר – Application קיים כל עוד היישום פועל בשרת (מרגע עלייתו ועד שהוא נסגר או מופעל מחדש), והוא אינו תלוי במשתמש מסוים. מבין ארבע האפשרויות, ב היא היחידה שמתארת נכון את המשותפוּת שלו ואת אורך חייו.
טעות נפוצה: לבלבל בין Application ל־Session (אפשרות א).
שאלה 3 · פלט HTML של לולאה – טבלה (20 נקודות)
העתק למחברתך את מה שיוצג בדפדפן לאחר הרצת הפונקציה Func שלפניך.
public void Func ()
{
string str = "<table border = '1'>";
for (int i = 1; i <= 50; i++)
{
if (i % 7 == 0)
{
str += "<tr>";
str += "<td>" + i + "</td>";
str += "</tr>";
}
}
str += "</table>";
Response.Write(str);
}העתק למחברתך את מה שיוצג בדפדפן לאחר הרצת הפונקציה Func.
◂ פתרון מלא לשאלה 3
פתרון
טבלה עם מסגרת, עמודה אחת ו־7 שורות: 7, 14, 21, 28, 35, 42, 49 (כל מספר בשורה משלו).
מה נדרש: לבנות את מחרוזת ה־HTML שהפונקציה כותבת, ולתאר איך הדפדפן מציג אותה.
רעיון: הלולאה עוברת על i מ־1 עד 50. רק כאשר i % 7 == 0 (i מתחלק ב־7) נוספת למחרוזת שורה חדשה (<tr>) עם תא אחד (<td>) שבו הערך של i. לפני הלולאה נפתחת טבלה עם מסגרת (border = '1') ואחריה הטבלה נסגרת.
מעקב: המספרים בין 1 ל־50 שמתחלקים ב־7:
| i | i % 7 | נוסף לטבלה |
|---|---|---|
| 1–6 | ≠ 0 | – |
| 7 | 0 | <tr><td>7</td></tr> |
| 14 | 0 | <tr><td>14</td></tr> |
| 21 | 0 | <tr><td>21</td></tr> |
| 28 | 0 | <tr><td>28</td></tr> |
| 35 | 0 | <tr><td>35</td></tr> |
| 42 | 0 | <tr><td>42</td></tr> |
| 49 | 0 | <tr><td>49</td></tr> |
| 50 | 1 | – (והלולאה מסתיימת) |
הפלט שנשלח לדפדפן:
<table border = '1'><tr><td>7</td></tr><tr><td>14</td></tr><tr><td>21</td></tr><tr><td>28</td></tr><tr><td>35</td></tr><tr><td>42</td></tr><tr><td>49</td></tr></table>מה יוצג בדפדפן: טבלה עם קווי מסגרת, עמודה אחת ו־7 שורות, בכל שורה מספר אחד:
+----+
| 7 |
+----+
| 14 |
+----+
| 21 |
+----+
| 28 |
+----+
| 35 |
+----+
| 42 |
+----+
| 49 |
+----+טעות נפוצה: לצייר את כל המספרים באותה שורה – אבל <tr> נפתח מחדש בכל מספר, ולכן כל מספר בשורה נפרדת. טעות נוספת: לכלול את 50 או את 0 – הלולאה מתחילה ב־1, ו־50 אינו מתחלק ב־7.
פרק שני
עליך לענות על חמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות.
שאלה 4 · בדיקת סיסמה חזקה ב־JavaScript (20 נקודות)
כדי שסיסמה תיחשב לסיסמה חזקה עליה לעמוד בדרישות האלה:
בטופס הרשמה לאתר כלשהו יש שדה למילוי סיסמה, והוא רשום כך:
<input type="password" maxlength=20 id="password" name="password" />בעמוד הבא מוצגת הפונקציה isStrongPassword ב־ JavaScript . הפונקציה מחזירה true אם הסיסמה שהתקבלה היא סיסמה חזקה, אחרת היא מחזירה false .
המשתנה lowerCaseLetters בפונקציה נועד לשמור על מספר האותיות הקטנות, המשתנה upperCaseLetters נועד לשמור על מספר האותיות הגדולות, והמשתנה digits נועד לשמור על מספר הספרות.
בפונקציה חסרים חמישה ביטויים. הביטויים החסרים מסומנים במספרים [1]–[5] .
כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[5] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.
function isStrongPassword ()
{
var password = document.getElementById(___[1]___).value;
if (password.length < 8)
return false;
var lowerCaseLetters = 0;
var upperCaseLetters = 0;
var digits = 0;
for (var i = 0; i < password.length; i++)
{
if (___[2]___)
digits ++;
else if (password.charAt(i) >= 'a' && password.charAt(i) <= 'z')
___[3]___
else if (___[4]___)
upperCaseLetters ++;
else
return ___[5]___;
}
return digits > 0 && lowerCaseLetters > 0 && upperCaseLetters > 0;
}כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[5] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 4
פתרון
[1] "password" [2] password.charAt(i) >= '0' && password.charAt(i) <= '9' [3] lowerCaseLetters ++; [4] password.charAt(i) >= 'A' && password.charAt(i) <= 'Z' [5] false
מה נדרש: להשלים חמישה ביטויים בפונקציה שבודקת אם הסיסמה שבשדה היא סיסמה חזקה.
רעיון:
id="password". הפרמטר של getElementById הוא מחרוזת, ולכן צריך מירכאות.'0' ל־'9' (השוואת תווים לפי הסדר שלהם בטבלת התווים).lowerCaseLetters (זו פקודה שלמה, כולל ;).'A' ל־'Z'.false.פתרון:
| מספר | הביטוי החסר |
|---|---|
| [1] | "password" |
| [2] | password.charAt(i) >= '0' && password.charAt(i) <= '9' |
| [3] | lowerCaseLetters ++; |
| [4] | password.charAt(i) >= 'A' && password.charAt(i) <= 'Z' |
| [5] | false |
הפונקציה המלאה:
function isStrongPassword ()
{
var password = document.getElementById("password").value;
if (password.length < 8)
return false;
var lowerCaseLetters = 0;
var upperCaseLetters = 0;
var digits = 0;
for (var i = 0; i < password.length; i++)
{
if (password.charAt(i) >= '0' && password.charAt(i) <= '9')
digits ++;
else if (password.charAt(i) >= 'a' && password.charAt(i) <= 'z')
lowerCaseLetters ++;
else if (password.charAt(i) >= 'A' && password.charAt(i) <= 'Z')
upperCaseLetters ++;
else
return false;
}
return digits > 0 && lowerCaseLetters > 0 && upperCaseLetters > 0;
}בדיקה: מה שמוצג בדפדפן:
| סיסמה | תוצאה | סיבה |
|---|---|---|
Abcdefg1 | true | 8 תווים, יש ספרה, קטנה וגדולה |
Abcdef1 | false | רק 7 תווים |
abcdefg1 | false | אין אות גדולה |
ABCDEFG1 | false | אין אות קטנה |
Abcdefgh | false | אין ספרה |
Abcd efg1 | false | רווח – תו אסור ([5]) |
Abcdefg1! | false | ! – תו אסור ([5]) |
12345678aB | true |
טעות נפוצה: לכתוב ב־[1] password בלי מירכאות (זה שם של משתנה שעדיין לא קיים, ולא ה־id של השדה). טעות נוספת: ב־[2] להשוות למספרים >= 0 && <= 9 במקום לתווים '0' ו־'9'.
שאלה 5 · הצגת טבלה ממסד נתונים – כלבים ממין זכר (20 נקודות)
במסד נתונים של מרפאה וטרינרית יש טבלה ששמה TableOfDogs , ובה נתוני הכלבים המטופלים במרפאה.
העמודות בטבלה הן: dogName (שם הכלב) מטיפוס מחרוזת, age (גיל הכלב) מטיפוס שלם, gender (מין הכלב — זכר או נקבה) מטיפוס מחרוזת, breed (גזע הכלב) מטיפוס מחרוזת ו־ idNumber (מספר הזיהוי של הכלב) מטיפוס שלם.
המרפאה רוצה להציג את הנתונים של כלבים ממין זכר בלבד, בעזרת הפונקציה PrintTable .
הפונקציה מקבלת מחרוזת התחברות למסד הנתונים — connectionString , ומציגה בטבלה את השם, הגיל, הגזע ומספר הזיהוי של כל אחד מן הכלבים שממין זכר.
דוגמה לטבלה שהפונקציה מציגה:
| שם הכלב | גיל | גזע | מספר זיהוי |
|---|---|---|---|
| שוקו | 6 | אמסטף | 1253 |
| ספייקי | 8 | פינצ'ר | 5436 |
| לאסי | 5 | קולי | 8564 |
הפונקציה PrintTable מוצגת לפניך, וחסרים בה שישה ביטויים. הביטויים החסרים מסומנים במספרים [1]–[6] .
כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[6] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.
protected void PrintTable(string connectionString)
{
DataSet ds = new DataSet();
SqlConnection connection = new SqlConnection(___[1]___);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = " ___[2]___ FROM ___[3]___ ";
cmd.Connection = connection;
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds) ;
connection.Close();
string resultStr = "<table>";
resultStr += "<tr><th> שם הכלב </th><th>___[4]___</th><th> גזע </th><th> מספר זיהוי</th></tr>";
foreach (___[5]___)
{
resultStr += "<tr>";
resultStr += "<td>" + dr["dogName"] + "</td>";
resultStr += "<td>" + dr["age"] + "</td>";
resultStr += "<td>" + dr["breed"] + "</td>";
resultStr += "<td>" + dr[___[6]___] + "</td>";
resultStr += "</tr>";
}
resultStr += "</table>";
Response.Write (resultStr);
}כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[6] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.
◂ פתרון מלא לשאלה 5
פתרון
[1] connectionString [2] SELECT dogName, age, breed, idNumber [3] TableOfDogs WHERE gender = 'זכר' [4] גיל [5] DataRow dr in ds.Tables[0].Rows [6] "idNumber"
מה נדרש: להשלים שישה ביטויים כך שהפונקציה תשלוף מהמסד את הכלבים ממין זכר ותציג אותם בטבלת HTML.
רעיון:
gender הוא "זכר". שימו לב: התנאי WHERE בא אחרי שם הטבלה, ולכן הוא חלק מ־[3].dr["age"] שבתא השני).ds.Tables[0].Rows, כשכל שורה היא DataRow בשם dr (השם dr משמש בגוף הלולאה)."idNumber" (שם עמודה הוא מחרוזת, ולכן במירכאות).פתרון:
| מספר | הביטוי החסר |
|---|---|
| [1] | connectionString |
| [2] | SELECT dogName, age, breed, idNumber |
| [3] | TableOfDogs WHERE gender = 'זכר' |
| [4] | גיל |
| [5] | DataRow dr in ds.Tables[0].Rows |
| [6] | "idNumber" |
אפשר גם ב־[2] SELECT * (שולף את כל העמודות, והלולאה משתמשת רק בארבע מהן).
הפונקציה המלאה:
protected void PrintTable(string connectionString)
{
DataSet ds = new DataSet();
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = " SELECT dogName, age, breed, idNumber FROM TableOfDogs WHERE gender = 'זכר' ";
cmd.Connection = connection;
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds) ;
connection.Close();
string resultStr = "<table>";
resultStr += "<tr><th> שם הכלב </th><th>גיל</th><th> גזע </th><th> מספר זיהוי</th></tr>";
foreach (DataRow dr in ds.Tables[0].Rows)
{
resultStr += "<tr>";
resultStr += "<td>" + dr["dogName"] + "</td>";
resultStr += "<td>" + dr["age"] + "</td>";
resultStr += "<td>" + dr["breed"] + "</td>";
resultStr += "<td>" + dr["idNumber"] + "</td>";
resultStr += "</tr>";
}
resultStr += "</table>";
Response.Write (resultStr);
}בדיקה: טבלה עם 5 כלבים (3 זכרים מהדוגמה ו־2 נקבות). הפונקציה הציגה בדיוק את שלוש השורות שבדוגמה (שוקו, ספייקי, לאסי) ולא הציגה את הנקבות:
<tr><td>שוקו</td><td>6</td><td>אמסטף</td><td>1253</td></tr>
<tr><td>ספייקי</td><td>8</td><td>פינצ'ר</td><td>5436</td></tr>
<tr><td>לאסי</td><td>5</td><td>קולי</td><td>8564</td></tr>הנחה: הערך שנשמר בעמודה gender לכלב זכר הוא המחרוזת 'זכר' (השאלה אומרת "זכר או נקבה").
טעות נפוצה: לכתוב את התנאי WHERE ב־[2] – אז הוא יופיע לפני FROM והשאילתה תהיה שגויה. טעות נוספת: לשכוח את המירכאות סביב 'זכר' (מחרוזת ב־SQL נכתבת בגרש בודד).
protected void PrintTable(string connectionString)
{
DataSet ds = new DataSet();
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = " SELECT dogName, age, breed, idNumber FROM TableOfDogs WHERE gender = 'זכר' ";
cmd.Connection = connection;
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds) ;
connection.Close();
string resultStr = "<table>";
resultStr += "<tr><th> שם הכלב </th><th>גיל</th><th> גזע </th><th> מספר זיהוי</th></tr>";
foreach (DataRow dr in ds.Tables[0].Rows)
{
resultStr += "<tr>";
resultStr += "<td>" + dr["dogName"] + "</td>";
resultStr += "<td>" + dr["age"] + "</td>";
resultStr += "<td>" + dr["breed"] + "</td>";
resultStr += "<td>" + dr["idNumber"] + "</td>";
resultStr += "</tr>";
}
resultStr += "</table>";
Response.Write (resultStr);
}שאלה 6 · הפניה לדפים לפי משתני Session (20 נקודות)
באתר משרד הבריאות הוגדרו שלושה משתני Session :
Session["age"] מספר שלם — גיל האדם, Session["wasSick"] בוליאני — היה חולה,
ו־ Session["wasVaccinated"] בוליאני — חוּסן.
כתוב קטע קוד המפנה את האדם המשתמש באתר לדף המתאים לו, על פי הוראות אלה:
כתוב קטע קוד המפנה את האדם המשתמש באתר לדף המתאים לו, על פי ההוראות.
◂ פתרון מלא לשאלה 6
פתרון
if (wasSick || wasVaccinated) → greenCard.aspx; else if (age >= 16) → getVaccine.aspx; else → covidInstructions.aspx
מה נדרש: לקרוא את שלושת משתני ה־Session ולהפנות (Response.Redirect) לאחד משלושה דפים.
רעיון:
object, ולכן צריך המרה (casting) לטיפוס המקורי: (int) לגיל, (bool) לשני הדגלים.>= 16) – getVaccine.aspx.else if, בבדיקה השנייה כבר ידוע שהאדם לא היה חולה ולא חוסן, ולכן אין צורך לבדוק זאת שוב.פתרון:
int age = (int)Session["age"];
bool wasSick = (bool)Session["wasSick"];
bool wasVaccinated = (bool)Session["wasVaccinated"];
if (wasSick || wasVaccinated)
Response.Redirect("greenCard.aspx");
else if (age >= 16)
Response.Redirect("getVaccine.aspx");
else
Response.Redirect("covidInstructions.aspx");בדיקה: כל הצירופים של גיל (10, 15, 16, 40) × היה חולה (כן/לא) × חוסן (כן/לא) – 16 מקרים. לדוגמה:
| age | wasSick | wasVaccinated | הדף |
|---|---|---|---|
| 40 | false | false | getVaccine.aspx |
| 16 | false | false | getVaccine.aspx (16 כלול – "בן 16 ומעלה") |
| 15 | false | false | covidInstructions.aspx |
| 10 | false | true | greenCard.aspx |
| 40 | true | false | greenCard.aspx |
כל 16 המקרים התאימו להוראות.
טעות נפוצה: לכתוב age > 16 – אז בן 16 בדיוק יופנה בטעות ל־covidInstructions.aspx. טעות נוספת: להשוות את ערכי ה־Session בלי המרה, למשל Session["age"] >= 16 – זו שגיאת קומפילציה, כי object אינו מספר.
int age = (int)Session["age"];
bool wasSick = (bool)Session["wasSick"];
bool wasVaccinated = (bool)Session["wasVaccinated"];
if (wasSick || wasVaccinated)
Response.Redirect("greenCard.aspx");
else if (age >= 16)
Response.Redirect("getVaccine.aspx");
else
Response.Redirect("covidInstructions.aspx");שאלה 7 · מונה לחיצות – שמירת מצב בין בקשות (20 נקודות)
בדף Default.aspx יש תוכנית שמטרתה להציג בעזרת הפונקציה Page_Load את מספר הפעמים שלחצו על הכפתור – button (המספר גדל ב־ 1 בכל לחיצה). התוכנית רצה אך הפונקציה Page_Load אינה מבצעת את הנדרש.
הסבר מדוע הפונקציה אינה מבצעת את הנדרש.
כתוב את הפונקציה Page_Load מחדש כך שתבצע את הנדרש (אפשר לשנות ולהוסיף שורות קוד בפונקציה).
<%@ Page Language="C#" %>
<!DOCTYPE html>
<script runat="server">
public int num;
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
num++;
else
num=0;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form id="form1" action="Default.aspx" method="get" runat="server">
<input name="button" type="submit" value="press here" />
</form>
<div>
The number of clicks is <% =num %>
</div>
</body>
</html>הסבר מדוע הפונקציה אינה מבצעת את הנדרש.
כתוב את הפונקציה Page_Load מחדש כך שתבצע את הנדרש (אפשר לשנות ולהוסיף שורות קוד בפונקציה).
◂ פתרון מלא לשאלה 7
פתרון
בכל בקשה לשרת נוצר עצם דף חדש ו־num מתחיל שוב מ־0, ולכן אחרי כל לחיצה מוצג 1. יש לשמור את המונה ב־Session (או ViewState) בין הבקשות.
מה נדרש: (1) להסביר למה המונה לא עולה; (2) לתקן את Page_Load.
הסבר – למה זה לא עובד:
num (שדה של הדף) אינו זוכר את ערכו מהבקשה הקודמת: בכל בקשה הוא נוצר מחדש עם הערך 0.IsPostBack הוא false ⇒ num=0. בכל לחיצה IsPostBack הוא true ⇒ num++ מגדיל את 0 ל־1.רעיון לתיקון: לשמור את המונה במקום שנשמר בין בקשות של אותו משתמש – ב־Session. בכל לחיצה קוראים את הערך הקודם מה־Session, מגדילים ב־1, ושומרים בחזרה. ב־num נשאר הערך להצגה (<% =num %>).
פתרון:
public int num;
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
num = (int)Session["num"];
num++;
}
else
num = 0;
Session["num"] = num;
}בדיקה: סימלצנו רצף בקשות (כל בקשה = עצם דף חדש, אותו Session): טעינה, 3 לחיצות, טעינה מחדש, לחיצה.
| בקשה | IsPostBack | הקוד המקורי מציג | הקוד המתוקן מציג |
|---|---|---|---|
| טעינה ראשונה | false | 0 | 0 |
| לחיצה 1 | true | 1 | 1 |
| לחיצה 2 | true | 1 | 2 |
| לחיצה 3 | true | 1 | 3 |
| טעינה חדשה (לא לחיצה) | false | 0 | 0 |
| לחיצה | true | 1 | 1 |
הערות:
ViewState["num"] (נשמר בתוך הדף עצמו בין הלחיצות) – גם זה פתרון נכון.runat="server" מוסיף לדף שדות נסתרים (כמו __VIEWSTATE), ובשיטה get הם נשלחים בכתובת (QueryString). לפיהם השרת מזהה שהבקשה היא PostBack, ולכן IsPostBack אכן true בלחיצות.טעות נפוצה: להחליף את public int num; ב־public int num = 0; או להעביר את האתחול – זה לא עוזר, כי הבעיה היא שהעצם כולו נוצר מחדש בכל בקשה.
public int num;
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
num = (int)Session["num"];
num++;
}
else
num = 0;
Session["num"] = num;
}פרק שלישי
עליך לענות על חמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות.
שאלה 8 · טופס כניסה – Session והפניות (20 נקודות)
בטופס כניסה לאתר כלשהו נדרש המשתמש להכניס שם משתמש וסיסמה לשדות האלה:
<input type="text" name="id" id="id"/>
<input type="password" name="password" id="password" />לפניך הפונקציה Direct . הפונקציה מקבלת מחרוזת — connectionString המתקשרת לבסיס הנתונים של האתר.
בבסיס הנתונים יש טבלה ששמה Registers , ובה העמודה id מטיפוס מחרוזת והעמודה password מטיפוס מחרוזת.
הפונקציה רצה ללא שגיאות.
protected void Direct(string connectionString){
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = new SqlCommand();
DataSet ds = new DataSet();
if (Request.Form["id"] != null)
{
command.CommandText = string.Format("Select * From Registers Where id = '{0}'", Request.
Form["id"]);
command.Connection= connection;
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(ds);
connection.Close();
if (ds.Tables[0].Rows.Count == 0)
{
Session["Key"] = 0;
Response.Redirect("Page1.aspx");
}
else if (ds.Tables[0].Rows[0]["password"].Equals (Request.Form["password"]))
{
Session["Key"] = 2;
Response.Redirect("Page2.aspx");
}
else
Session["Key"] = 1;
}
}בעבור כל אחד מארבעת ההיגדים שלפניך רשוֹם אם הוא נכון או לא נכון, ונמק.
בעבור כל אחד מארבעת ההיגדים 1–4 רשוֹם אם הוא נכון או לא נכון, ונמק.
◂ פתרון מלא לשאלה 8
פתרון
1 – לא נכון; 2 – לא נכון; 3 – לא נכון; 4 – נכון.
מה נדרש: להבין מה הפונקציה עושה בכל מקרה, ולקבוע לכל היגד אם הוא נכון.
מה הפונקציה עושה: אם נשלח טופס (Request.Form["id"] != null), היא שולפת מהטבלה Registers את השורה שבה id שווה לשם המשתמש שהוקלד, ואז:
| מקרה | Session["Key"] | הפניה |
|---|---|---|
| שם המשתמש לא קיים בטבלה (0 שורות) | 0 | Page1.aspx |
| המשתמש קיים והסיסמה נכונה | 2 | Page2.aspx |
| המשתמש קיים והסיסמה שגויה | 1 | אין הפניה – נשארים בדף |
ההיגדים:
ds.Tables[0].Rows[0]["password"].Equals(Request.Form["password"]) מתקיים, והמשתמש מופנה ל־Page2.aspx. (הערך Rows[0]["password"] הוא מטיפוס object, אבל בתוכו יש מחרוזת, ולכן Equals משווה את תוכן המחרוזות.)Rows.Count == 0). אם הסיסמה שגויה (והמשתמש קיים) – מתבצע ה־else: Session["Key"] = 1 ואין שום הפניה; המשתמש נשאר בדף הנוכחי.Session["Key"] מקבל את הערך 2. הערך 1 ניתן כשהסיסמה שגויה.Session["Key"] מקבל 2 רק אחרי כניסה מוצלחת, וה־Session נשמר לכל משתמש בנפרד. לכן ב־Page2.aspx אפשר לבדוק אותו ולהחזיר כל מי שלא נכנס כראוי, למשל (שם דף הכניסה – Login.aspx – הוא לדוגמה):protected void Page_Load(object sender, EventArgs e)
{
if (Session["Key"] == null || (int)Session["Key"] != 2)
Response.Redirect("Login.aspx");
}בדיקה: הפונקציה Direct כפי שהיא מודפסת, עם טבלת Registers (dana / Dana2021):
| id | password | תוצאה |
|---|---|---|
| dana | Dana2021 | הפניה ל־Page2.aspx, Key=2 |
| dana | wrong | נשאר בדף, Key=1 |
| noone | x | הפניה ל־Page1.aspx, Key=0 |
| (טופס לא נשלח) | נשאר בדף, Key לא נקבע |
ובדיקת השמירה על Page2.aspx: בלי Key, או עם Key=1 – הפניה ל־Login.aspx; עם Key=2 – הכניסה מותרת.
טעות נפוצה: לחשוב שהמשתמש מופנה ל־Page1.aspx כשהסיסמה שגויה – כדאי לעקוב בזהירות אחרי שלושת הענפים של ה־if.
protected void Page_Load(object sender, EventArgs e)
{
if (Session["Key"] == null || (int)Session["Key"] != 2)
Response.Redirect("Login.aspx");
}שאלה 9 · הודעה לכל משתמש אלפי – Application ו־Session (20 נקודות)
לפניך הפונקציה Page_Load :
protected void Page_Load(object sender, EventArgs e)
{
if (Session["firstlog"] == null)
{
Application.Lock();
if (Application["myCount"] == null)
Application["myCount"] = 0;
Application["myCount"] = (int)Application["myCount"]+ 1;
Application.UnLock();
Session["firstlog"] = "true";
}
}הוסף בפונקציה קטע קוד המציג באמצעות העצם (האובייקט) Response בעבור המשתמש ה־ 1,000 , ה־ 2,000 , ה־ 3,000 וכן הלאה (כל 1,000 משתמשים נוספים) את ההודעה שלפניך: ברוך הבא המשתמש ה־ x (x הוא מספר המשתמש).
לדוגמה, בעבור המשתמש ה־ 1,000 תוצג ההודעה: ברוך הבא המשתמש ה־ 1,000 .
כתוב במחברתך את הפונקציה המתוקנת.
כתוב במחברתך את הפונקציה המתוקנת.
◂ פתרון מלא לשאלה 9
פתרון
בתוך ה־Lock שומרים int x = (int)Application["myCount"]; ואחרי ה־UnLock: if (x % 1000 == 0) Response.Write("ברוך הבא המשתמש ה־ " + x);
מה נדרש: להוסיף לפונקציה הצגת הודעה למשתמש שמספרו הסידורי הוא כפולה של 1,000.
מה הפונקציה עושה כבר: Application["myCount"] הוא מונה משותף לכל המשתמשים. Session["firstlog"] הוא סימון אישי לכל משתמש – בפעם הראשונה שהמשתמש מגיע הוא null, ואז המונה גדל ב־1 והמשתמש מסומן, כך שכל משתמש נספר פעם אחת בלבד. לכן הערך של המונה מיד אחרי ההגדלה הוא מספר המשתמש הנוכחי.
רעיון:
UnLock, שומרים את ערך המונה במשתנה מקומי x. כך מובטח ש־x הוא המספר של המשתמש הזה – משתמש אחר לא יכול לשנות את המונה בין ההגדלה לקריאה.x % 1000 == 0 (1000, 2000, 3000, ...).Response.Write.firstlog, כי רק שם המשתמש מקבל את מספרו.פתרון:
protected void Page_Load(object sender, EventArgs e)
{
if (Session["firstlog"] == null)
{
Application.Lock();
if (Application["myCount"] == null)
Application["myCount"] = 0;
Application["myCount"] = (int)Application["myCount"]+ 1;
int x = (int)Application["myCount"];
Application.UnLock();
Session["firstlog"] = "true";
if (x % 1000 == 0)
Response.Write("ברוך הבא המשתמש ה־ " + x);
}
}בדיקה: סימלצנו 3,500 משתמשים שונים, וכל אחד טוען את הדף פעמיים. ההודעה הוצגה בדיוק 3 פעמים – למשתמשים 1000, 2000 ו־3000, בטעינה הראשונה שלהם – ובסוף המונה היה 3500 (הטעינה השנייה של כל משתמש לא נספרה).
הערות:
Session["userNum"] = x;) ולבדוק אותו מחוץ ל־if.טעות נפוצה: לבדוק (int)Application["myCount"] % 1000 == 0 אחרי UnLock – בין השחרור לבדיקה משתמש אחר עלול להגדיל את המונה, וההודעה תוצג למשתמש הלא נכון (או לא תוצג כלל). טעות נוספת: לשים את הבדיקה מחוץ ל־if – אז משתמש שמרענן את הדף יקבל את ההודעה של מישהו אחר.
protected void Page_Load(object sender, EventArgs e)
{
if (Session["firstlog"] == null)
{
Application.Lock();
if (Application["myCount"] == null)
Application["myCount"] = 0;
Application["myCount"] = (int)Application["myCount"]+ 1;
int x = (int)Application["myCount"];
Application.UnLock();
Session["firstlog"] = "true";
if (x % 1000 == 0)
Response.Write("ברוך הבא המשתמש ה־ " + x);
}
}טבלת התשובות
| שאלה | סעיף | תשובה |
|---|---|---|
| 1 | - | SELECT phoneNumber FROM AllStudents WHERE firstName LIKE 'A%' OR lastName LIKE 'B%'; |
| 2 | - | ב. Application שומר על הנתונים כל עוד היישום פתוח אצל משתמש אחד לפחות. |
| 3 | - | טבלה עם מסגרת, עמודה אחת ו־7 שורות: 7, 14, 21, 28, 35, 42, 49 (כל מספר בשורה משלו). |
| 4 | - | [1] "password" [2] password.charAt(i) >= '0' && password.charAt(i) <= '9' [3] lowerCaseLetters ++; [4] password.charAt(i) >= 'A' && password.charAt(i) <= 'Z' [5] false |
| 5 | - | [1] connectionString [2] SELECT dogName, age, breed, idNumber [3] TableOfDogs WHERE gender = 'זכר' [4] גיל [5] DataRow dr in ds.Tables[0].Rows [6] "idNumber" |
| 6 | - | if (wasSick || wasVaccinated) → greenCard.aspx; else if (age >= 16) → getVaccine.aspx; else → covidInstructions.aspx |
| 7 | - | בכל בקשה לשרת נוצר עצם דף חדש ו־num מתחיל שוב מ־0, ולכן אחרי כל לחיצה מוצג 1. יש לשמור את המונה ב־Session (או ViewState) בין הבקשות. |
| 8 | - | 1 – לא נכון; 2 – לא נכון; 3 – לא נכון; 4 – נכון. |
| 9 | - | בתוך ה־Lock שומרים int x = (int)Application["myCount"]; ואחרי ה־UnLock: if (x % 1000 == 0) Response.Write("ברוך הבא המשתמש ה־ " + x); |