DCSIMG
October 2006 - Posts - Justin myJustin = new Justin( Expriences.Current );

October 2006 - Posts

Question from Tapuz .Net forum: How can I access controls inside Custom/User Controls if they haven't been created at Page_Load?

שאלה:

יש לי User Control שמכיל DropDownList, ויש לי דף שמכיל אותו.
שמתי לב שה Page_Load של המסך רץ לפני ה Page_Load של הקונטרול,
לכן אני לא יכול לאתחל את הקונטרול ב Page_Load של המסך, אלא רק בשלב מאוחר יותר,
למשל OnPreRender של המסך.
 האם הבנתי נכון, וכך עושים זאת ?

 

תשובה:

הבנת נכון את רצף האירועים. Page מבצע ראשון את כל האירועים שלו, ואז כל מיני סוגי דפים אחרים שקשורים אליו (UserControls ו-MasterPages) מריצים את האירועים שלהם. לא ברור לי מה הכוונה ב"לאתחל את הקונטרול", ה-UserControl נוצר כמו כל פקד אחר שהוספת באמצעות ה-Designer לפני ה-Page_load ולכן כבר מאותחל.

אם למשל אתה מתכוון שאתה לא יכול לגשת לפקדים הפנימיים של UserControl בשלב ה-Page_load של הדף שמכיל אותו, בזה אתה צודק. אתה אמור לקבל על זה שגיאה כי באמת הפקדים בפנים לא מאותחלים. במצבים מאוד פשוטים כאלו, אתה לא תקבל את השגיאה הזו כי איפהשהו מתבצעת אופטימיזציה שקולטת "איזה טיפש התוכניתן, הוא מנסה לגשת לפקד שלא קיים, אני אצור את הפקדים בפנים קודם". האופטימזיציה הזאת מושלמת להדגמות מול קהל ומפסיקה לעבוד ברגע שהדף מספיק מסובך (ע"ע Real world).

כ-כלל אצבע, מה שעושים במצבים כאלו שרוצים לגשת לפקדים של Custom Control או User Control שיתכן שקיימים ויתכן שאינם קיימים הם לדאוג שהפקדים קיימים. בתוך כל Property שאתה חושף לפני שאתה ניגש לפקדים עצמם תצטרך לקרוא למתודה EnsureControlsCreated. זאת מתודה שנמצאת בשימוש נרחב בכל מקום שבו כותבים פקדים באמת כדי לוודא שקיימים פקדים לפני שניגשים אליהם מתוך Properties למיניהן שאינן קשורות בישירין לסדר האירועים הדיפולטי של הדף.
יש דוגמה לזה בשאלה על MasterPages של אוהד מלפני שבוע.

    public string HeadlineText
    {
        get
        {
            EnsureChildControls();
            return lblHeadline.Text;
        }
        set
        {
            EnsureChildControls();
            lblHeadline.Text = value;
        }
    }

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=87714586

Question from Tapuz .Net forum: ViewState or PostBack data isn't being loaded into my ASP.Net server-side controls

שאלה:

הוספתי למסך כפתור, שמבצע פוסטבק,
במתודת ה Click של הכפתור (בשרת), אני ניגש ל SelectedValue של ה DropDownList בקונטרול,
אך תמיד אני מקבל את הערך הראשון ברשימה, ולא באמת את הערך שהגולש בחר.  מדוע ?

 

תשובה:

אי-טעינת מידע ViewState או PostBack נגרמת ב-99% מהמקרים ע"י דבר אחד: ה-ID של הפקד שאמור לקלוט את הערכים אינו קבוע ומשתנה בין PostBackים.
ViewState מספק בסופו של דבר זוגות של "מפתחות-ערכים" כאשר המפתח הוא ה-ID של הפקד והערך הוא הערך הנבחר הקודם שלו.
אחרי PostBack אנו מקבלים מרשימת Request.Form רשימת IDים של פקדים ואת הערכים החדשים שלהם שנבחרו.
הכל מבוסס על זה ש-ID הוא קבוע ולא ישתנה.
אם אחד מהשניים לא נטען, תביט על Request.Form אחרי PostBack ותראה את ה-ID של הפקד שהוא מנסה לשלוח לו ערכים. ה-ID הוא למעשה הרכבה של ההיררכיה המלאה של NamingContainers שבתוכו יושב הפקד. אחד מהאלמנטים שם יהיה עם שם כמו ctl001 וזה הפקד בעל השם הבלתי קבוע. הפתרון הוא לקבוע לכל הפקדים בהירככיה הזו ID קבוע ברמת הקוד.

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=87714586

 

Posted by Justin-Josef Angel [MVP]
תגים:,

Question from Tapuz .Net forum: How to use Microsoft AJAX Library to create a ''Gmail'' like site navigation exprience

שאלה:

מה הדרך הנכונה לבנות אפליקציה אינטרנטית סטייל GMAIL,
שבה כל הניווט והפעולות באתר מתבצע בצורה שקטה ?

 

תשובה:

האפשרות הפשוטה היא להשתמש ב-Fake AJAX שזה דף שעובד רגיל עם PostBackים רק שיש אפקט צד לקוח שגורם להכל להיראות כאילו הוא AJAX.
Fajax -- the fake alternative to ajax


האפשרות היותר מתוחכמת היא לעבוד עם AJAX אמיתי, ספציפית עם Microsoft AJAX Libary (לשעבר: אטלס).
קיים אלמנט בשם UpdatePanel שהקונספט מאחוריו הוא לאפשר מה שנקרא PartialRendering, שזה רנדור מחדש של כל הפנאל בלי לגרום לרנדור מחדש של כל הדף. אם אתה רוצה "ניווט שקט" "סטייל GMAIL" תוכל להכניס את כל התוכן של האתר לתוך UpdatePanel.

רק שזה דורש שינוי קל במחשבה מאשר תכנות ASP.Net רגיל. בתכנות ASP.Net רגיל היית שם את כל תוכן האתר בתוך ContentTemplate של MasterPage ומצפה להצליח להחליף את ה-Content כרצונך. עקב העובדה שה-MasterPage הוא בפועל User Control של Page לא ניתן להחליף באופן דינמי Pageים, אלא רק MasterPages.

ככה שאתר שנבנה ככה יצטרך להיבנות בצורה שבנינו אתרים לפני MasterPages, עם הרבה User Controls. את כל הדפים באפליקציה כזו תצטרך להמיר ל-User Control שזה בעקרון עניין של שינוי שם הקובץ ל-ASCX, את ה-Page Directive בראש הדף ואת המחלקה ממנו יורשת ה-Partial של ה-Code Behind (וגם יש כלים אוטומטים שעושים את זה).

נביט על זה במטאפורה של טלוויזיה למשל. אנו רוצים שרק המשבצת באמצע תתרנדר מחדש כל פעם שאנחנו מחליפים תחנה אבל כל "המסביב" ישאר זהה.
בשביל זה נכניס כל פעם User Control מה-User Controls שפעם היו הדפים שלנו לתוך ה-ContentTemplate של ה-UpdatePanel שלנו.
אם היינו בונים את זה עם PostBackים קל לראות שהיינו יוצרים משהו סטייל PlaceHolder שצמוד אליו כל פעם פקד אחד בדיוק. היינו יוצרים בצד רשימת כפתורים שדואגת שכל פעם שלוחצים אליה הייתה מחליפה את ה-UserControl שצמוד ל-PlaceHolder בזה שהיא תנקה את הפקד הקיים ממנו ותוסיף את הפקד המתאים (לפי איזה כפתור נלחץ).

עכשיו השאלה היא - אנחנו לא רוצים PostBackים איך עושים את זה בAjax?
דבר ראשון זה להפעיל PartialRendering ברמת ה-ScriptManager בזה שנקבע לו EnablePartialRendering=true.
מה שנקבל מזה זה שכל אירוע שבעבר היה דורש PostBack לשרת בתוך ה-UpdatePanel כעת גורם ל-Partial Rendering של כל התוכן בתוך ה-Update Panel.
נרצה להגביל את האירועים שיוצרים Partial Rendering וריצה לשרת ונקבע ל-UpdatePanel שלנו Mode="Conditional" שזה אומר שצריך לפרט איזה טריגר גורם לרנדור מחדש לקרות. טריגר לרנדור מחדש יכול להיות שינוי של ערך של פקד צד-משתמש (למשל, שינוי הערך של תיבת טקסט) או אירוע צד-משתמש (למשל, לחיצה על כפתור).

הטריק האמיתי כאן זה לגרום לכפתורים מחוץ ל-UpdatePanel לגרום לרנדור מחדש של ה-UpdatePanel.
נוכל להוסיף ל-UpdatePanel שלנו טריגר מסוג ControlEventTrigger שמקשיב לכל הכפתורים שלנו. למעשה מה שיקרה זה שבצד לקוח הוא יקשיב ללחיצה על הכפתורים האלו ויגיד "אם הם נלחצו, תרנדר את ה-UpdatePanel מחדש".
כמו כן, נרצה להעביר איכשהו דרך ה-UpdatePanel את המידע של איזה כפתור נלחץ אחרון ובשביל זה נשמור משתנה HtmlHiddenInput שיהיה נגיש לשינוי צד-לקוח ויקלט בצד-שרת.

בגדול, יש כמה אפשרויות לגרום לכפתור לעדכן UpdatePanel:

1. להוסיף ControlEventTrigger על אירוע הלחיצה של הכפתור בתוך הטריגרים של ה-UpdatePanel. אפשר לרשום את זה כקוד Designer Hard-coded בצורה הבאה:

            <Triggers>
                <atlas:ControlEventTrigger ControlID="Panel3bButton" EventName="Click" />
            </Triggers>

עדיף כמובן לבצע את זה תכנותית כדי שהניווט באתר יהיה יותר גמיש והקישור בין הכפתור לבין הרפרוש של ה-UpdatePanel.

        protected void Page_Load(object sender, EventArgs e)
        {
            Button btn = new Button();
            btn.ID = "btnUpper";
            btn.Click += new EventHandler(btn_Click);
            btn.Text = "To upper";
            UpdatePanel2.Controls.Add(btn);
            Microsoft.Web.UI.ControlEventTrigger trig = new Microsoft.Web.UI.ControlEventTrigger();
            trig.ControlID = "btnUpper";
            trig.EventName = "Click";
            trig.OnEvent(btn, new EventArgs());
            UpdatePanel1.Triggers.Add(trig);
        }
        // copied from:
http://aspspider.org/sevob/asp/atlas/uppnl1.aspx

2. ניתן לרשום כל כפתור וכפתור למתודת ScriptManager.RegisterAsyncPostBackControl ברמת הדף.

myScriptManager.RegisterAsyncPostBackControl(Button1);

3. אם אתה עובד עם פקד כמו TreeView שיש לו אירועים צד-לקוח ללחיצה על Nodeים תרצה לתפוס את האירועים צד-לקוח האלו, ולגרום שם לרפרוש של ה-UpdatePanel (ולמנוע את המשך ביצוע הפניית ה-HTTP בגלל שהלקוח לחץ על הכפתור). בשביל זה תוכל ליצור את פקד ה-UpdatePanel בצד לקוח ולקרוא למתודת Update שלו.

 

עכשיו השאלות המעניינות לסגנון:
האם חייבים לעבוד עם כפתורים? אפשר גם לעבוד עם HyperLinkים וליצור  ControlEventTrigger לאירוע Click שלו.
האם אפשר לממשק את זה ישירות ל-TreeView? בגדול, לא. TreeView מרנדר אוסף של TreeNodes ואלו אינם מכילים פקדים צד-שרת משום סוג, הם מרנדרים HTML ישירות. אם אין לך ניסיון בלשבור בכוח את הפריימוורק (להוציא שתי מחלקות מאוד כבדות החוצה, ולשנות מתודה פנימית מאוד ענפה שהיא המתודה של הרנדור של TreeNode) מומלץ לא לנסות את ההתאמה הזאת. אמור להיות אירוע צד-לקוח בשם TreeView_SelectNode שקורה כל פעם שלוחצים על קישור ב-TreeView. אם תוכל להירשם לאירוע הזה או להשתלט על הרנדור צד-לקוח שלו תוכל לגרום לרנדור מחדש של ה-UpdatePanel ע"י קריאה למתודה צד-לקוח של UpdatePanel בשם Update.

אני ממליץ בחום שתקרא את שני המאמרים הבאים על UpdatePanel ולעבור עליהם בדקדוק ובמיוחד על הדוגמאות.
Server Class Library > UpdatePanel Class
Walkthrough: Using an "Atlas" UpdatePanel

היות והתשובה כאן והמאמרים הנ"ל מתייחסים ל-July CTP צריך להוריד את התיעוד מ-ASP.NET AJAX July CTP Documentation.

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=87754870

Question from .Net Tapuz forum: How to build general Search screens when working with Tiers Architecture and Business Entities?

שאלה:

רציתי לדעת מה הדרך הנכונה יותר מבחינת ארכיטקטורה להריץ שאילתות דינמיות במערכת מבוססת שכבות ( Business logic ו -Data access ) כלומר,
אם יש ממשק משתמש בעל כמה שדות שונים וברצוני להריץ שאילתה אך ורק עם אותם שדות שמולאו מבלי להחזיק את משפט הSQL כתוב בקוד.

 

תשובה:

שאילתות Find הן אכן נושא מסובך. מסכי חיפוש כאלו באמת מאתגרים ובוחנים את הכוח של הארכיטקטורה שלנו.

דבר ראשון בנושא קבלת "החלטות ארכיטקטורה נכונות" צריך לדעת מה הארכיטקטורה הנוכחית הרי נושא כמו שאילתות Find למיניהן לא תקבע לנו את הארכיטקטורה.

מצב ראשון הוא בו הישויות העסקיות שלנו מוטמעות בתוך ה-DAL שלנו.
כלומר, לפי הדוגמה הידועה שלי על ניהול משק פרות, כאן יהיה לנו Cow ויהיה לנו CowDAL, כאשר CowDAL יחשוף מתודות כמו Create, Update, Delete שמקבלות פרה. כמו כן יהיה לנו משהו כמו GetAll שמחזיר מערך של פרות. עכשיו השאלה שלך היא איך נממש את CowDAL.Find שמחזיר מערך של פרות ומקבל פרמטרים. דבר ראשון שאני עושה כאן, זה ליצור מתודה ספציפית למסך חיפוש הספציפי שלנו בתוך CowDAL. משהו כמו FindAllCowsForMainSearchSreenASPX. כן, יש כאן אלמנט שאנחנו נותנים ל-GUI שלנו להכתיב את המבנה של ה-DAL, אבל באמת שהמבנה של ה-GUI מחייב מאתנו התייחסות ספציפית ב-DAL.
במתודה הזו נקבל את *כל* הפרמטרים שנקלטים בטופס (שם הפרה, גיל מינימלי לפרה, גיל מקסימלי לפרה, אחו של הפרה וכיו"ב). כל פרמטר שלא הוזן ישלח כ-Null ולכן גם נצטרך לעבוד עם Nullable Types.

המתודה תבנה דינמית SQL (או יותר טוב, תאציל את הבנייה הדינמית למחלקה שבאמת יודעת לטפל בזה), תבצע את השאילתא הדינמית ותבנה לפי התוצאות את מערך הפרות.
דוגמה למחלקת עזר כזו שבונה שאילתות בניתי אחרי 10 דקות שעבדתי בדוט נט. מה שאנחנו רואים למטה זה קובץ XML שהמטרה שלו היא להראות את המבנה של השאילתא. לכל שאילתא יש StaticParams שאלו פרמטרים שתמיד יהיו בשאילתא, ו-OptionalLines שיהיו קיימים אם ורק אם הפרמטרים בהם הם מותנים סופקו למחולל השאילתות. הרעיון הוא לשלוח מערך של נתונים למחולל השאילתות והוא כבר ידע לבנות את זה לבד. ההיגיון מאחורי זה גם מאוד קל ופשוט להבנה: ניצור מתודה למטרות חיפוש שהיא Strong typed מבחינת הפרמטרים ב-DAL, שתעביר את מספר השאילתא (או איזה מזהה כזה) ואת הפרמטרים הקיימים למחולל השאילתות שיחזיר לפי הוראות בנייה מראש את השאילתא.  

<query>
    <num>1</num>
    <details>
        <desc>שאילתא שתחזיר את המוציאים לאור</desc>
        <type>דינימי</type>
        <WebFormNames>WebForm1.asp,WebForm3.asp</WebFormNames>
    </details>
    <StaticParams>
        <Param>
            <ParamName>pub_id</ParamName>
            <DataType>System.Data.SqlDbType.VarChar</DataType>
            <Size>4</Size>
        </Param>
        <Param>
            <ParamName>pub_id</ParamName>
            <DataType>System.Data.SqlDbType.VarChar</DataType>
            <Size>4</Size>
        </Param>
    </StaticParams>
    <OptionalLines>
        <Line>
            <LineText>And City=@city</LineText>
            <LineSimul>{O1}</LineSimul>
            <Param>
                <ParamName>City</ParamName>
                <DataType>System.Data.SqlDbType.VarChar</DataType>
                <Size>20</Size>
            </Param>
        </Line>
        <Line>
            <LineText>And country=@country</LineText>
            <LineSimul>{O2}</LineSimul>
            <Param>
                <ParamName>country</ParamName>
                <DataType>System.Data.SqlDbType.VarChar</DataType>
                <Size>30</Size>
            </Param>
        </Line>
        <Line>
            <LineText>And state=@state</LineText>
            <LineSimul>{O3}</LineSimul>
            <Param>
                <ParamName>state</ParamName>
                <DataType>System.Data.SqlDbType.VarChar</DataType>
                <Size>2</Size>
            </Param>
        </Line>
        <Line>
            <LineText>And Pub_name like '%@pub_Name%'</LineText>
            <LineSimul>{O4}</LineSimul>
            <Param>
                <ParamName>pub_Name</ParamName>
                <DataType>System.Data.SqlDbType.VarChar</DataType>
                <Size>40</Size>
            </Param>
        </Line>
    </OptionalLines>
 <SQL>
        Select    City,
            Country,
            State,
            Pub_name,
            pub_id
        From     Publishers
        Where    IsNumber(Pub_id)
        and Pub_id = @pub_id
        {O1}
        {O2}
        {O3}
        {O4}
    </SQL>
</query>

מצב שני הוא בו ה-DAL שלנו מוטמע בתוך הישויות העסקיות.
לפי הדוגמה למעלה אין לנו בכלל CowDAL אלא על Cow עצמה יושבות מתודות כמו Create, Update ו-Delete שספצפיות למופע אחד של פרה (בניגוד ל-CowDAL שיעבוד עם מתודות סטטיות). כמו כן יהיו לנו כמה מתודת סטטיות כמו GetAllCows שיחזיר את כל הפרות. עכשיו השאלה היא שוב, מה עושים עם ה-Find (הסטטי) שלנו?
בדומה לקודם, נבנה מתודה ספציפית למסך החיפוש הספציפי שלנו שמקבלת את כל הפרמטרים שעשויים להישלח ואת אלו שהם null לא נוסיף לשאילתא.
נבנה את השאילתא הדינמית לפי הנתונים שנשלחו (וכמובן שעדיף שמחלקה אחרת תבצע את הבנייה הדינמית לפי הגדרות מראש כמו שהראנו קודם), נבצע אותה ונפרמט את התוצאות לפרות.

עכשיו עולה שאלה יותר מעניינת, למה הפרדתי בין שתי המקרים אם בפועל עושים בהם בדיוק אותו דבר?
במקרה השני (שבו ה-DAL מוטמע בתוך הישויות העסקיות) אפשר ורצוי לשלב Presistence layers או ORM. כלומר, שאת המימוש של מחולל השאילתות נשאיר לאיזה פריימוורק שאחראית על בניית השאילתא ואנחנו רק נדאג לדבר איתה בשפה שהיא מבינה. מרבית ה-ORMים יתנו לך איזה ממשק פאסודו-SQLי כזה למקרים בהם אתה רוצה להזין פרמטרים. למשל לא צריך אפילו להגיע למצב של מסך חיפוש, אלא מספיק לשאול "איך אני מחפש את כל הפרות ששם עדנה?" ושם כבר חייב להתגלות ממשק שבו אפשר לבקש את כל הפרות עם פרמטר מסויים. לדוגמה ActiveRecord מאפשר שימוש ב-NHibernate Expressions והקוד שלנו יראה משהו כזה:

 

        public static Cow[] FindAllWithTheseProperties(string Name, decimal? ID,
                                                            , DateTime? BetweenBirthDate, DateTime? AndThisBirthDate)
        {
            List<ICriterion> Crit = new List<ICriterion>();


            if (ID != null)
                Crit.Add(Expression.Eq("Id", ID));


            if (!string.IsNullOrEmpty(Name))
            {
                SimpleExpression FirstName = Expression.Like("FirstName", Name, MatchMode.Anywhere);
                SimpleExpression FamilyName = Expression.Like("FamilyName", Name, MatchMode.Anywhere);
                ICriterion ExpressionThatSearchsOneNameInAll = Expression.Or(FirstName, FamilyName);
                Crit.Add(ExpressionThatSearchsOneNameInAll);
            }


            if (BetweenBirthDate != null)
                if (AndThisBirthDate != null)
                    Crit.Add(Expression.Between("BirthDate", BetweenBirthDate,AndThisBirthDate));
                else
                    Crit.Add(Expression.Eq("BirthDate", BetweenBirthDate));


            List<Order> order = new List<Order>();
            order.Add(Order.Desc("FirstName"));
            order.Add(Order.Desc("Id"));


            return ((Cow[])(ActiveRecordBase.FindAll(typeof(Cow), order.ToArray(), Crit.ToArray())));
        }

 

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

 

קישור להמשך הדיון המאוד מעניין (מומלץ בחום): http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=87733298

 

Question from Tapuz .Net forum: MasterPage causes CrossPagePostBack to stop working

שאלה:

לא ניתן להשתמש ב - CrossPostBack בדפים שמשתמשים ב - MasterPage,
כלומר אם הדף השולח הוא עם MP, הדף שנשלחים אליו הנתונים לא יזהה את הפקדים ב PreviousPage כאשר נעבוד עם this.PreviousPage.FindControl,
האם מישהו מכיר פתרון לבעיה הזו

 

תשובה:

FindControl אינו מבצע חיפוש היררכי בתוך Container אלא מבצע חיפוש בן רמה אחת בלבד בתוך בניו הישירים של Container. כאשר אין MasterPage אז this.PreviousPage יבציע ל-Form שהוא ה-Container העליון בדף הכתוב. כאשר יש MasterPage אז this.PreviousPage יצביע ל-MasterPage עצמו שהוא בתוכו מכיל את ה-Form וכך הלאה.

זאת אחת מהסיבות הרבות שלא צריך להשתמש ב-FindControl (התלות במיקום היחסי של פקדים). עוד סיבה טובה היא העובדה ש-FindControl הוא loosly typed שמבוסס על מחרוזות ולא על דברים שבאמת עוברים קומפילציה. ככה שאם נכתוב אפליקציה שמשתמשת ב-FindControl ואז חס וחלילה נשנה את שם הפקד או מיקומו האפליקציה לא תעבוד, אבל גם תעבור קומפילציה.

הפתרון הפשוט והבעייתי של המצב הזה הוא לחפש את אלמנט ה-form בתוך ה-MasterPage ובתוכו לחפש את הפקד שלך (וכך הלאה בכל רמת קינון נוספת שבתוכו יושב הפקד). תוכל לגלות את הנתיב "הנכון" (ביחס לאותה שנייה בה אתה מפתח את האפליקציה) באמצעות נבירה וחיפוש ב-PreviousPage.Controls במצב דיבאג עד שתגיע לפקד שאתה מחפש.  

הפתרון הנכון במקרה הזה הוא שוב עבודה עם מאפיינים. נגיד ויש לנו טופס Content1.aspx שיש בו כפתור ואנחנו רוצים לגלות מה הטקסט של הכפתור בדף Content2.aspx שאליו נבצע Cross Page PostBack. ברמת Content1 נחשוף מאפיין שיציג את הטקסט של הכפתור שאותה ינצל Content1.

public partial class Content1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
    }
    public string ButtonText
    {
        get
        {
            return Button1.Text;
        }
    }
}

בטעינה ב-Content2 נבדוק שבאמת מדובר ב-Cross Page PostBack ואם כן ניקח את הדף הקודם ונמיר אותו לסוג Content1 ובכך נקבל גישה למאפיין החדש שלנו.

public partial class Content2 : BasePage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (this.PreviousPage != null)
        {
            Content1 myPreviousPage = (Content1) PreviousPage;
            Response.Write(myPreviousPage.ButtonText);
        }
    }
}

הסיבה לביצוע ההמרה היא כמובן ש-PreviousPage הוא מסוג System.Web.UI.Page שאינו מכיל את המאפיין החדש שלנו ואלינו לדאוג לבצע את ההמרה מ-Page לסוג דף הספציפי שלנו בכדי שנקבל גישה תכנותית למאפיין החדש. בדומה לדוגמה הקודמת נדאג גם לא להעתיק ממקום למקום את ההמרה של Previous Page ונדאג ליצור אותו מחדש ברמת Conten2.

public partial class Content2 : BasePage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //if (this.PreviousPage != null)
        //{
        //    Content1 myPreviousPage = (Content1)PreviousPage;
        //    Response.Write(myPreviousPage.ButtonText);
        //}
        if (this.PreviousPage != null)
            Response.Write(this.PreviousPage.ButtonText);
    }
    private new Content1 PreviousPage
    {
        get
        {
            if (this.PreviousPage == null)
                return null;
            return (Content1) this.PreviousPage;
        }
    }
}

יש גם סיבה הרבה יותר תיאורטית ללמה זאת צורת עבודה נכונה והיא קשורה למילים גבוהות וגדולות כמו Cohesion גבוה בתוך כל דף (שהוא מארז בפני עצמו לפי מודל הקומפילציה) ו-Coupling נמוך בין הדפים עצמם. במילים של בן-אדם נורמלי, זה אומר שאנחנו רוצים שכל דף יהיה אחראי כמה שיותר לעצמו ולא ליצור תלות מיותרת בין הדפים. למשל אם נשתמש ב-FindControl בדף Content2.aspx נקבל מצב ששינוי במבנה ההירכי של פקדים ב-Content1.aspx או בשם הפקדים יגרור לכך ש-Content2.aspx יכשל בצורה שקטה. לעומת זאת כאשר Content1.aspx חושף רק את המידע הרלוונטי לדפים שמשתמשים בו והוא אחראי שהמידע הנכון יחשף אנחנו יוצרים תוכנה שיהיה הרבה יותר קל לתחזק והרבה יותר עמידה לשינויים.

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=87412250

Question from .Net Tapuz forum: How to change an ASP.Net MasterPage programmatically from inside the Page (The OOP way)

שאלה:

לא ניתן לשתף באופן ישיר משתנים בין MasterPage והדפים שמתמשים בו(כלומר לגשת מדף X למשתנה שנמצא ב - MasterPage שלו),
יש כל מיני חלטורות כאלו (לדוגמה לשתף עם Session או כל מיני דברים מסובכים), האם מישהו מכיר פתרון קל, נוח ויעיל לנושא

 

תשובה:

הבעיה הראשונה היא דווקא בעיה מאוד מעניינת ומאוד נפוצה - עבודה מול MasterPage מתוך הדף (Page) שלנו.

בואו נציג דוגמה מאוד פשוטה. יש לי MasterPage שמציג ככותרת את השם של הדף הנוכחי לפי ה-SiteMapProvider שלי. במימוש הכי בסיסי, יש לי MasterPage עם Label וה-Label.Text שלי מציג את האינפורמציה מקובץ sitemap. נביט על קובץ ה-siteMap:

// Web.sitemap
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="Content.aspx" title="Hello world!" />
</siteMap>

אני רוצה שבראש כל עמוד באפליקציה שיש לה MasterPage שהוא יציג אוטומטית את ה-Title מהקובץ XML הזה. נו טוב, נזרוק איזה Label בשם lblHeadline בתוך ה-MasterPage:

// MasterPage.master (desginer)
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="lblHeadline" runat="server" Text="Label"></asp:Label><br />
        <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
        </asp:contentplaceholder>
    </div>
    </form>
</body>
</html>

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

public partial class MasterPage : System.Web.UI.MasterPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        lblHeadline.Text = SiteMap.CurrentNode.Title;
    }
}

ניצור דף Content Page בשם Content.Aspx (שמוגדר בקובץ ה-sitemap שלנו) שה-MasterPage שלו הוא ה-MasterPage שלנו. וכאשר נריץ את Content.aspx באמת נקבל שמוצג לנו Hello world.

כל זה טוב ויפה ומדגים עבודה עם SiteMapProvider שנהדרת ל-Demoים, אבל איך זה בכלל קשור לשאלה שלך? זה לא. בערך כאן לרוב מפסיקים המאמרים. אז בוא ניקח את זה צעד קדימה, יש לנו דף Content2.aspx והוא לא מוצהר ב-SiteMapProvider (כלומר, אין לו אלמנט ב-web.sitemap) וה-MasterPage שלו הוא ה-MasterPage שלנו. אם ננסה להריץ את הקוד שלנו עכשיו נקבל שגיאה ש-SiteMap.CurrentNode הוא בכלל null והרי זה נכון כי לא הגדרנו SiteMapNode לדף הנוכחי החדש שלנו.

לשם הדוגמה שלנו נחליט שאנחנו רוצים שהדף הרגיל שלנו (Content2.aspx) יקבע את הכותרת ב-MasterPage. אנחנו רוצים ש-Content2.aspx יוכל לקבוע את מה שיוצג ב-label ב-MasterPage.

דבר ראשון, צריך שננסה לקבוע כותרת רק אם היא קיימת ב-SiteMapProvider:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (SiteMap.CurrentNode != null)
            lblHeadline.Text = SiteMap.CurrentNode.Title;
    }

זה היה החלק הפשוט, עכשיו נחשוף מאפיין  (גם באנגלית: Property) מתוך ה-MasterPage שיאפשר לשנות את הטקסט של ה-Label:

    public string HeadlineText
    {
        get
        {
            EnsureChildControls();
            return lblHeadline.Text;
        }
        set
        {
            EnsureChildControls();
            lblHeadline.Text = value;
        }
    }

לא עשינו כאן הרבה, אמרנו "תקבל מחרוזת, תהיה בטוח שבאמת קיים Label ולתוך ה-Label הזה תכניס את הערך שקיבלת, אההה ואתה גם יכול להחזיר את הערך הנוכחי ב-Label". עכשיו בתוך הדף עצמו נרצה לגשת ל-MasterPage שלנו. חשוב לשים כאן שיש שתי מחלקות שהטיפוס שלהן הוא MasterPage: יש לנו את System.Web.UI.MasterPage ויש לנו את המחלקה שיורשת ממנה בשם MasterPage שיושבת בפרוייקט שלנו. ההבדל הוא שהמחלקה שלנו היא היחידה שמכילה את המאפיין הזה וכל דף באשר הוא מכיל הפנייה לטיפוס של הפריימוורק ולא לטיפוס שלנו. אז בתוך הדף שלנו (Content2.aspx) נמיר את ה-MasterPage שלו לטיפוס MasterPage שלנו ואז ניגש למאפיין.

public partial class Content2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        MasterPage myMasterPage = (MasterPage) this.Master;
        myMasterPage.HeadlineText = "www.JustinAngel.Net";
    }
}

ואכן בתור כותרת נקבל www.JustinAngel.Net. ניגשנו ל-MasterPage של הדף הנוכחי, המרנו אותו מהטיפוס של הפריימוורק לטיפוס שלנו וניגשנו למאפיין שהוספנו לו. וזאת התשובה לשאלה שלך - מאפיינים.

עכשיו ניקח את זה עוד צעד קדימה. אני לא רוצה בכל מקום שאני צריך לעבוד מול ה-Properties של ה-MasterPage שלי להמיר תמיד ידנית ל-MasterPage מהטיפוס שלנו. אני רוצה ש-this.Master תמיד יוביל ל-MasterPage שלנו. אז נגיד ככה: כל טופס באפליקציה לא יירש ישירות מ-System.Web.UI.Page אלא יירש ממחלקה שלנו שיורשת מ-Page ותיקרא BasePage.

// BasePage.cs
public class BasePage : System.Web.UI.Page
{
    public BasePage()
    {
    }
}
// every *.aspx.cs in the app
public partial class Content2 : BasePage

עכשיו נגיד לאפליקציה שכל פעם שפונים ל-this.Master אנחנו רוצים את ה-MasterPage שלנו.

public class BasePage : System.Web.UI.Page
{
    public BasePage()
    {
    }
    public new MasterPage Master
    {
        get
        {
            return (MasterPage) base.Master;
        }
    }
}

שים לב לזה שהשתמנו במילת המפתח new ששמנו את התיאור של המאפיין. הסיבה היא שרצינו להשתמש במשהו שלא קיים נכון להיום בדוט נט - Convient types. לא נסביר בדיוק מה זה, אבל נסתפק בזה שאין את זה וכפתרון עקרוני אני משתמש ב-new שצריך להחזיר טיפוס ספציפי שיורש מטיפוס שמוגדר יותר למעלה בהיררכיה. ועכשיו נוכל לכתוב ככה:

// content2.aspx.cs
public partial class Content2 : BasePage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //MasterPage myMasterPage = (MasterPage) this.Master;
        //myMasterPage.HeadlineText = "www.JustinAngel.Net";
        this.Master.HeadlineText = "www.JustinAngel.Net";
    }
}

זאת הייתה דוגמה מלאה של איך ומתי צריך לעבוד מול MasterPages. זה אומר לחשוף ברמת ה-MasterPage מאפייני גישה (גם באנגלית: Properties) , זה אומר לבצע ירושה מ-BasePage, וזה אומר לחשוף ברמת ה-BasePage את הטיפוס הספציפי של ה-Master כדי שלא נצטרך כל הזמן לבצע המרה.

 

קישור:  http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=87356136

Next Tapuz Usergroup: 3Tiers Architecture by Justin-Josef Angel + ActiveRecord by Oren Eini

קבוצת משתמשי פורום דוט נט בתפוז

 

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

 

מקווים לראותכם, המארגנים,
ג'סטין-יוסף אנג'ל ויאיר אובל

       

 

תאריך ושעה: 30 באוקטובר, 18:00-21:00
מקום: מיקרוסופט ישראל, רעננה - הפנינה 2, קומת קרקע 0 09-7625100
פרסים: בסוף הפגישה יוגרלו שלושה ספרי דוט נט מהוצאת הוד-עמי בין המשתתפים אשר ימלאו את המשובים!

על סדר היום:
18:00 – 17:30 התאספות
19:20 – 18:00 דוגמאות מעשיות לפיתוח פרוייקטים מונחי עצמים בדוט נט, ג'סטין-יוסף אנג'ל
[ רבע שעה הפסקה ]
21:00– 19:35 Using Active Record: How To Write Less Code, אורן עיני

       

 

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

 

אתם לא מצליחים לתחזק אפליקציות שכתבתם. נקודה.
אל תעמידו פנים, אתם יודעים שכל פעם שאתם צריכים לגשת לקוד שכתבתם לפני יותר משבוע, אין לכם מושג איך לגשת אליו. כותבים 100 שורות קוד בלחיצה של כפתור, פותחים חיבור למסד נתונים בטעינת הדף ועושים חישובים מתמטיים בתוך תא של GridView. יש דרך יותר טובה לכתוב קוד, שבה תוכלו לכתוב קוד ולתחזק אפליקציות כמו שאתם תמיד רציתם.
כל פעם שאתם מעתיקים קוד ממקום למקום ברחבי האפליקציה שלכם, אתם יוצרים קוד שאתם לא יכולים לתחזק. כל פעם שאתם כותבים יותר מ-5 שורות קוד במתודה אתם יוצרים קוד שאתם לא תבינו. כל פעם שאתם מרגישים כאילו אתם מבזבזים זמן לריק, אתם באמת סתם שורפים זמן.
ב-OOP (באנגלית: Object Oriented Programming) יש לנו כלל אחד ויחיד שחוזר על עצמו בכמה צורות: תפתור בעיה פעם אחת ופעם אחת בלבד, אל תעתיק קוד ברחבי האפליקציה שלך, תשים כל אחריות במקום שלה. במהלך ההרצאה נתקוף את הסוגיה של פיתוח אפליקציות לאור הכלל האחד הזה – כתיבת קוד נכון. כתיבת קוד חזק. כתיבת קוד שעובד וקל לתחזוקה.
כשאנו נתקלים בבעיה לכולנו יש את הנטייה לפתור אותה ולשכוח ממנה, וככה פעם הבאה שניתקל בה פשוט לא נידע מה לעשות. OOP מראה לנו דרך לפתור בעיה ולפתור אותה פעם אחת. אנו מקבלים קוד שקל לתחזוקה, קל לקריאה וקל לפתח איתו.
תצטרפו אלינו במסע של לימוד באמצעות דוגמאות קוד את המשמעות והכוח האמיתי של מחלקות, פרוייקטים, מאפיינים, מתודות ועוד.

על המרצה:
ג'סטין-יוסף אנג'ל עובד בחמש שנים האחרונות בפיתוח בתחום ה-web ומתוכם בשלוש שנים האחרונות כמפתח דוט-נט בכיר.
בשלוש שנים אחרונות אלו הוא עבד וייעץ בפרוייקטים רבים תחת כובעים שונים וביניהם: יועץ, אחראי פרוייקט, ארכיטקט ותוכניתן בכיר. הוא פעיל ועובד בתחומים רבים: ניהול פרוייקטים במתודולוגיות Agile, פיתוח מערכות מבוזרות, ASP.Net, ניתוח, אפיון ופיתוח מונחה ארכיטקטורת עצמים, Web 2.0, ניהול והכשרות.
ג'סטין-יוסף אנג'ל מחזיק את בבלוג הוותיק ביותר בעברית בתחום הדוט נט - www.JustinAngel.Net. בבלוג זה הוא מפרסם מאמרים טכניים ו-Webcastים שונים המיועדים הן למפתחים מתחילים והן לגורואים בתחומם כאחד.
הוענק לג'סטין-יוסף אנג'ל תואר MVP לשנת 2006 שניתן ע"י מיקרוסופט למפתחים שהראו ידע מעמיק, מיומנות טכנית רצינית ובעלי היסטוריה של מעורבות קהילתית.

       

 

הרצאה: Using Active Record: How To Write Less Code !
אורן עיניי

 

מה אם נאמר לכם שאחרי הרצאה של שעה וחצי תוכלו להיפתר מכתיבת שאילתות SQL בדוט נט? תגיעו להרצאת ActiveRecord של אורן עיני ותקבלו את היכולת להיפתר ממרבית הקוד זבל שלכם שמטפל בגישה למסדי נתונים.
לא נמאס לכם לכתוב את אותו קוד שוב פעם ושוב פעם? כל פעם לפתוח חיבור למסד, לחשוב איזה שדות להחזיר, להציג בטבלה או בתגית טקסט? כל פעם לפתוח חיבור למסד, לכתוב שאילתת Insert/Update/Delete, לבצע את השאילתא ולהגיד למשתמש בוצע? כל פעם אותו דבר. פלטפורמת ActiveRecord בדוט נט תאפשר לכם לבצע את מרבית הפעולות במערכות מידע בלי לכתוב שורת SQL אחת והכל בצורה נוחה וקלה.
ב-ActiveRecord אנו משתמשים ב"אובייקטים" של שפות דוט-נטיות בכדי לפתור את המתכנת ממשימות חוזרות על עצמן. אותם "אובייקטים" מודעים kסכמה (טבלאות וקשרים) במסד הנתונים ויודעים כיצד לחפש אובייקטים ממסד הנתונים, כיצד לעדכן שינויים בהם, כיצד להוסיף אובייקטים חדשים וכיצד למחוק אובייקטים. ודרך אותן פעולות הן יעדכנו עבורנו את מסד הנתונים ויחזירו עבורנו נתונים. כל זה בלי לכתוב שורת SQL אחת.
תוך כדי כתיבת קוד מינימלית ושימוש חוזר באותם עקרונות שמניעים כל מערכת מידע ActiveRecord תחסוך לך זמן, מאמצים והשקעה כוללת בפיתוח מערכות מידע. למען האמת, פיתוח מערכות מידע לא היה קל יותר מעולם היות וב-ActiveRecord אנו מבצעים רק את הפעולות העסקיות המינימליות הנדרשות, ובכך מורידים את כמות זמן הפיתוח בצורה ניכרת.
כל היתרונות האלו יהיו שלכם ותדעו ליישם אותם כאשר תגיעו להרצאה.

על המרצה:
אורן עיני הוא מפתח בכיר בחברת We! בתחום דוט-נט, תוך התמקדות ברמת הארכיטקטורה וגישה לנתונים. במהלך השנה האחרונה אורן עובד בעיקר על מערכות עיסקיות מורכבות תוך שימוש בדוט נט 2.0, NHibernate ו Castle.
אורן חבר פעיל במספר רב פרויקטי קוד פתוח כולל Castle, NHibernate ו Rhino Mocks.יש לאורן בלוג ב http://www.ayende.com/Blog בו הוא מפרסם הגיגים שונים לעיתים רחוקות.

מרתון שאלות דוט נט בפורום דוט נט בתפוז - 18/10/2006

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

התשובות המוצלחות ביותר (שלי ושל משתתפים אחרים) יפורסמו בבלוג זה.

 

כמו כן ניתן לפרסם ישירות בעמוד הראשי של פורום דוט נט בתפוז שאלות: http://www.tapuz.co.il/tapuzforum/main/forumpage.asp?id=831

 

קישור לשרשור המדובר בפורום דוט נט בתפוז:

http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=87691915

 

Posted by Justin-Josef Angel [MVP]
תגים:

Justin Updates: Next Tapuz .Net Usergroup meetings, looking Work part 2 and future lectures

Tapuz Usergroup

Next Tapuz Usergroup meeting is on 30/10/2006 at Microsoft Israel Ra'nana 18:00. I'll be giving a talk about 3Tiered applications and Oren Eini will be giving a talk about ActiveRecord. Full invite to come later this weekend.

                                                                                        

Work

Regarding my previous work related post. I've decided to actively look for work as a Dev VP/whatever of a small startup (up to 16 develops). Now it's just a matter of actually knowing someone who's just opening up a fully-funded .Net startup. Apparently, I don't know anyone like that. Do you?

I have opted no to do a career change to architect, nor work as a freelance instructor, nor regress to a non-senior developer.

My job search as a big-guy-in-charge-of-development-in-a-small-.net-oriented-startup doesn't mean I've decided not to pursue a freelancer consultant career or work as a senior .net developer somewhere. The only reason I've started looking this early for that position is because it's going to be a lot harder finding this kind of job and I'd like to have a head start.

                                                                                        

Lectures & Articles

Relating to the previous topic, I will officially be looking for work as of Thursday this week. I'm happy with the last couple of years of working where I have, but I've been feeling like it's time to move on for quite some time now. And why am I telling you this you ask? Because that means I'll FINNALLY have some free time to write articles and do lectures at Israeli Usergroups.

Here are my options for talks I've been thinking about pitching to usergroup leads in Israel:

  • Security - ".Net security for the rest of us". A 3 hour talk covering a different .Net security related topic every ten minutes. I'll start off simple with something like IDbParameter (SQL injection), move to how to deploy an ASP.Net  web application (always use custom errors, debug is false, precomiple your app and that sort of things) and talk about why we should use strong named assemblies.
  • Tapuz - Well it's my usergroup and I've already written I'll be giving a talk in three weeks on 3Tiered applications.
  • Sequel - "Upgrading to sequel 2005 for developers". The name of the talk explains it's content rather adequately. It's a 3 hours talk in which I'll switch a PowerPoint slide every 1 minute exactly. About 150 slides all of which contain Code Samples. If your brain explodes, I win.
  • Architects - I though about giving something in that usergroup but I'm just not sure what I've got to offer that kind of crowd. I don't talk about SOA and I don't care much for DDD (nothing personal I just like my talks to have at lest SOME code). Frankly, I just don't want to get stoned to death by a raging mob of architects.
  • C# & VB - Well pretty much every topic in the world. Maybe something like best practices, some neat tips & tricks or whatever will interest people coming to that groups. 

Basically, I'm looking for YOU to tell me what will interest YOU and if these talk (or others) are up your alley.

 

I'll also sit down and write up some Justin-styled articles on my Hebrew blog. Right now I've got two topics I'd like to cover: .Net Threading and Design-time programming. Each is roughly 50-100 pages long. If you've got ANY other .Net related subject you'd like to read a very well written article in Hebrew, comment here and I'll probably end up writing it if it's interesting enough. Seriously, I'm wide open here.

 

One thing I do not plan on completing at this time is my book that I'm working on.

Justin looking for work: Part 1 - Considering possible career paths

As some of the people who are close to me (and whom I trusted with this information) I will as the end of this month start looking for a new job. This has been coming for a long time now (I've been training my substitutes for this day over six months).

My first order of business is to find out WHAT career I'd like to peruse. 

As you can see I've got six possible career paths I can choose to peruse which vary on payment and amount of time I'd have to invest in that job.  

Let me start of by saying that all of these jobs are careers I'd love to peruse. The big question is WHAT I WANT to do.  There are three big categories of careers: Freelancer, Salaried Employee (In Hebrew: שכיר) or senior management.

 

freelancer

As a freelancer I've got two major options: Instructor and Consultant. As an instructor I'll give two 3 hours classes each day. I LOVE teaching things I'm passionate about and I'm very passionate about anything related to the .Net Framework. A good part of my current job is making sure everyone in my deportment are up to speed in whatever we're working at the moment. There were periods I spent over 50% of my time at working with inexperienced developers in training to higher levels in their perspective fields.  Probably the main reason I got my MVP award is my ability to talk with people at their own level on various technical subjects.

While I enjoy teaching, the pays sucks in comparison to any other career. While money isn't my main motivation, I won't lie - It's important to me. I work hard and I know how much I'm worth.

Another option as a freelancer is becoming a consultant.  Basically helping various companies create a variety of solutions on a per-project basis. That's basically what I do on my current job. There were periods I juggled half a dozen projects at the same time, so I've got a lot of experience in ALOT of fields. I know I've got a lot to contribute. 

The main advantage of this kind of job is also it's main disadvantage - you never strike up roots where you work. You keep moving between different work environments and different companies. It's good because you gain ALOT of experience that way, but it does require a lot of hard work to keep bringing companies up to speed.

Although the headline of this possible career path is "Freelancer" I am currently not considering opening my own business as a software company. I'm good with other people, I love working with people and sharing knowledge between us. Working solo just isn't me.

 

 Salaried Employee

My definition of a "Salaried employee" is "You've got a boss and you have a well-defined base salary". My current boss is a great man and I love him dearly. It's also very nice to know that at the end of the month I get a paycheck with a fixed base salary.

A possible career is being an every-day run-of-the-mill programmer. You get up at the morning, you go to work, you churn some code and you go home. You don't worry about schedules, you don't look at the great picture and you don't care about the customer. I don't see myself being this kind of person. I'm a big picture kinda person. But I sure would love going home at six PM and the moment I leave the office - I leave the office behind me. The pay is good, I'll have more time for my personal life, and I could possibly sleep more then three hours each night.

Another option is doing a career change to an architect. I LOVE coding. I love going from the big picture to the macro level. But I could do more staying only on the big level. If I change my career to an architect I'll no longer be the big fish in the pond. I'll have to start as a junior architect because my architecture expertise is limited. Actually, I'm pretty sure I wrote this option up just to make sure I consider all possible options. I don't see myself working as an architect right now. It's just no me.  

My current job is a senior developer and it's a great job. I don't spend all my time dealing with schedules & customers like team leads do, but I'm up-level enough to be a project manager. I know the schedule, I know the features and I know what we need to do to get the software up and running. It's like being armed with every possible weapon and a platoon soldiers and going to battle everyday. You can't just go storming in and wasting everyone's time. You've got to have enough of a plan to make sure you know what you're doing and you've got to know every big bump in the road. This job is very time consuming as you're never actually of the clock. Your MIND is always working on some problem. Wether it's a class you can refactor, a business logic you have no idea how to code, or adopting a new technology. I defiantly seeing my self being a senior developer at my next job.

 

Senior management

 Well, I've got the skills and the experience to manage a small development deportment of up to 16 developers. A position I'd love to have is the Dev VP of a small Startup. I could definitely be great at this kind of job as it will take all my skills to the max. I'll have to use everything I know and the some more. It means managing people, being a 100% in charge of making a project work and of every possible aspect of the development process. 

This kind of job will be a lot of work, I'll have to search for this kind of job a lot, it would take a lot out of my life and I'll get a lot out of it. But it could be great.

 

To conclude one of the longest personal posts I've ever written, I've got some thinking and decision making in front of me. If anyone has any input or advice, I'd love to hear it. 

Question from .Net Tapuz forum: How does ASP.Net decides how to render pages and how can I make it render to a mobile device?

שאלה:

למה התקנים ניידים (במקרה שלי נוקיה 6230I) לא יכולים להציג WebForm שנוצר ע"י VisualStudio 2005 אלא רק MobileWebForm?
למרות שהגעתי למצב שפלט הHTML של שניהם היה שווה לחלוטין.

הבעיה שלי שהVisuel Studio 2005 לא מכיר את כל אפשרויות הגלשן של נוקיה, וכל עיצוב שלדעתו הגלשן לא מכיר נמחק ע"י ASP.NET בהרצה... למישהו יש ניסיון בזה?

תשובה:

הקביעה של היכולות של הדפדפן שלך נקבעת ע"י הפריימוורק ובהתאם הרנדור.

בדוט נט 1.1 קיים אלמנט <browserCaps> שיושב ברמת ה-machine.config (למרות שגם ניתן לקבוע אותו ברמת ה-web.config) ומכיל את ההתאמה בין המאפיינים השונים של הדפדפן (בעיקר שמו ופילטרים נוספים המבוססים על כך) לבין היכולות של הדפדפן ביחס למה שהפריימוורק צריך לדעת (האם תומך בטבלאות, האם תומך ב-CSS, האם תומך ב-XML, האם תומך בג'אווה סקריפט, האם תומך בעוגיות וכיו"ב). כלומר, באמצעות regexp על שם הדפדפן מזהה דוט נט 1.1 את הדפדפן ולפי הזיהוי החח"ע הזה קובע את היכולות שלו.
למשל, הנה ה-<browserCaps> שהיה נהוג להוסיף כדי לגרום ל-ASP.Net 1.1 להכיר בפיירפוקס כדפדפן עילי שיכול לתמוך בג'אווה סקריפט (ובכך בוולידטורים):

<browserCaps>
<case match="^Mozilla/5\.0 \([^)]*\) (Gecko/[-\d]+)(?'VendorProductToken'
   (?'type'[^/\d]*)([\d]*)/(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*)))?">
    browser=Gecko
    <filter>
        <case match="(Gecko/[-\d]+)(?'VendorProductToken'
(?'type'[^/\d]*)([\d]*)/(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*)))">
            type=${type}
        </case>
        <case> <!-- plain Mozilla if no VendorProductToken found -->
            type=Mozilla
        </case>
    </filter>
    frames=true
    tables=true
    cookies=true
    javascript=true
    javaapplets=true
    ecmascriptversion=1.5
    w3cdomversion=1.0
    css1=true
    css2=true
    xml=true
    tagwriter=System.Web.UI.HtmlTextWriter
    <case match="rv:(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*))">
        version=${version}
        majorversion=0${major}
        minorversion=0${minor}
        <case match="^b" with="${letters}">
            beta=true
        </case>
    </case>
</case>
</browserCaps>

 

בדוט נט 2.0 הקונספט הזה השתנה עקב קיומם של כמה אלפי דפדפנים בימינו. מה שנהוג כיום הוא ליצור קבצי browser שמופקים אוטומטית מאתר מיקרוסופט. הקבצים האלו נוצרים בכך שתפתח מהדפדפן הרלוונטי אתר ספציפי של מיקרוסופט וזה יחזיר לך את קובץ ה-browser לאותו דפדפן. קובץ ה-browser מכיל גם את תבנית הזיהוי של אותו הדפדפן, אבל בנוסף מכיל לא רק איזה 10-20 מאפיינים לאותו דפדפן אלא מכיל באזור ה-300 מאפיינים שונים של אותו דפדפן וזה מגיע לרזולוציות מאוד חזקות של אבחון יכולות הדפדפן. את כל קבצי ה-browser הרלוונטים אוספים ביחד בתיקייה אחת ומשתמשים בכלי aspnet_regbrowsers.exe (או משהו כזה) ומקמפלים אותם ל-dll שנקרא browser assembly ואותה פורסים כחלק מאפליקציה או כ-browser assmebly ראשית של פריימוורק על השרת. מיקרוסופט הפיצה בהתחלה אוסף קטן של 3,000 קבצי browser לכל הדפדפנים שיכלה למצוא ומאז הפסיקה את העדכון של קבצים אלו וסומכת ע"י גורמים צד שלישי או הלקוחות שיהיו להם את קבצי ה-browser העדכניים.
למשל, תוכל למצוא במחשב שלך ב-
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\Browsers\ie.browser את קובץ ה-browser של Internet explorer.

במקרה שלך אם אתה רוצה שהפלאפון שלך יתמך כדפדפן עילי (ע"ע IE) תצטרך בהתאם לגירסת הפריימוורק שלך להגדיר אותו כאחד כזה. בין אם זה ע"י הוספת אלמנט ל-<browserCaps> בדוט נט 1.1 או ע"י יצירת קובץ browser בדוט נט 2.0 ושינוי אותו כך שיתרנדר בדומה ל-IE. רק אזהרה מראש, זה שה-HTML זהה, לא אומר שבכל מצב הוא תמיד יהיה זהה. יתכן וכי הגעת למצב מאוד בסיסי ופשוט שבו ה-HTML באמת זהה, אבל Mobile Controls יכולים גם להתרנדר לאפליקציות על פלאפונים מאוד ישנים שבכלל לא דוברים HTML או אפילו לא יודעים מה זה WAP, מה שבלתי אפשרי כמובן ל-ASP.Net WebControls.

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp...

 

Question from .Net Forum: What's the difference between MVC and 3Tier Architecture

שאלה: מה ההבדל בין ארכיטקטורת MVC לבין ארכיטקטורת 3Tier?

 

תשובה:

3Tier או כל ארכיטקטורת שכבות אחרת מדברת על "יש לי פרוייקט א' שתלוי בפרוייקט ב' שתלוי בפרוייקט ג'" וממשיך עד תלוי בפרוייקט X. ארכיטקטורת 3Tier אומרת שיש תלות של ה-GUI על ה-DAL שלו בתורו יש תלות על ה-BL או שיש ל-GUI תלות על ה-BL שיש לו תלות על ה-DAL. הקונספט הוא הרבה פחות קונספט לוגי של אחריות והפרדה, אלא הרבה יותר קונספט של לארגן את המחלקות עצמן בקבוצות אחריות מוכרות וידועות. למשל, ב-GUI יהיה לי רק טפסים שפונים לישויות ב-BL שבתורן כותבות את עצמן באמצעות מתודות מה-DAL.

MVC (או MVC משופר שכתב לאחרונה פאולר או MVP שהוא שכלול של MVC) מסתמכים הרבה יותר על חלוקה לוגית של אחריות ושל הפרדת "מי מדבר עם מי". ב-MVC יש שלושה גם שלושה שחקנים עיקריים: Model שמכיל בתוכו את המידע עצמו בצורה של ישות מידע, View שהוא ה-GUI שנחשף למשתמש וה-Controller. עכשיו ה-Controller הוא המקום שההבדל באמת מגיע. במודלי שכבות אין מניעה שבו למשל ה-GUI יהיה זה שיוצר טיפוס BL חדש (למשל "פרה" חדשה בשם עדנה באחו מספר 3) ואז יגיד ל-DAL "אתה תשתמור את עדנה במסד נתונים". ב-MVC אסור ל-View לנשום אפילו באזור של המסד נתונים. ה-Controller נרשם לאירועים ב-View (כגון לחיצת כפתור, סגירת טופס ושינוי טקסט) והוא בתורו היחידי שבאמת יודע מה הקשר בין ה-TextBox לשם של הפרה ובין ה-DropDownList לאחו בו רועה הפרה. ככה שיוצא שה-View רק באמת מטפל בסוגיות של תצוגה וה-Controller רק באמת מטפל בסוגיות של שינוי מידע.

ההגבלה של MVCים למיניהם הרבה יותר חזקה מההגבלה של מודלי שכבות למיניהם. זה בגלל שבנוסף להפרדת השכבות שמשתמעת ב-MVCים (למרות שלמשל את עצם קיומו של DAL לא מניחים במפורש ב-MVC אלא אומרים "אלו יהיו Helper Methods") יש גם את הכלל שבו ה-GUI עצמו בכלל לא יודע את התוצאות של מה שקורה בו. מה שלא נכון ב-3Tier כי אני באותה מידה יכול ליצור מתודה של כפתור שדואגת לשמירה ועדכון במסד הנתונים גם אם הוא לא מדבר איתו בשום שלב ישירות אלא רק דרך מתודות של ה-DAL או ה-BL.

 

שאלת המשך: אצלי הGUI אף פעם לא ניגש ישירות לDAL, חשבתי שזו הדרך הכי הגיונית... למה לאפשר כזו גישה ישירה?

תשובת המשך:
מה שאתה אומר לא מדוייק, ה-GUI שלך כנראה מאוד לא ניגש ישירות ל-DAL אבל יוזם במודע ובאופן ישיר מצב שבו ה-DAL יהיה חייב להיכנס לפעולה. למשל אם יש לנו טופס "יצירת פרה" וכפתור Submit בארכיטקטורת 3Tier ככה יראה הקוד שלך:

protected void btnCreateCow_Click(object sender, EventArgs e) {
   Cow myNewCow = new Cow();
   myNewCow.Name = tbxCowName.Text;
   myNewCow.Create();
}

זה היה במקרה שה-BL שלך מכיל את ה-DAL בתוכו או במקרה שה-DAL מכיל בתוכו את ה-BL זה יראה כך:

protected void btnCreateCow_Click(object sender, EventArgs e) {
   Cow myNewCow = new Cow();
   myNewCow.Name = tbxCowName.Text;
   CowDAL.Create(myNewCow);
}

עכשיו ככה הקוד היה נראה ב-3Tier אבל טוען פאולר ובצדק שהקוד הזה בעייתי. הבעיה היא שה-GUI שלנו יודע במפורש שלחיצה על כפתור מובילה ליצירת פרה חדשה, עוד יותר גרוע הוא גם יודע ש-tbxCowName הוא השם של הפרה החדשה ואפילו עוד יותר גרוע יוצר במפורש או במשתמע פנייה ל-DAL. זה המון התנהגות להכניס ל-GUI. המון.
הקוד הזה גם יכול להיות עוד יותר נורא בזה שנגיד שקיים DropDownList ב-GUI של "אחו" לפרה ואז לפי ה-ID הנבחר ב-DropDownList נצטרך לקבוע את האחו של הפרה:

protected void btnCreateCow_Click(object sender, EventArgs e)
{
   Cow myNewCow = new Cow();
   myNewCow.Name = tbxCowName.Text;
   myNewCow.Meadow = Meadow.Find(BLHelper.SelectIndexToId(ddlMedows.SelectedIndex));
   myNewCow.Create();
}

עכשיו נתנו ל-GUI שלנו לא רק אחריות על יצירת פרה חדשה אלא היא גם יוצרת את הקשרים בין הישויות ועוד יותר גרוע מתרגמת את המשמעות של המידע ב-GUI למידע עסקי.
ב-MVC היינו יוצרים View בצורה של טופס חדש עם למשל TextBoxים, DropDownListים וכפתור שיחשוף את האירועים של שינוי פקדים ולחיצה על פקדים ובנוסף את המאפיינים (באנגלית: Properties). ה-Controller שלנו ירשם לאירועים וספציפית ירשם לאירוע לחיצה על כפתור, הוא יצור Modal חדש (במקרה שלנו, פרה) והוא זה שיהיה אחראי לתרגם מ-myCowNameText (שזה מאפיין שיחשוף ה-View) את הערך לתוך השם של הפרה. כלומר, ה-Controller הוא זה שמגשר בפועל בין View לבין Modal. ל-View שהוא GUI אסור בשום שלב ליצור אובייקטים עסקיים חדשים ובטח שלא ליזום ישירות פעולות שנוגעות לעבודה עם מידע. ה-View הוא באמת רק זה.

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp...

Question from .Net Tapuz Forum: Overhead of using MasterPages and UserControls in ASP.Net (ASP.Net compilation modal)

שאלה: האם שימוש ב-UserControls וב-MasterPages (שהוא למעשה UserControl) גורר פגיעה כלשהי בביצועים?

 

תשובה:

האבחנה שה-Master Page הוא למעשה סוג של User Control היא נכונה מאוד. דפי Master Pages  אפילו (בניגוד מוחלט לאינטואציה) מתרנדרים אחרי הדפים עצמם שאותם הם מכילים.

לשימוש ב-User Control מול שימוש ב-Custom/Composite Controls היה אימפקט קל מאוד על הביצועים ב-ASP.Net 1.1. זהו אותו הבדל שהיה על כתיבת קוד בדף ה-ASP.Net Designer (כל מיני תגיות <asp:> למיניהן) לבין הצהרה על הפקדים תכנותית. הסיבה להבדל ביצועים הזה נובעת מכך שבגישה הראשונה מאז האתחול האחרון של ה-AppDomain (שקורה כברירת מחדל פעם ב-29 שעות ב-IIS) ה-Runtime צריך לבדוק את התוכן של הדף ולתרגם אותו לפקדים תכנותיים. אותם פקדים תכנותיים הוחזקו בקבצי ה-ASP.Net Temporary files והם למעשה גירסה בלי תגיות ASP.Net של דפים עם תגיות ASP.Net.
בכל גישה ראשונה לדף מאז האתחול הדף נותח ונבנה מחדש מה שלקח בממוצע 30 שניות לדף, אך אחרי הבקשה הראשונה לא היה יותר עיכוב שכזה.


בדוט נט 2.0 יש אפשרות חדשה שהיא לבצע Precompile לכל האתר עצמו מה שלוקח את קבצי ה-ASPX וה-ASCX שבעבר נותחו רק עם פנייה לדף ומקפמל אותם למחלקות אמיתיות. את ה-Procompile הזה ניתן להגדיר בעת הפריסה של האפליקציה באמצעות Deploy Web Site של Visual Studio 2005 או ניתן להשתמש ב-aspnet_precompile.exe שמגיע עם הפריימוורק ומאפשר לקמפל אתר כאתר או לקמפל אתר לתוך תיקייה שתיפרס במועד מאוחר יותר.

עם ההצגה של מודל הקומפילציה של דוט נט 2.0 אין יותר הבדלים בין כתיבת ASPX/ASCX לכתיבת קוד מבחינת ביצועים.

בדוט נט 1.1 היה נהוג ליצור Service שעם כל אתחול של ה-AppDomain שמכיל את האתר היה מבצע בקשות מול כל האתר ובכך מבצע פרה-קומפילציה בזמן-ריצה.

 

קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp...