יש הרבה דרכים להציג מידע מבסיס נתונים, ניתן לשלוף את המידע ישר ולהדפיס אותו או לשלוף אותו לרשימה ואז להדפיס את הרשימה.
כל אפליקצייה בוחרת ליישם את השיטות שלה לשליפת ושליחת מידע לבסיס נתונים.
ניקח לדוגמא את טבלת המשתמשים מהמדריך הקודם.
נניח ואנו רוצים למפות את כל המידע לתוך פקד (ASP.NET Repeater), נוכל לרשום דבר כזה:
כמו במדריך קודם, אנחנו שולפים את הנתונים ל DataSet אבל הפעם אנחנו לא פולטים את ה DataSet שורה שורה אלא שולחים אותו ל Repeater.
בקוד HTML אנחנו רושמים:
שימו לב שבערת הפונקציה Eval אנחנו יכולים לגשת לעמודות ב DataSet.
ה ASP.NET Repeater יודע אוטומית מה לעשות כאשר הוא מקבל DataSet.
אובייקט של משתמש
ניצור תיקייה חדשה בשם App_Code (לחיצה ימינית על הפרוייקט -> Add -> Add ASP.NET Folder -> App_Code)
בתוך App_Code ניצור תיקייה בשם Entities וניצור שם מחלקה חדשה בשם User.cs
נירצה לעצב את המחלקה User כך שהיא תכיל את כל התכונות שיש ל User בבסיס הנתונים (ID, Username, Password):
כעת, בעמוד שלנו (Default.aspx) נוכל ליצור מופעים של המחלקה User ולמלא אותם במשתמשים:
נוסיף את המופעים לרשימה:
כעת נראה שנוכל לשלוח ל Repeater את רשימת המשתמשים כמו שיכולנו לשלוח את ה DataSet:
DataSet מול אובייקטים
אז מהם למעשה ההבדלים בין DataSet לבין מה שעשינו פה?
DataSet היא מחלקה כללית ללא אובייקט (מטרה) מסויים.
DataSet היא מאד גמישה, הרשומות נשמרות בתוך טבלה שהעמודות שלה יכולות להשתנות בהתאם לאיך שהן מסודרות בבסיס הנתונים.
DataSet נקרא לפעמים Loosly typed object, אין ל DataSet מטרה ספציפית.
ההיתרון של DataSet הוא שקל מאד למלא אותו, קל מאד לקבל אותו מבסיס נתונים והוא גם מציג שיטות לעידכון מידע בבסיס נתונים (כמו שראינו במדריך הקודם).
החיסרונות של DataSet:
- מאחר ואין לו אובייקט, קשה מאד לנהל אותו בצורה יחודית, כל DataSet ינוהל בצורה כללית בלי יחס למידע שאותו הוא מייצג.
- מאחר ואין לו אובייקט והוא גמיש מאד, כל שגיאה (למשל גישה לשדה שלא קיים) תגרור שגיאת זמן ריצה ולא שגיאת קומפילציה, מה שהופך את הטיפול בשגיאות לקשה מאד.
- קשה יותר להפריד בין שכבת המשתמש לשכבת הלוגיקה עם DataSets (פירוט במדריך הבא).
לעומת זאת, במקום להשתמש ב DataSets נוכל להשתמש באובייקטים שהם מופעים למלקחות שאנחנו מגדירים מראש (כמו המחלקה שהגדרנו למשתמשים).
כמו ש DataSet שומר רשימה של רשומות מבסיס הנתונים, נוכל לשמור רשימה של האובייקטים שהגדרנו.
אובייקטים כאלה נקראים Strict typed objects, הם מגדירים מטרה מאד ספציפית ומשמשים לשימור של אובייקטים מוגדרים מראש.
בנוסף לכך, קל מאד לבצע מניפולציות על אובייקטים כאלו מה שהופך את ניהול בסיס הנתונים להרבה יותר גמיש.
שליפה מבסיס הנתונים
ראינו כבר איך ניתן לשלוף אל DataSet מבסיס הנתונים.
כעת נראה כיצד ניתן לשלוף אל רשימה של אובייקטים מבסיס הנתונים.
נניח ואנחנו רוצים את כל המשתמשים מבסיס הנתונים אל רשימה של המחלקה User שנקראת users.
נפתח חיבור לבסיס נתונים ואז ניצור DataReader:
כעת נקרא את כל השורות בטבלה של המשתמשים ועבור כל שורה ניצור אובייקט של משתמש שנמלא אותו בערכים של השדות ונכניס אותו לרשימה של המשתמשים:
(שימו לב ש DataReader הוא רשימה של משתנים מסוג object ולכן יש צורך בהמרה לסוג הרצוי).
וכמובן שבסוף נסגור את החיבור לבסיס הנתונים ע"י connection.Close().
DataSet מול DataReader
אתם כנראה שואלים את עצמכם למה לא שלפתי קודם כל את המידע אל אובייקט של DataSet ורק אז מ DataSet למלא את הרשימה של האובייקטים של המשתמשים.
בשביל זה כדאי שתיקראו את הדוקומנטציה באתר של מיקרוסופט על שימוש ב DataAdapter כדי למלא DataSet:
http://msdn.microsoft.com/en-us/libr…vs.110%29.aspx
כלומר, מאחורי הקלעים DataAdapter משתמש ב DataReader כדי למלא את ה DataSet הנתון.
DataAdapter היא מחלקה ממש מורכבת ו DataAdapter.Fill היא פונקציה מאד מורכבת אך כדי להעביר את הנקודה, ננסה להמחיש את אופן הפעולה שלה:
(ל DataAdapter.Fill יש גירסה שמקבלת DataTable ולא DataSet, מאחר ו DataSet יכול להכיל מספר רב של טבלאות חשבתי שיהיה יותר פשוט להמחיש את אופן הפעולה של DataAdapter.Fill על טבלה יחידה).
פה אני מנסה להמחיש לכם את אופן הפעולה של DataAdapter.Fill. כמובן שלא מדובר בקוד האמיתי, הקוד האמיתי הרבה יותר מורכב אבל אני רק רוצה להמחיש לכם את האופן שבו DataAdapter משתמש כבר ב DataReader כדי למלא את ה DataSet.
אז במה עדיף להשתמש, ב DataReader או ב DataSet?
השאלה הזאת תלויה מאד במטרה של התוכנית.
אם מטרת התוכנית היא לשלוף את הנתונים מבסיס הנתונים בדיוק כפי שהם מוצגים ואז להציג אותם למשתמש או לערוך אותם ישירות אז לדעתי מומלץ להשתמש ב DataSet.
אם המטרה שלנו היא למלא אובייקטים שיצרנו מראש במידע מבסיס נתונים (כמו שעשינו עם הדוגמא של הרשימה של המשתמשים) אז מומלץ להשתמש ב DataReader.
הסיבה לכך היא: אם נשתמש ב DataSet למטרה זו, בכל זאת נצתרך אחרי זה לעבור שוב בלולאה על ה DataSet כדי לשלוף את המידע שלו אל האובייקטים.
אך אנחנו יודעים כבר שמאחורי הקלעים, אופן שליפת הנתונים אל DataSet מתבצע ע"י DataReader, לכן שימוש ב DataSet למטרה זו יצור מצב של קוד כפול:
1. שליפת נתונים מבסיס נתונים אל DataSet
2. שליפת נתונים מ DataSet אל רשימה של אובייקטים
במקום:
1. שליפת נתונים מבסיס הנתונים אל רשימה של אובייקטים
2. זהו.
קישורים בין טבלאות
כמו שראינו במדריך הראשון בסידרה, ניתן ליצור קשרים בין טבלאות שונות בבסיס התונים, קשר יחיד לרבים, קשר רבים לרבים…
נראה כעת כיצד ראוי לטפל בקשרים אלו בעת מיפוי מידע מבסיס הנתונים אל אובייקטים.
ניזכר במערכת הפשוטה של המשתמשים והחיות:
כל משתמש יכול להיות בעלים למספר רב של חיות.
כעת נחשוב על איך לשמור כל חיה כאובייקט באתר שלנו.
נוכל ליצור מחלקה כזו:
ממבט ראשון, המודל הזה נראה בסדר לחלוטין עד שאנחנו מתחילים לחשוב מה המטרה בכלל בלמפות טבלאות מבסיס הנתונים אל אובייקטים מוגדרים מראש (נקרא לפעמים גם ORM).
המטרה היא ליצור סביבת עבודה שמאפשרת עבודה עם אובייקטים בלי מגע ישיר עם בסיס הנתונים או עם מושג שקשור לבסיס הנתונים שלנו.
כשאנחנו מסתכלים על השדה public int Owner אנחנו רואים ניסיון לקשר חיה מסויימת אל בעלים שלה אך האופן שבו הקישור הזה נעשה הוא אופן שמסגיר את מנגנון הפעולה של בסיס הנתונים שלנו.
המתכנת שאחראי על טיפול באובייקטים לא צריך לדעת ש Owner מייצג את המפתח הראשי של המשתמש שאליו החיה שייכת, אותו מתכנת פשוט צריך לדעת ש Owner מייצג של המשתמש שאליו החיה שייכת ומאחר שלמתכנת יש גישה למחלקה User, הוא יידע איך לטפל בבעלים של החיה.
לכן במקום הקוד למעלה, יותר נכון לרשום מחלקה כזו:
קוד ליצירת רשימה של משתמשים:
הקוד זהה למה שעשינו מקודם כדי למלא רשימה של משתמשים מבסיס הנתונים, אך הפעם שמנו את הקוד בתוך פונקציה.
קוד ליצירת רשימה של חיות:
פה אנחנו עושים פחות או יותר אותו דבר כמו שאנחנו עושים עם המשתמשים אך הפעם עבור כל חיה, אנחנו משתמשים בפונקציה שיצרנו ליצירת רשימת משתמשים כדי להשיג אובייקט של הבעלים של החיה ואז אנחנו שומרים אותו בתוך האובייקט של החיה.
במדריך הבא נדבר יותר על הפרדת חלקי קוד ומשימות