בין שלל החידושים של גרסת VS 11 Beta ששוחררה לאחרונה, הוצגה יכולת חשובה בעבודה עם נתונים: Strongly Typed Data Controls. במאמר זה נזכיר את "תבניות" הנתונים הללו ואת השיטה הנוכחית לביצוע Data Binding, נחקור את האתגרים שאנו נתקלים בהם כיום ונראה כיצד באמצעות VS 11 Beta ניתן להתמודד עם האתגרים האלה, לשפר גם את הפרודוקטיביות ואף להינות מביצועים טובים יותר.
הורידו והתנסו ללא תשלום בגרסה החדשה של Visual Studio 11 Beta
Data Binding – המצב כיום
כל מפתח WebForms מכיר את ה"תבניות" (Templates) שקיימות מהגרסה הראשונה של ASP.NET. תבניות אלו מאפשרות לנו לשנות את ה-markup המיוצר על ידי קונטרולים התומכים בהן, כגון GridView או Repeater, בהתאם לנתונים שאליהם הקונטרול מקושר (bound). על מנת לבצע את הקישור משתמשים במתודות עזר ()Eval עבור קישור חד-כיווני, ו- ()Bind עבור קישור דו-כיווני. שתי המתודות מקבלות בתור פרמטר מחרוזת שחייבת להיות זהה לשם המאפיין אליו נרצה לבצע את הקישור:
<asp:GridView runat="server" ID="lateBoundGrid" AutoGenerateColumns="false" >
<Columns>
<asp:TemplateField HeaderText="Id">
<ItemTemplate>
<span><%# Eval("Id") %></span>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Title">
<ItemTemplate>
<asp:TextBox runat="server" Text='<%# Bind("Title") %>' Id="title"/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Author">
<ItemTemplate>
<asp:TextBox runat="server" Text='<%# Bind("Author") %>' Id="author"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
בזמן ריצה, יתבצע שימוש ב-Reflection כדי "להזריק" את ערכי המאפיינים מתוך האובייקטים המקושרים לתוך ה-markup. להלן הקוד (בצורה מפושטת), שיווצר מאחורי הקלעים כדי לבצע קישור של שדה Title ל- TextBox בשם title:
if ((this.Page.GetDataItem() != null))
{
title.Text = Convert.ToString(this.Eval("Title"), CultureInfo.CurrentCulture);
}
נזכור כי הקישור מוגדר בעזרת מחרוזות המיצגות את שמות המאפיינים. כלומר, אנו משתמשים ב-Late Bound Expressions, מה שמספק לנו מספר אתגרים:
- חוסר בטחון בזמן קידוד – המחרוזות המוקלדות אינן נבדקות לא בזמן פיתוח ולא בזמן קומפילציה, כך שבמידה וטעינו, את השגיאה נקבל רק בזמן ריצה.
- מחסור ב-Intellisense – חייבים להקליד את שמות המאפיינים במדויק באופן ידני – אין עזרה של Visual Studio.
- רגישות לשינויים – שינוי בשם מאפיין לא יגרום לשום התראה בזמן פיתוח וקומפילציה – את השגיאה נגלה רק בזמן ריצה (בשאיפה בבדיקות).
- השפעה על ביצועים – בשל שימוש ב-Reflection. למרות שמנגנון ה-Binding עושה שימוש ב-Caching של מאפיינים, וחיפוש המאפיין מתבצע רק פעם אחת על כל פעולת ה-DataBind, עדיין קיימת השפעה על ביצועיי הדפים (עוד על כך בהמשך המאמר).
הפתרון – Strongly Typed Data Controls
גרסת ASP.NET 4.5 המותקנת עם VS 11 Beta, מציגה את Strongly Typed Data Controls בתבניות, המאפשר הגדרת טיפוס האובייקט המקושר באמצעות מאפיין חדש: ItemType.
מאפיין זה קיים בכל קונטרול שיורש מתוך System.Web.UI.WebControls.DataBoundControl (כגון: GridView, FormView, CheckBoxList, DropDownList, ListBox ואחרים), וכן ב-Repeater שלא יורש מהנ"ל. כאשר מוגדר ה-ItemType, אנו יכולים להשתמש בביטוי Item ולקבל השלמה אוטומאטית (Intellisense) ב- VS 11 Beta:
המשמעות היא שאין יותר טעויות בהקלדת מחרוזות! ואין יותר צורך בלזכור שמות של מאפיינים. כעת ניתן להינות מביטחון מירבי בזמן הקידוד ומפרודוקטיביות גבוהה יותר.
מכיוון שכעת אנחנו עובדים Strongly Typed כל הקוד נבדק הן בזמן הקידוד והן בזמן הקומפילציה, וכך נקבל התראה אם משהו לא תקין מיד עם הופעת התקלה:

הביטוי Item מאפשר לנו קישור חד-כיווני. על מנת לבצע קישור דו-כיווני, עלינו להשתמש בביטוי BindItem, שגם הוא מספק לנו Intellisense מלא. להלן הדוגמא שהצגתי קודם אך הפעם עם שימוש ב-Strongly Typed Data Controls:
<asp:GridView runat="server" ID="stronglyBoundGrid"
AutoGenerateColumns="false" ItemType="BookStore.Book" >
<Columns>
<asp:TemplateField HeaderText="Id">
<ItemTemplate>
<span><%# Item.Id %></span>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Title">
<ItemTemplate>
<asp:TextBox runat="server" Text='<%# BindItem.Title %>' Id="title"/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Author">
<ItemTemplate>
<asp:TextBox runat="server" Text='<%# BindItem.Author %>' Id="author"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
ומה לגבי הביצועים? בשימוש ב-Item, כלומר בקישור חד-כיווני, שהוא השימוש הנפוץ ביותר, אין שימוש ב-Reflection. מנוע ה-ASP.NET יתרגם את הקישור לקוד הדומה לקוד הבא:
Item = ((BookStore.Book)(Container.DataItem));
target.SetDataBoundString(0, Convert.ToString(Item.Id, CultureInfo.CurrentCulture));
מהניסיונות שביצעתי*, הפעלת ()DataBind תוך שימוש ב-Item עורכת כמחצית מהזמן בהשוואה לשימוש
ב- ()Eval. לצערי, כאשר משתמשים בקישור דו-כיווני, המנגנון נשאר זהה לשימוש ב-()Bind, כך שלא מרוויחים בביצועים, אך עדיין מקבלים השלמה אוטומאטית.
* הנתונים הנ"ל הן תוצאות של ניסויים שלי בלבד ואינם נתונים רשמיים. בניסויים אחרים ובתסריטים אחרים התוצאה עשויה להיות שונה.
הורידו והתנסו ללא תשלום בגרסה החדשה של Visual Studio 11 Beta
הפוסט נכתב ע"י ולאד אזארכין ,מהנדס תמיכת Premier למפתחים (PFE), וכן מוביל טכנולוגי בתחום פיתוח Web ב-EMEA. בין יתר ההתמחויות, ולאד הוא מומחה בתחום ביצועים, ניטור, health, ו-troubleshooting של מערכות Web. בעבר ולאד היה יועץ פיתוח תוכנה, שימש בתפקידי ניהול טכנולוגי בכירים, הציג בכנסים וניהל קבוצת משתמשים.