דילוג לתוכן המרכזי

מתכונת 3 במדעי המחשב - תכנות באינטרנט 899372

נבחנים אקסטרניים · שאלון 899372 · 9 שאלות · 2 שעות · פתרון מלא לכל סעיף

מבחן מתכונת במדעי המחשב, תכנות באינטרנט, במבנה של בחינת הבגרות. כל שאלה בו בנויה כמו שאלה מבגרות אחרת. לכל סעיף יש תשובה ופתרון מלא, וכל קוד מופיע ב-Java וב-C#. פתרו בזמן של הבחינה, ורק אחר כך פתחו את הפתרונות.

הוראות הבחינה

א. משך הבחינה: שעתיים.

ב. מבנה השאלון ומפתח הערכה: בשאלון זה שלושה פרקים.

פרק ראשון — (10x3) — 30 נקודות
פרק שני — (20x2)+(10x1) — 50 נקודות
פרק שלישי — (20x1) — 20 נקודות
סך הכול — 100 נקודות

ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנות.

יש לכתוב במחברת הבחינה בלבד, יש לרשום "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.

השאלות בשאלון זה מנוסחות בלשון רבים, אף על פי כן על כל תלמידה וכל תלמיד להשיב עליהן באופן אישי.

בהצלחה!

פרק ראשון

ענו על שלוש השאלות 3-1 (לכל שאלה – 10 נקודות).

שאלה 1 · שליחת טופס בשיטת get

בדף HTML מוגדר טופס החיפוש הזה:

HTML
<form action="search.aspx" method="get">
  <input type="text" name="city" />
  <input type="submit" value="חפש" />
</form>

מה קורה לנתוני הטופס כאשר המשתמש לוחץ על הכפתור "חפש"?
העתיקו למחברת הבחינה את התשובה הנכונה מבין האפשרויות א–ד שלפניכם.

א. הנתונים נשמרים בדפדפן בלבד ואינם נשלחים לשרת
ב. הנתונים נשלחים לשרת בגוף הבקשה, ואינם נראים בשורת הכתובת של הדפדפן
ג. הנתונים נשלחים לשרת כחלק מכתובת ה-URL (אחרי הסימן ?), ובצד השרת קוראים אותם בעזרת Request.QueryString
ד. הנתונים נשלחים ישירות למסד הנתונים, בלי לעבור בדף search.aspx

העתיקו למחברת הבחינה את התשובה הנכונה מבין האפשרויות א–ד.

(10 נקודות)
◂ פתרון מלא לשאלה 1

פתרון

תשובה

ג. הנתונים נשלחים לשרת כחלק מכתובת ה-URL (אחרי הסימן ?), ובצד השרת קוראים אותם בעזרת Request.QueryString

מה נדרש: להבין מה עושה הדפדפן עם נתוני טופס שהשיטה שלו (method) היא get.

רעיון: בלחיצה על כפתור submit הדפדפן שולח בקשה לדף שכתוב ב-action (כאן search.aspx). השיטה קובעת איפה יהיו הנתונים:

•method="get" – הנתונים מצורפים לכתובת אחרי ? בצורה name=value (כמה שדות מופרדים ב-&). בשרת קוראים אותם ב-Request.QueryString["city"].
•method="post" – הנתונים נשלחים בגוף הבקשה ואינם נראים בכתובת. בשרת קוראים אותם ב-Request.Form["city"].

פתרון: התשובה הנכונה היא ג. אם המשתמש הקליד חיפה, הדפדפן יבקש את הכתובת search.aspx?city=חיפה (האותיות העבריות מקודדות בכתובת), ובקוד השרת של search.aspx:

C#
string city = Request.QueryString["city"];   // "חיפה"

בדיקה – למה האחרות שגויות:

•א – לחיצה על submit תמיד שולחת בקשה לשרת (לדף שב-action).
•ב – זה התיאור של שיטת post, לא get.
•ד – הדפדפן אף פעם לא פונה ישירות למסד הנתונים; רק קוד השרת (בדף search.aspx) ניגש למסד.

דוגמה: מהטופס נבנית הכתובת search.aspx?city=%D7%97%D7%99%D7%A4%D7%94, ופענוח החלק שאחרי ? (מה ש-Request.QueryString עושה) מחזיר בדיוק "חיפה".

טעות נפוצה: להחליף בין get ל-post, או לקרוא נתוני get בעזרת Request.Form (מתקבל null).

שאלה 2 · מהו DataSet

מהו DataSet?

(1) טבלה בבסיס הנתונים שבה נשמרים פרטי המשתמשים הרשומים באתר.

(2) אובייקט בצד השרת המחזיק בזיכרון עותק של נתונים (טבלה אחת או יותר) שנשלפו מבסיס הנתונים, וניתן לעבור עליו גם לאחר סגירת החיבור לבסיס הנתונים.

(3) פקודת SQL הבוחרת שורות מטבלה לפי תנאי.

(4) אובייקט בצד הלקוח השומר בדפדפן את הנתונים שהמשתמש הקליד בטופס.

מהו DataSet?

(10 נקודות)
◂ פתרון מלא לשאלה 2

פתרון

תשובה

(2)

מה נדרש – לבחור את ההגדרה הנכונה של DataSet.

רעיון – לזכור את שלבי העבודה עם בסיס הנתונים בצד השרת: פותחים חיבור (SqlConnection), מגדירים פקודה (SqlCommand), ממלאים DataSet בעזרת SqlDataAdapter (Fill), סוגרים את החיבור – ורק אז עוברים על הנתונים.

פתרון

DataSet הוא אובייקט ב-C# (בצד השרת) שמחזיק בזיכרון עותק של הנתונים שנשלפו: טבלה אחת או יותר (ds.Tables[0]), ובכל טבלה שורות (ds.Tables[0].Rows[i]) ועמודות (Rows[i]["title"]). מכיוון שזה עותק בזיכרון, אפשר לסגור את החיבור לבסיס הנתונים ועדיין לקרוא ממנו.

תשובהנכון/לא נכוןלמה
(1)✘DataSet אינו טבלה בבסיס הנתונים, אלא עותק בזיכרון של השרת.
(2)✔ נכוןזו ההגדרה.
(3)✘זה תיאור של שאילתת SELECT ... WHERE. ה-DataSet מקבל את התוצאה של שאילתה כזו.
(4)✘DataSet הוא בצד השרת, ואינו קשור לטופס בדפדפן.

התשובה: (2).

בדיקה – דוגמה ב-C#: ממלאים DataSet בטבלה Books עם שתי שורות, סוגרים את החיבור, ואחר כך עוברים בלולאה על ds.Tables[0].Rows ומקבלים את שתי השורות (Alef:120, Bet:340) – הנתונים נשארים זמינים אחרי סגירת החיבור.

שאלה 3 · פלט של פעולת שרת – טבלת סכומים

לפניכם פעולה בשם Show הכתובה בצד שרת. העתיקו למחברתכם את מה שיוצג בדפדפן לאחר הרצת הפעולה Show עם הפרמטר k = 5.

C#
public void Show (int k) {
   string str = "<table border = '1'>";
   int sum = 0;
   for (int i = 1; i <= k; i++)
   {
      sum += i;
      str += "<tr><td>" + i + "</td>";
      if (sum % 2 == 0)
         str += "<td>" + sum + "</td></tr>";
      else
         str += "<td><b>Odd</b></td></tr>";
   }
   str += "</table>";
   Response.Write (str);
}

העתיקו למחברתכם את מה שיוצג בדפדפן לאחר הרצת הפעולה Show עם הפרמטר k = 5.

(10 נקודות)
◂ פתרון מלא לשאלה 3

פתרון

תשובה

טבלה עם מסגרת, 5 שורות ו־2 עמודות: (1, Odd), (2, Odd), (3, 6), (4, 10), (5, Odd); המילה Odd מוצגת בכתב מודגש

מה נדרש – לתאר את הטבלה שהדפדפן מציג (לא את קוד ה-HTML).

רעיון – בכל סיבוב של הלולאה (i מ־1 עד 5) נוספת שורה (<tr>) עם שני תאים: בתא הראשון הערך של i, ובתא השני – לפי הזוגיות של הסכום המצטבר sum = 1 + 2 + ... + i. אם sum זוגי נכתב sum, ואחרת נכתבת המילה Odd בתוך <b>, כלומר בכתב מודגש. לכן מתקבלת טבלה עם 5 שורות ו־2 עמודות.

פתרון – מעקב:

isumsum % 2 == 0 ?השורה שנוספה
11לא<tr><td>1</td><td><b>Odd</b></td></tr>
23לא<tr><td>2</td><td><b>Odd</b></td></tr>
36כן<tr><td>3</td><td>6</td></tr>
410כן<tr><td>4</td><td>10</td></tr>
515לא<tr><td>5</td><td><b>Odd</b></td></tr>

המחרוזת שנשלחת לדפדפן:

HTML
<table border = '1'><tr><td>1</td><td><b>Odd</b></td></tr><tr><td>2</td><td><b>Odd</b></td></tr><tr><td>3</td><td>6</td></tr><tr><td>4</td><td>10</td></tr><tr><td>5</td><td><b>Odd</b></td></tr></table>

מה יוצג בדפדפן – טבלה עם מסגרת, חמש שורות ושתי עמודות (Odd מודגש):

1Odd
2Odd
36
410
5Odd

בדיקה – עבור k = 5 מתקבלת בדיוק המחרוזת שלמעלה – חמישה <tr>, בכל אחד שני תאים.

טעות נפוצה: לבדוק את הזוגיות של i במקום של sum (למשל לכתוב Odd בשורה 3 ו־2 בשורה 2), או לשכוח ש-sum מצטבר ואינו מתאפס בכל סיבוב.

פרק שני

ענו על שלוש שאלות: על שאלה 4 (חובה – 10 נקודות) ועל שתיים מן השאלות 7-5 (לכל שאלה – 20 נקודות).

שאלה 4 · טבלת HTML – rowspan/colspan וקישור פנימי

מיכל, תלמידת מדעי המחשב, בנתה אתר לשכבה שלה, ובו טבלה של מערכת השעות בשבוע הטיול השנתי.

HTML
<!DOCTYPE html>
<html lang="he">
  <meta charset="utf-8"/>
  <table style="width: 260px" border=2px dir="ltr">
    <tr>
      <td colspan=3 align="center">מערכת שעות</td>
    </tr>
    <tr>
      <td>Sun</td>
      <td>Math</td>
      <td rowspan=2>Lab</td>
    </tr>
    <tr>
      <td>Mon</td>
      <td>History</td>
    </tr>
    <tr>
      <td>Tue</td>
      <td colspan=2 align="center">Trip</td>
    </tr>
  </table>
</html>
א.

כיצד יוצג הדף? כתבו במחברת הבחינה את אות התשובה הנכונה מבין האפשרויות (a-d).

ב.

הוסיפו לקוד ה-HTML את מה שנדרש כך שלחיצה על המילה Trip תעביר לדף trip.html, הנמצא באותה תיקייה שבה נמצא דף זה.

◂ פתרון מלא לשאלה 4

סעיף א

תשובה

b

מה נדרש: לבנות בראש את הטבלה מתוך הקוד ולבחור את התמונה המתאימה.

רעיון: עוברים שורה-שורה (<tr>) וסופרים את העמודות שכל תא תופס (colspan) ואת השורות (rowspan). כאן dir="ltr", ולכן התא הראשון בכל שורה נמצא בצד שמאל.

פתרון – בניית הטבלה (3 עמודות, משמאל לימין):

שורהעמודה שמאליתעמודה אמצעיתעמודה ימנית
1"מערכת שעות" – colspan=3, ממורכז←←
2SunMathLab – rowspan=2 (שורות 2–3)
3MonHistory(ממשיך Lab)
4TueTrip – colspan=2, ממורכז←

שימו לב: בשורה 3 כתובים בקוד רק שני תאים (Mon, History), כי העמודה הימנית כבר תפוסה על ידי Lab משורה 2. בשורה 4 Lab כבר הסתיים, ולכן Trip תופס את העמודה האמצעית והימנית. זו בדיוק תמונה b.

למה לא האחרות:

•a – Lab תופס שורה אחת בלבד, ובשורה של Mon נשאר חור מימין ל-History; בקוד ל-Lab יש rowspan=2.
•c – הטבלה הפוכה (Sun בצד ימין, Lab בצד שמאל): כך הייתה נראית עם dir="rtl".
•d – Lab תופס שלוש שורות ו-Trip תופס עמודה אחת בלבד; בקוד Lab הוא rowspan=2 ו-Trip הוא colspan=2.

תשובה: b.

בדיקה: רשת התאים לפי rowspan/colspan: 4 שורות ו-3 עמודות, Lab תופס את שורות 2–3 בעמודה הימנית, ו-Trip תופס את שתי העמודות האחרונות בשורה 4. את ארבע התמונות יצרנו בדפדפן, ותמונה b נוצרה מהקוד שבשאלה עצמו.

טעות נפוצה: לחשוב שבשורה 3 חסר תא ולכן יהיה בה חור (תמונה a) – התא החסר "מכוסה" על ידי ה-rowspan של Lab.

סעיף ב

תשובה

<td colspan=2 align="center"><a href="trip.html">Trip</a></td>

מה נדרש: להפוך את המילה Trip לקישור לדף אחר באותו אתר.

רעיון: קישור נוצר בתגית <a href="...">. כשהדף נמצא באותה תיקייה, מספיק לכתוב ב-href את שם הקובץ בלבד (כתובת יחסית) – אין צורך בכתובת מלאה עם https://. עוטפים את המילה Trip בתגית <a> בתוך התא הקיים, ומשאירים לתא את colspan=2 ואת היישור.

פתרון – מחליפים את השורה

HTML
<td colspan=2 align="center">Trip</td>

בשורה:

HTML
<td colspan=2 align="center"><a href="trip.html">Trip</a></td>

הדף המלא אחרי השינוי – בלשונית HTML.

בדיקה: מבנה הטבלה לא משתנה (עדיין כמו בתמונה b – Trip תופס שתי עמודות וממורכז), והמילה Trip היא עכשיו קישור ל-trip.html.

טעות נפוצה: למחוק בטעות את colspan=2 כשמשכתבים את התא – אז Trip יתפוס עמודה אחת בלבד וייווצר חור בטבלה.

שאלה 5 · ניתוב בין דפים לפי משתני Session – אתר תרגול תאוריה

באתר לתרגול מבחן התאוריה "דרך צלחה", בסוף כל סבב תרגול האתר מחליט לאיזה דף להעביר את התלמיד. לשם כך האתר משתמש במספר משתני Session:

•Session["score"]: מספר שלם המייצג את הציון (0–100) של התלמיד במבחן התרגול האחרון.
•Session["finished_all"]: משתנה בוליאני המציין האם התלמיד סיים לתרגל את כל פרקי הלימוד: כן (true) או לא (false).
•Session["sign_mistakes"]: מספר שלם המייצג את מספר הטעויות של התלמיד בשאלות על תמרורים.

בהתבסס על משתנים אלה, המשתמש עובר לדף מתאים לפי החוקיות הבאה:

– אם score גדול או שווה ל-90 וגם finished_all הוא true וגם sign_mistakes שווה ל-0, התלמיד מועבר לדף "מוכן למבחן" readyForTest.aspx.
– אחרת, אם score גדול או שווה ל-70 או ששני התנאים הבאים מתקיימים יחד:
finished_all הוא true וגם sign_mistakes קטן או שווה ל-2,
התלמיד מועבר לדף תרגול נוסף morePractice.aspx.
– שאר התלמידים מועברים לדף השיעורים lessons.aspx.

כתבו קוד המתאים לדרישות.

כתבו קוד המתאים לדרישות.

(20 נקודות)
◂ פתרון מלא לשאלה 5

פתרון

תשובה

קוראים את שלושת משתני ה-Session עם המרה (int)/(bool), ואז: if (score >= 90 && finished && signs == 0) Response.Redirect("readyForTest.aspx"); else if (score >= 70 || (finished && signs <= 2)) Response.Redirect("morePractice.aspx"); else Response.Redirect("lessons.aspx");

מה נדרש: קוד צד שרת (C#) שמעביר את התלמיד לאחד משלושה דפים לפי ערכי שלושה משתני Session.

רעיון:

1.ב-Session נשמרים ערכים מטיפוס object, ולכן קוראים כל משתנה עם המרה לטיפוס שלו: (int) למספרים ו-(bool) לבוליאני.
2.בודקים את הכללים לפי הסדר בשרשרת if / else if / else: קודם הכלל המחמיר (מוכן למבחן), אחריו תרגול נוסף, ובסוף כל השאר.
3.בכלל השני יש "או" בין score ≥ 70 לבין צירוף של שני תנאים שמתקיימים יחד – לכן ה-"וגם" (&&) נכתב בתוך סוגריים. (ב-C# && קודם ל-|| ולכן גם בלי סוגריים התוצאה זהה, אבל הסוגריים מבהירים את הכוונה.)
4.מעבר לדף אחר נעשה ב-Response.Redirect("שם הדף").

פתרון (בקובץ ה-code behind של הדף):

C#
protected void Page_Load(object sender, EventArgs e)
{
    int score = (int)Session["score"];
    bool finished = (bool)Session["finished_all"];
    int signs = (int)Session["sign_mistakes"];

    if (score >= 90 && finished && signs == 0)
        Response.Redirect("readyForTest.aspx");
    else if (score >= 70 || (finished && signs <= 2))
        Response.Redirect("morePractice.aspx");
    else
        Response.Redirect("lessons.aspx");
}

בדיקה: בכל הצירופים של הערכים הקוד מפנה לדף הנכון, לפי הכללים שבשאלה. כמה דוגמאות:

scorefinished_allsign_mistakesהדף
95true0readyForTest.aspx
95true1morePractice.aspx (טעות בתמרורים, אבל ציון ≥ 70)
75false5morePractice.aspx (מספיק ש-score ≥ 70)
60true2morePractice.aspx (ציון נמוך, אבל סיים הכול ועד 2 טעויות)
60true3lessons.aspx
90false0morePractice.aspx (לא סיים את כל הפרקים)

טעות נפוצה:

•לכתוב score > 90 במקום score >= 90 ("גדול או שווה").
•לכתוב שלושה if נפרדים במקום else if – אז תלמיד שעומד בתנאי הראשון "ממשיך" לבדיקה השנייה, ו-Redirect נוסף מנסה להעביר אותו לדף אחר.
•להשוות Session["score"] >= 90 בלי המרה ל-int – לא עובר הידור.
הקוד המלא
C#
protected void Page_Load(object sender, EventArgs e)
{
    int score = (int)Session["score"];
    bool finished = (bool)Session["finished_all"];
    int signs = (int)Session["sign_mistakes"];

    if (score >= 90 && finished && signs == 0)
        Response.Redirect("readyForTest.aspx");
    else if (score >= 70 || (finished && signs <= 2))
        Response.Redirect("morePractice.aspx");
    else
        Response.Redirect("lessons.aspx");
}

שאלה 6 · JavaScript – חילוץ שם ספק הדואר מכתובת

כתובת דואר אלקטרוני בנויה משני חלקים המופרדים בתו @:
א. שם המשתמש (user) – לפני התו @. שם המשתמש עשוי להכיל את התו נקודה (.).
ב. שם השרת (host) – אחרי התו @. שם השרת מתחיל בשם ספק הדואר (provider), ואחריו סיומת אחת או יותר. החלקים של שם השרת מחוברים באמצעות התו נקודה (.).

לדוגמה:

dana@walla.co.il
yossi.cohen@gmail.com

כתבו בשפת JavaScript פונקציה בשם getProvider המקבלת מחרוזת המייצגת כתובת דואר אלקטרוני תקינה ומחזירה מחרוזת המכילה את שם ספק הדואר (provider). לדוגמה:

•עבור הכתובת dana@walla.co.il תוחזר המחרוזת: walla
•עבור הכתובת yossi.cohen@gmail.com תוחזר המחרוזת: gmail
(20 נקודות)
◂ פתרון מלא לשאלה 6

פתרון

תשובה

function getProvider(email) { var host = email.split("@")[1]; return host.split(".")[0]; }

מה נדרש – פונקציית JavaScript שמחזירה את החלק שבין התו @ לבין הנקודה הראשונה שאחריו.

רעיון – מפרקים בשני שלבים:

1.email.split("@") מחזיר מערך של שני חלקים: שם המשתמש ושם השרת. שם השרת הוא האיבר באינדקס 1.
2.את שם השרת מפרקים לפי "." – שם הספק הוא האיבר הראשון (אינדקס 0).

אסור לפרק את כל הכתובת לפי נקודה, כי גם בשם המשתמש יכולות להיות נקודות.

פתרון

JavaScript
function getProvider(email) {
    var host = email.split("@")[1];     // the part after @, e.g. "walla.co.il"
    var parts = host.split(".");        // ["walla", "co", "il"]
    return parts[0];
}

פתרון חלופי עם indexOf ו־substring – מוצאים את מקום ה־@, ואז את הנקודה הראשונה שאחריו (החיפוש מתחיל ממקום at + 1):

JavaScript
function getProvider(email) {
    var at = email.indexOf("@");            // position of @
    var dot = email.indexOf(".", at + 1);   // first dot AFTER the @
    return email.substring(at + 1, dot);
}

בדיקה – שתי הגרסאות:

•getProvider("dana@walla.co.il") → walla
•getProvider("yossi.cohen@gmail.com") → gmail
•וגם a.b.c@bezeqint.net → bezeqint, noa@012.net.il → 012.

טעות נפוצה: לכתוב email.split(".")[0] או לחפש את הנקודה הראשונה במחרוזת כולה (email.indexOf(".")). עבור yossi.cohen@gmail.com זה נותן yossi, כי הנקודה הראשונה נמצאת בשם המשתמש. לכן חייבים להתחיל מהתו @.

שאלה 7 · מונה לייקים בתחרות צילום – Session או Application

לפניכם דף האינטרנט הבא (צד שרת) מתוך אתר של תחרות צילום:

הדף מציג תמונה וכפתור "אהבתי". מטרת התוכנית היא להציג את מספר הלייקים שהתמונה קיבלה מכל הגולשים באתר יחד, ולהגדיל אותו באחד בכל לחיצה על הכפתור, אולם התוכנית אינה מבצעת את הנדרש.

רשמו והסברו היכן התקלות וכיצד ניתן לתקנן.

HTML
<%@ Page Language="C#" %>
<script runat="server">
   public void Page_Load(object sender, EventArgs e)
   {
       if (!IsPostBack)
       {
           Session["likes"] = 0;
       }
   }
   void Like_Click(object sender, EventArgs e)
   {
       Session["likes"] = Session["likes"].ToString() + 1;
   }
</script>
<html>
<head>
</head>
<body>
   <form id="form1" runat="server">
       <img src="sunset.jpg" alt="שקיעה בכנרת" />
       <input type="button" runat="server" id="btnLike" value="אהבתי" onserverclick="Like_Click" />
       <div runat="server" id="div1">
           מספר הלייקים: <% =Session["likes"] %>
       </div>
   </form>
</body>
</html>

רשמו והסברו היכן התקלות וכיצד ניתן לתקנן.

(20 נקודות)
◂ פתרון מלא לשאלה 7

פתרון

תשובה

(1) Session["likes"].ToString() + 1 משרשר מחרוזות: מוצג 0, 01, 011... – צריך המרה למספר: (int)...+1. (2) המונה נשמר ב-Session, שהוא נפרד לכל גולש – צריך Application, שמשותף לכל הגולשים. באתחול: לאתחל Application["likes"] = 0 רק אם הוא null (ולא בכל כניסה של גולש חדש), ובלחיצה Application["likes"] = (int)Application["likes"] + 1 (עם Lock/UnLock), ולהציג <% =Application["likes"] %>.

מה נדרש – למצוא למה המונה אינו עובד כנדרש, ולתקן.

רעיון – לבדוק שני דברים: (1) מה קורה לערך בכל לחיצה – מה הטיפוס שלו ואיזו פעולה מתבצעת עליו; (2) איפה הערך נשמר – Session הוא נפרד לכל גולש, ואילו Application משותף לכל הגולשים באתר.

פתרון

1.תקלה ראשונה – חיבור מחרוזות במקום חיבור מספרים. Session["likes"].ToString() + 1 הוא מחרוזת ועוד מספר, ולכן ב-C# זה שרשור: "0" + 1 = "01", ואחר כך "011", "0111"... במקום 1, 2, 3. צריך להמיר למספר: (int) או Convert.ToInt32.
2.תקלה שנייה – Session במקום Application. כל גולש מקבל Session משלו, ולכן כל גולש רואה רק את הלחיצות שלו, וגולש חדש מתחיל מ-0. מונה משותף לכל הגולשים נשמר ב-Application.
3.שימו לב לאתחול – אם רק נחליף Session ב-Application ונשאיר if (!IsPostBack) Application["likes"] = 0;, אז כל גולש חדש שנכנס לדף יאפס את המונה של כולם. לכן מאתחלים רק אם המונה עדיין לא קיים (== null), כלומר פעם אחת מאז שהאתר עלה.

התיקון – קטע הסקריפט המתוקן (Lock/UnLock מונעים ששני גולשים ישנו את המונה בדיוק באותו זמן):

C#
public void Page_Load(object sender, EventArgs e)
{
    Application.Lock();
    if (Application["likes"] == null)      // only the first visitor since the site started
    {
        Application["likes"] = 0;
    }
    Application.UnLock();
}
void Like_Click(object sender, EventArgs e)
{
    Application.Lock();
    Application["likes"] = (int)Application["likes"] + 1;
    Application.UnLock();
}

ושורת התצוגה בדף:

HTML
<div runat="server" id="div1">
    מספר הלייקים: <% =Application["likes"] %>
</div>

(אפשר גם לאתחל את Application["likes"] פעם אחת ב-Application_Start שבקובץ Global.asax.)

בדיקה – הדמינו שני גולשים (שני Session נפרדים ו-Application משותף), כשבכל בקשה נוצר אובייקט דף חדש:

בקשההדף המקוריהדף המתוקן
גולש א' נכנס00
גולש א' לוחץ011
גולש א' לוחץ0112
גולש ב' נכנס02
גולש ב' לוחץ013
גולש א' לוחץ01114

טעות נפוצה: לתקן רק את ההמרה למספר – אז כל גולש סופר לעצמו; או לעבור ל-Application אבל להשאיר את האיפוס ב-!IsPostBack – אז כל כניסה של גולש חדש מאפסת את המונה.

הקוד המלא
C#
public void Page_Load(object sender, EventArgs e)
{
    Application.Lock();
    if (Application["likes"] == null)      // only the first visitor since the site started
    {
        Application["likes"] = 0;
    }
    Application.UnLock();
}
void Like_Click(object sender, EventArgs e)
{
    Application.Lock();
    Application["likes"] = (int)Application["likes"] + 1;
    Application.UnLock();
}

פרק שלישי

ענו על שאלה אחת מן השאלות 9-8.

שאלה 8 · ASP.NET ומסד נתונים – קבלה למרפאה (השלמת קוד)

בעמדת קבלה ממוחשבת במרפאה נדרש המטופל להזין את מספר המטופל שלו ואת שם הרופא שאליו הוא הגיע:

HTML
<input type="text" name="patientId" id="patientId"/>

<input type="text" name="doctor" id="doctor"/>

במסד הנתונים של המרפאה קיימת טבלה בשם appointments ובה שתי עמודות:

patientId (טקסט), מספר המטופל

doctor (טקסט), שם הרופא שאצלו נקבע התור

לכל מטופל עשויים להיות כמה תורים להיום, אצל רופאים שונים, ולכן מספר מטופל יכול להופיע בטבלה ביותר משורה אחת.

דוגמה לתוכן הטבלה:

patientIddoctor
311Katz
311Peretz
522Katz

לפניכם קטע קוד חלקי של פעולה בשם CheckAppointment שמטרתה לבדוק את הנתונים שהוזנו על ידי המטופל. בהתאם לתוצאות הבדיקה, יש להפנות את המטופל לעמוד מתאים לפי הכללים הבאים:

•אם למספר המטופל אין אף תור בטבלה, מעבר לעמוד noAppointments.aspx
•אם למטופל יש תורים, אך אף אחד מהם אינו אצל הרופא שהוזן, מעבר לעמוד wrongDoctor.aspx
•אם למטופל יש תור אצל הרופא שהוזן, מעבר לעמוד checkIn.aspx

עליכם להשלים את הקוד החסר.

C#
protected void CheckAppointment(string connectionString)
{
   SqlConnection connection = new SqlConnection(connectionString);
   SqlCommand command = new SqlCommand();
   DataSet ds = new DataSet();
   command.CommandText = string.Format(
      "SELECT * FROM appointments WHERE patientId = '{0}'",
      Request.Form["patientId"]);
   command.Connection = connection;
   connection.Open();
   SqlDataAdapter adapter = new SqlDataAdapter(command);
   adapter.Fill(ds);
   connection.Close();

   // *************** השלימו כאן ***************
}

עליכם להשלים את הקוד החסר.

(20 נקודות)
◂ פתרון מלא לשאלה 8

פתרון

תשובה

לולאה על ds.Tables[0].Rows שמחפשת שורה עם doctor == Request.Form["doctor"] (found = true); אם Rows.Count == 0 ⇐ noAppointments.aspx, אחרת אם !found ⇐ wrongDoctor.aspx, אחרת ⇐ checkIn.aspx.

מה נדרש – להשלים את סוף הפעולה: לפי השורות שהשאילתה החזירה ל-ds, להפנות לאחד משלושת העמודים.

רעיון

•השאילתה מחזירה את כל התורים של המטופל – אפס, אחת או כמה שורות ב-ds.Tables[0].
•אם Rows.Count הוא 0 ⇐ noAppointments.aspx.
•אחרת לא מספיק לבדוק את השורה הראשונה בלבד, כי התור אצל הרופא המבוקש יכול להיות בכל שורה. עוברים בלולאה על כל השורות ומחפשים שורה שבה Rows[i]["doctor"].ToString() שווה ל-Request.Form["doctor"]; אם נמצאה – מסמנים found = true.
•לפי found מפנים ל-checkIn.aspx או ל-wrongDoctor.aspx.

פתרון – הקוד להשלמה (במקום ההערה "השלימו כאן"):

C#
    bool found = false;
    for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
    {
        if (ds.Tables[0].Rows[i]["doctor"].ToString() == Request.Form["doctor"])
            found = true;
    }

    if (ds.Tables[0].Rows.Count == 0)
        Response.Redirect("noAppointments.aspx");
    else if (!found)
        Response.Redirect("wrongDoctor.aspx");
    else
        Response.Redirect("checkIn.aspx");

(כשאין שורות הלולאה לא מתבצעת כלל, found נשאר false, והבדיקה הראשונה מפנה ל-noAppointments.aspx.)

בדיקה – ההשלמה עם DataSet שמולא בתוצאת השאילתה, על טבלת הדוגמה בתוספת תור שלישי למטופל 311 אצל Levin:

patientIddoctor שהוזןשורות שהוחזרודף
311KatzKatz, Peretz, LevincheckIn.aspx (השורה הראשונה)
311PeretzKatz, Peretz, LevincheckIn.aspx (שורה אמצעית)
311LevinKatz, Peretz, LevincheckIn.aspx (השורה האחרונה)
311AmsalemKatz, Peretz, LevinwrongDoctor.aspx
522KatzKatzcheckIn.aspx
522PeretzKatzwrongDoctor.aspx
700KatzאיןnoAppointments.aspx

השאילתה: עבור '311' מוחזרות שתי שורות (Katz, Peretz), עבור '522' – שורה אחת, ועבור '700' – אין שורות.

טעות נפוצה: לבדוק רק את Rows[0] – אז מטופל 311 שהגיע ל-Peretz יופנה בטעות ל-wrongDoctor.aspx. טעות נוספת: לבצע Response.Redirect("wrongDoctor.aspx") בתוך הלולאה ברגע ששורה אחת לא מתאימה, לפני שבדקו את כל השורות.

הקוד המלא
C#
    bool found = false;
    for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
    {
        if (ds.Tables[0].Rows[i]["doctor"].ToString() == Request.Form["doctor"])
            found = true;
    }

    if (ds.Tables[0].Rows.Count == 0)
        Response.Redirect("noAppointments.aspx");
    else if (!found)
        Response.Redirect("wrongDoctor.aspx");
    else
        Response.Redirect("checkIn.aspx");

שאלה 9 · השלמת פעולה המציגה טבלת סרטים

במסד הנתונים של מועדון קולנוע יש טבלה בשם Movies ובה ארבע עמודות: title (שם הסרט, טקסט), genre (סוגה, טקסט), minutes (אורך הסרט בדקות, מספר שלם), year (שנת יציאה, מספר שלם).

דוגמה לטבלה:

yearminutesgenretitle
2014169מדע בדיונימסע בין כוכבים
2003100אנימציההדג הקטן
1978120קומדיההלהקה
2001178פנטזיהשר הטבעות
201995דרמההבית בהר

מנהל המועדון מעוניין להפיק טבלה ובה כל פרטי הסרטים שאורכם יותר מ-120 דקות.

הפונקציה showLongMovies מקבלת מחרוזת התחברות למסד הנתונים – connectionString – ומציגה בטבלת HTML את כל פרטי הסרטים המתאימים (כפי שהם מופיעים בטבלה Movies).

בפונקציה חסרים חמישה ביטויים המסומנים במספרים בין סוגריים עגולים. כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (5), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.

C#
protected void showLongMovies(string connectionString)
{
    string html = "";
    SqlConnection conn = new SqlConnection(connectionString);
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = "select * from _____(1)_____ where _____(2)_____";
    cmd.Connection = _____(3)_____;
    conn.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    adapter.Fill(ds);
    conn.Close();
    html += "<table border='1'><tr><th>title</th><th>genre</th><th>minutes</th><th>year</th></tr>";
    foreach (DataRow row in _____(4)_____)
    {
        _____(5)_____
    }
    html += "</table>";
    Response.Write(html);
}

כתבו במחברת הבחינה את מספרי הביטויים החסרים (1) – (5), בסדר עולה, וכתבו ליד כל מספר את הביטוי החסר שהוא מייצג.

(20 נקודות)
◂ פתרון מלא לשאלה 9

פתרון

תשובה
•(1) Movies
•(2) minutes > 120
•(3) conn
•(4) ds.Tables[0].Rows
•(5) html += "<tr><td>" + row["title"] + "</td><td>" + row["genre"] + "</td><td>" + row["minutes"] + "</td><td>" + row["year"] + "</td></tr>";

מה נדרש: להשלים חמישה ביטויים בפונקציה ששולפת מהמסד את הסרטים שאורכם יותר מ-120 דקות ובונה מהם טבלת HTML.

רעיון – מעבר על הפונקציה לפי הסדר:

•(1) שם הטבלה שממנה שולפים ⇐ Movies.
•(2) התנאי "אורכו יותר מ-120 דקות". האורך נשמר בעמודה minutes, ו"יותר מ-" הוא אי-שוויון חזק ⇐ minutes > 120.
•(3) הפקודה צריכה לדעת דרך איזה חיבור לפנות למסד. משתנה החיבור בפונקציה נקרא conn (ולא connection) ⇐ conn.
•(4) הלולאה עוברת על השורות של טבלת התוצאה שב-DataSet ⇐ ds.Tables[0].Rows.
•(5) לכל שורה מוסיפים ל-html שורת טבלה <tr> עם ארבעה תאים <td>, לפי סדר הכותרות (title, genre, minutes, year):
C#
html += "<tr><td>" + row["title"] + "</td><td>" + row["genre"] + "</td><td>" + row["minutes"] + "</td><td>" + row["year"] + "</td></tr>";

(אפשר גם לפי מספרי עמודות: row[0] … row[3].)

פתרון – תשובה במחברת:

•(1) Movies
•(2) minutes > 120
•(3) conn
•(4) ds.Tables[0].Rows
•(5) html += "<tr><td>" + row["title"] + "</td><td>" + row["genre"] + "</td><td>" + row["minutes"] + "</td><td>" + row["year"] + "</td></tr>";

הפונקציה המלאה – בלשונית C#.

בדיקה:

•השאילתה select * from Movies where minutes > 120 על טבלת הדוגמה מחזירה 2 סרטים – מסע בין כוכבים (169) ושר הטבעות (178). הלהקה (120 בדיוק) לא הוחזר, כי 120 אינו יותר מ-120.
•הפונקציה המלאה על 5 הסרטים יוצרת טבלת HTML עם שורת כותרות ו-2 שורות נתונים, למשל <tr><td>שר הטבעות</td><td>פנטזיה</td><td>178</td><td>2001</td></tr>.

טעות נפוצה: בסעיף (2) לכתוב minutes >= 120 – אז גם הלהקה יוצג. בסעיף (3) לכתוב connection – אין משתנה כזה בפונקציה. בסעיף (5) לשכוח את <tr>/</tr> – אז כל התאים יופיעו באותה שורה.

הקוד המלא
C#
protected void showLongMovies(string connectionString)
{
    string html = "";
    SqlConnection conn = new SqlConnection(connectionString);
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = "select * from Movies where minutes > 120";
    cmd.Connection = conn;
    conn.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    adapter.Fill(ds);
    conn.Close();
    html += "<table border='1'><tr><th>title</th><th>genre</th><th>minutes</th><th>year</th></tr>";
    foreach (DataRow row in ds.Tables[0].Rows)
    {
        html += "<tr><td>" + row["title"] + "</td><td>" + row["genre"] + "</td><td>" + row["minutes"] + "</td><td>" + row["year"] + "</td></tr>";
    }
    html += "</table>";
    Response.Write(html);
}
טבלת התשובות
שאלהסעיףתשובה
1-

ג. הנתונים נשלחים לשרת כחלק מכתובת ה-URL (אחרי הסימן ?), ובצד השרת קוראים אותם בעזרת Request.QueryString

2-

(2)

3-

טבלה עם מסגרת, 5 שורות ו־2 עמודות: (1, Odd), (2, Odd), (3, 6), (4, 10), (5, Odd); המילה Odd מוצגת בכתב מודגש

4א

b

ב

<td colspan=2 align="center"><a href="trip.html">Trip</a></td>

5-

קוראים את שלושת משתני ה-Session עם המרה (int)/(bool), ואז: if (score >= 90 && finished && signs == 0) Response.Redirect("readyForTest.aspx"); else if (score >= 70 || (finished && signs <= 2)) Response.Redirect("morePractice.aspx"); else Response.Redirect("lessons.aspx");

6-

function getProvider(email) { var host = email.split("@")[1]; return host.split(".")[0]; }

7-

(1) Session["likes"].ToString() + 1 משרשר מחרוזות: מוצג 0, 01, 011... – צריך המרה למספר: (int)...+1. (2) המונה נשמר ב-Session, שהוא נפרד לכל גולש – צריך Application, שמשותף לכל הגולשים. באתחול: לאתחל Application["likes"] = 0 רק אם הוא null (ולא בכל כניסה של גולש חדש), ובלחיצה Application["likes"] = (int)Application["likes"] + 1 (עם Lock/UnLock), ולהציג <% =Application["likes"] %>.

8-

לולאה על ds.Tables[0].Rows שמחפשת שורה עם doctor == Request.Form["doctor"] (found = true); אם Rows.Count == 0 ⇐ noAppointments.aspx, אחרת אם !found ⇐ wrongDoctor.aspx, אחרת ⇐ checkIn.aspx.

9-
•(1) Movies
•(2) minutes > 120
•(3) conn
•(4) ds.Tables[0].Rows
•(5) html += "<tr><td>" + row["title"] + "</td><td>" + row["genre"] + "</td><td>" + row["minutes"] + "</td><td>" + row["year"] + "</td></tr>";

כל האתר במבט אחד

קיצורי דרך לכל הכלים, הבגרויות והקורסים

GuyAI הוא המורה הפרטי המתקדם בישראל, המבוסס על מודלי הבינה המלאכותית החזקים בעולם בשילוב הידע הפדגוגי של GOOL. המערכת מספקת פתרונות לכל המקצועות ולכל הרמות - מיסודי ועד אקדמיה, וממתמטיקה ועד היסטוריה.