LINQ to SQL/ADO.NET EF, מודל השכבות ו Data Transfer Objects

6 בSeptember 2009

תגיות: ,
6 תגובות

בפורום “תכנות .NET” בתפוז, עלה לאחרונה נושא השילוב בין ADO.NET Entity Framework ומודל השכבות. ובאמת, אם נסתכל על הדוגמאות הנפוצות באינטרנט, מאד קל לראות עמודים ששמכילים ב code behind שלהם שאילתות LINQ שמקומם ב DAL ופעולות לוגיות שונות שמקומם ב BLL. בסופו של דבר, יש משהו מאד מפתה בנוחות שעלול לגרום ליצירת קוד מבולגן.

אם תחשבו על אפליקציה הבנוייה עפ”י מודל השכבות, מאד ברור לנו מה אמור להיות ב DAL ומה אמור להיות ב BLL ומה אמור להיות בGUI, למשל. אבל, השאלה היא, כשהאפליקציה מתבססת על LINQ to SQL/ ADO.NET EF כשכבת ה DAL, היא איך אנחנו מעבירים את המידע בין השכבות. שיטה שמאד מאד מקובלת בכל מיני דוגמאות באינטרנט, היא להשתמש באובייקטים שמופו מה DB ע”י ה ORM כBO’s ולמעשה, אותם להעביר בין השכבות.
שיטה זו בעייתית. דבר ראשון, האובייקטים שנוצרים באמצעות המיפוי של הORM-ים האלה עמוסים בהרבה מאד… זבל (לפחות, עד הגרסא הבאה של EF שתתמול בעבודה עם POCO). כלומר, דברים מאד מאד חשובים, אבל לא דברים שחשובים לאובייקטים ב BO שלנו. הם גם יורשים וממשים כל מיני דברים שיכולים “לבלגן” את האפליקציה שלנו (כל מתודות ה OnInsert וכו’).
הבעייתיות השנייה, היא ששימוש באובייקטים האלה כ-ORM כופה עלינו תלות משמעותית מדי ב ORM. אנחנו מסתמכים על האובייקטים שהוא מייצר בצורה שתגרום לכך שאם מתישהו נצטרך להחליף, התיקונים שלנו יהיו הרבה מעבר לתיקונים בDAL, אלא נצטרך לתקן גם ב BLL וגם ב GUI. או לחלופין, במידה שנחליף, נצטרך לעשות שמיניות באוויר כדי לשמור על איזושהי תאימות.
וחוץ מזה, נוצרת איזושהי פגיעה בהפרדה בין השכבות כשלGUI יש רפרנס ישיר ל DAL. המצב האידיאלי מבחינתי, הוא שלGUI יש רפרנס ל BLL שמחזיק בתורו רפרנס ל DAL ושלכולם יש רפרנס לBO’s.

אז, מה עושים? מה צורת העבודה המומלצת?
התשובה לזה קצת מורכבת, כי יש הרבה שיטות. השיטה שלי היא להשתמש ב DTO’s שגם משמשים כסוג של BO.
Data Transfer Objects הם אובייקטים שהייעוד העיקרי שלהם הוא העברת מידע בין השכבות. הם כמעט תמיד לא יכילו שום פונקציונאליות, למעט הצהרה על properties עם getters ו setters.

דוגמא מעשית:

כאשר משכבת ה GUI אני רוצה להוסיף, נניח, מאמר. לחיצה על כפתור ההוספה תגרור יצירת מופע של BO.ArticleDTO (אובייקט, שתפקידו להחזיק את המידע). המופע הזה יועבר כפרמטר למתודה שנמצאת, נניח, ב BLL.Articles.Insert. בBLL, למשל, אני אשלח אימייל על המאמר החדש שהתווסף לרשימת התפוצה, ואקרא ל DAL.Articles.Insert שאעביר אליו גם כן את אותו המופע של BO.ArticleDTO. בשכבת ה DAL, אני אצור מופע של DAL.Article (שהוא האובייקט שיצר ה ORM ושאיתו הוא עובד, כי הוא הרי לא מכיר את הDTO שלי) ואבצע את ההוספה.
במקרה של שליפה, מה ששכבת ה DAL תחזיר הלאה, זה גם כן מופע של BO.ArticleDTO.

חוץ מהעובדה ששימוש בשיטה הזאת פותר, למעשה, את הבעיות שציינתי כסיבה לא להשתמש באובייקטים שיוצר ה ORM, הוא גם נותן כמה יתרונות נוספים: דבר ראשון, ה DTO לא צריך לחפוך בשום צורה שהיא למודל ב DB, אין שום חובה שכל הנתונים שבו יהיו מקושרים ל DB, ולמעשה יש חופש מוחלט לגבי המבנה שלו.
מעבר לכך, במידה שקורה איזשהו שינוי בשכבת ה DAL, אין שום צורך לגעת באף שכבה עליונה יותר – כולם מכירים את ה DTO ועובדים מולו, והוא מאפשר ליצור איזושהי חציצה בין השכבות.

כמובן, שלשיטה הזאת יש חסרון – צריך, בפעם הראשונה, לעשות עבודה כפולה. ליצור את ה DTO. אבל, כבר לא פעם השיטה הזאת חסכה לי לא מעט עבודה בהמשך ואני ממליץ עליה בחום כשיטת עבודה כשעובדים עם ADO.NET EF או עם LINQ to SQL, לפחות עד ש EF יציג תמיכה סבירה בעבודה עם POCO.

בהצלחה.

הוסף תגובה
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

6 תגובות

  1. Gil Fink6 בSeptember 2009 ב 22:49

    הי שחר,
    פוסט טוב והמלצה נכונה. אגב, נכון לכיום צוות ADO.NET עובד על feature חדש שנקרא self-tracking entities שגם מנסה לפתור את בעיית ה-N-Tier. כנראה שהוא לא יכנס ל-EF4 אלא רק בגירסת ה-SP1 אבל עדיין שווה להעפיף בו מבט: http://blogs.msdn.com/adonet/pages/feature-ctp-walkthrough-self-tracking-entities-for-the-entity-framework.aspx.

    Reply
  2. שחר גבירץ7 בSeptember 2009 ב 9:06

    גיל, זה נראה נחמד אבל האובייקט הוא עדיין לא אובייקט שהייתי מעביר בין השכבות – בעיקר בגלל התלות שהוא עדיין יוצר בORM שבחרת (EF במקרה הזה).

    Reply
  3. לולי27 בSeptember 2009 ב 8:31

    נשמע מאד מענייםן. האם יש לך דוגמת קוד קטנה לדוגמא שממחימשה?
    תודה

    Reply
  4. מתכNET22 בJanuary 2010 ב 23:08

    ובעקרון כל שכבה רצוי שתכיר רק את השכבה מתחתיה.

    לעולם לא את השכבה מעליה

    אני מסכים עם שחר גבירץ ,לפי דעתי self-tracking entities מהיכרותי המעטה זה דבר נורא,זה נוגד את כל הכללים שעד היום השתמשנו בהם.

    Reply
  5. אבישי6 בMarch 2010 ב 22:55

    מה לגבי ADO.NET Data Service ?
    האם זה לא חוסך את בניית ה DTO ? ה server יעביר ב wire רק
    את השדות של האוביקט עצמו ולא data מיותר…
    ?

    Reply