DCSIMG
July 2007 - Posts - Justin myJustin = new Justin( Expriences.Current );

July 2007 - Posts

Validation Application Block with Strippers: ObjectValidator (Overview of all VAB Validators)

image

 

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, פורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, פורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, פורסם ב-22.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, פורסם ב-24.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , פורסם ב-26.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, פורסם ב-29.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, פורסם ב-31.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-2.7.2007

 

 

ObjectValidator - מה המספר פלאפון שלך?

התקבלה ההחלטה שניקח מהמבקרים במועדון החשפנות את הפלאפון שלהם ונתקשר אליהם שבוע לפני היום-הולדת שלהם כדי להזכיר להם על ה-Lap dance החינם.
בנוסף, במהלך אותו ישיבה רבת-מעלה בצידי הבמה בזמן שהאורות האדומים והירוקים מהבהבים, נאמר משהו על כך שבעתיד נרצה לקחת עוד כמה מספרי פלאפון. ואם זה לא היה מספיק גרוע, שאנחנו שומרים למסד נתונים נרצה לשמור את הקידומת (054\052\056\050) בנפרד משאר מספר הפלאפון ובכלל לתוך טבלה נפרדת.

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

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

    public class CellPhone

    {

 

    }

מה המחלקה הזו תכיל? מספר פלאפון זה בטוח.

    public class CellPhone

    { 

        private string _phoneNumber;

 

        public string PhoneNumber

        {

            get { return _phoneNumber; }

            set { _phoneNumber = value; }

        }

    }

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

    public class Phone

    {

        public Phone(string PhoneNumber)

        {

            this.PhoneNumber = PhoneNumber;

        }

 

        private string _phoneNumber;

 

        public string PhoneNumber

        {

            get { return _phoneNumber; }

            set { _phoneNumber = value; }

        }

    }

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

    public class CellPhone

    {

        public CellPhone(string PhoneNumber)

        {

            this.PhoneNumber = PhoneNumber;

        }

 

        private string _phoneNumber;

        [RegexValidator(@"^05[0246]([-]{0,1})\d{7,8}$",

                        MessageTemplate="Must be a valid cell phone number")]

        public string PhoneNumber

        {

            get { return _phoneNumber; }

            set { _phoneNumber = value; }

        }

    }

נעבור בקצרה על ה-Regex לדוגמה הזה:
image_thumb26  - מחייב שמספר הפלאפון יתחיל עם 05 ואחריו 0, 2, 4 או 6.
image_thumb27 - משאיר מקום לסימן מקף "-" באמצע המספר פלאפון ומאפשר בין 0 ל-1 הופעות שלו.
image_thumb28  - בין 7 ל-8 ספרות

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

    public class CellPhone

    {

        public CellPhone(string PhoneNumber)

        {

            this.PhoneNumber = PhoneNumber;

        }

 

        private string _phoneNumber;

        [RegexValidator(@"^05[0246]([-]{0,1})\d{7,8}$",

                        MessageTemplate="Must be a valid cell phone number")]

        public string PhoneNumber

        {

            get { return _phoneNumber; }

            set { _phoneNumber = value; }

        }

 

        public string ExctractCellPhoneAreaCode()

        {

            if (PhoneNumber.Length < 4)

                return null;

            else

                return PhoneNumber.Replace("-", "").Substring(0, 4);

        }

    }

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

    public class CellPhone

    {

        public CellPhone(string PhoneNumber)

        {

            this.PhoneNumber = PhoneNumber;

        }

 

        private string _phoneNumber;

        [RegexValidator(@"^05[0246]([-]{0,1})\d{7,8}$",

                        MessageTemplate="Must be a valid cell phone number")]

        public string PhoneNumber

        {

            get { return _phoneNumber; }

            set { _phoneNumber = value; }

        }

 

        [DomainValidator("0522", "0523", "0524", "0525", "0528", // Cellcom

                        "0505", "0506", "0507", "0508", // Palaphone

                        "0544", "0545", "0546", "0547", // Orange

                        "0577", // Mirs

                MessageTemplate = "Not a valid cell phone area code")]

        public string ExctractCellPhoneAreaCode()

        {

            if (PhoneNumber.Length < 4)

                return null;

            else

                return PhoneNumber.Replace("-", "").Substring(0, 4);

        }

    }

נשים לב שאפשר לשים Validatorים של VAB על פונקציות!
יש מספר הגבלות ל-VAB עם פונקציות, בעיקר שהן לא מקבלות שום פרמטרים.

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

OK, בנינו איזה מחלקת פלאפון מתוחכמת להחריד שיודעת לעשות חצי מהכל ועוברת כמה וולידיות. אז מה?
אנחנו הרי בונים מערכת דירוג ביקור במועדון חשפנות!

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

    public class VisitRating

    {

        ...

 

        private CellPhone _cellPhone;

 

        public CellPhone CellPhone

        {

            get { return _cellPhone; }

            set { _cellPhone = value; }

        }

    }

עכשיו יש לנו דילמה. הרי יש לנו שדות ב-Winforms ו-ASP.Net שצריכים וולידציה.
והם צריכים לפנות לוולידציה ל: (1) מחלקה מסויימת (2) מאפיין מסויים.
אי-אפשר להפנות אותם רק ל-CellPhone.PhoneNumber כי אז תתפספס הוולידציה על ()CellPhone.ExctractCellPhoneAreaCode ולהפך.

אז נפנה את הוולידציה שלנו, בשני ה-GUIים ל-VisitRating.CellPhone.


ב-Winforms יש לנו את השדה:

image_thumb30
ובהתאם נקבע שהוא יפנה ל-VisitRating.CellPhone

image_thumb31

ב-ASP.Net יש לנו את השדה הבא

image_thumb32

ונקבע לו מאפיינים שיצביעו גם על BL.VisitRating.CellPhone.
image_thumb33

 

עכשיו אתם שואלים, "אבל רגע, ג'סטין, VisitRating.CellPhone הוא בכלל מטיפוס CellPhone ואנחנו מבצעים וולידציה עד עכשיו רק על טיפוסים פרימיטביים."
נכון, בשביל שה-PropertyProxyValidator של ASP.Net וה-ValidationProvider של Winforms ידעו להציב את המחרוזת שקיבלנו ב-tbxCellPhone לתוך VisitingRating.CellPhone נצטרך להמיר ממחרוזת ל-CellPhone.
נדבר על זה עוד שנגיע לוולידציה.

ההמרה ממחרוזת ל-CellPhone היא יחסית פשוטה, פשוט נאתחל מופע חדש של BL.CellPhone עם המחרוזת הרלוונטית.

ב-ASP.Net נשתמש באירוע ה-ValueConvert:

        protected void Propertyproxyvalidator8_ValueConvert(object sender, ValueConvertEventArgs e)

        {

            e.ConvertedValue = new CellPhone(e.ValueToConvert.ToString());

        }

כאן קיבלנו את הערך של המחרוזת בתוך תיבת הטקסט כ-e.ValueToConvert והחזרנו אותו בתור e.ConvertedValue.

ב-Winforms גם נשתמש באירוע ה-ValueConvert, אבל בצורה קצת שונה:

        private void VisitRatingValidation_ValueConvert(object sender, ValueConvertEventArgs e)

        {

            if (e.TargetType == typeof(CellPhone))

            {

                e.ConvertedValue = new CellPhone(e.ValueToConvert.ToString());

            }

        }

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

 

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

    public class VisitRating

    {

        ...

 

        private CellPhone _cellPhone;

 

        public CellPhone CellPhone

        {

            get { return _cellPhone; }

            set { _cellPhone = value; }

        }

    }

יהפוך ל

    public class VisitRating

    { 

        ...

  

        private CellPhone _phone;

        [ObjectValidator()]

        public CellPhone CellPhone

        {

            get { return _cellPhone; }

            set { _cellPhone = value; }

        }

    }

שימו לב שה-GUIים שלנו מריצים וולידציה על VisitRating.CellPhone ואין להם מושג בכלל מה הכללי וולידציה בתוך BL.CellPhone.

בואו נראה איך כל זה נראה ב-GUIים שלנו.
ננסה להכניס סתם טקסט:

image_thumb34

image_thumb36

ננסה להכניס את מספר הפלאפון שלי 054-6566789 שבתקווה גם יעבור וולידציה:

image_thumb37

ועכשיו נשנה את הקידומת של הפלאפון למשהו שלא קיים ב-DomainValidator שכתבנו. למשל לקידומת 0540 הלא קיימת.

image_thumb38

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

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

image_thumb39

שימו לב שבהגדרה של CellPhone יש גם מתודה שעוברת וולידציה באמצעות ה-DomainValidator שלנו שבודק קידומות.

image_thumb41

נביט על ה-XML שקיבלנו מהעורך הגרפי בקובץ הקונפגיורציה BL.config:

<type defaultRuleset="default" assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

  name="BL.CellPhone">

  <ruleset name="default">

      <properties>

          <property name="PhoneNumber">

              <validator pattern="^05[0246]([-]{0,1})\d{7,8}$" options="None"

                patternResourceName="" patternResourceType="" messageTemplate="Must be a valid cell phone number"

                messageTemplateResourceName="" messageTemplateResourceType=""

                tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RegexValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                name="Regex Validator" />

          </property>

      </properties>

      <methods>

          <method name="ExctractCellPhoneAreaCode">

              <validator negated="false" messageTemplate="Not a valid cell phone area code"

                messageTemplateResourceName="" messageTemplateResourceType=""

                tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.DomainValidator`1[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                name="Domain Validator">

                  <domain>

                      <add name="0522" />

                      <add name="0523" />

                      <add name="0524" />

                      <add name="0524" />

                      <add name="0525" />

                      <add name="0528" />

                      <add name="0505" />

                      <add name="0506" />

                      <add name="0507" />

                      <add name="0508" />

                      <add name="0544" />

                      <add name="0545" />

                      <add name="0546" />

                      <add name="0547" />

                      <add name="0577" />

          </domain>

        </validator>

      </method>

    </methods>

  </ruleset>

</type>

הדבר היחידי הבאמת מעניין כאן זה שהקונפיגיורציה של CellPhone מכילה וולידציה על מתודה ומולנו אפשר לראות שיש תגית <methods> מיוחדת לזה ובתוכה מקוננות תגיות <method>, ובתוכן יש וולידציה כרגיל.

נוסיף את ההגדרה של ObjectValidator שיושב על VisitRating.

image_thumb42

אין שום דבר מעניין לפרט במאפיינים של ObjectValidator.
בקונפיוגרציה של VisitRating יש רק הנחייה כללית לבצע וולידציה על CellPhone לפי הכללים של הטיפוס CellPhone.

    <type defaultRuleset="default" assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

      name="BL.VisitRating">

      <ruleset name="default">

        <properties>

          ...

          <property name="CellPhone">

            <validator targetRuleset="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.ObjectValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Object Validator" />

          </property>

        </properties>

      </ruleset>

    </type>

 

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

יש לנו שלוש מחלקות:

1. FirstClass שבתוכה יש SecondClass

    public class FirstClass

    {

        private SecondClass _second = new SecondClass();

 

        public SecondClass Second

        {

            get { return _second; }

            set { _second = value; }

        }

    }

2. SecondClass שבתוכה יש ThirdClass

    public class SecondClass

    {

        private ThirdClass _third = new ThirdClass();

 

        public ThirdClass Third

        {

            get { return _third; }

            set { _third = value; }

        }

    }

3. ThirdClass עם מחרוזת שהיא לכל היותר שלושה אותיות.

    public class ThirdClass

    {

        private string _maxThreeLetterString;

 

        public string MaxThreeLetterString

        {

            get { return _maxThreeLetterString; }

            set { _maxThreeLetterString = value; }

        }

    }

נראה את זה בדיאגרמה:

image_thumb43

נכתוב קוד שקובע ערך ל-MaxThreeLetterString.

            FirstClass firstClassWithValidString = new FirstClass();

            firstClassWithValidString.Second.Third.MaxThreeLetterString = "abc";

כאן, הלכנו דרך FirstClass.SecondClass.ThirdClass והגענו ל-MaxThreeLetterString.

עכשיו נראה למה צריך וולידציה.

            FirstClass firstClassWithNonValidString = new FirstClass();

            firstClassWithNonValidString.Second.Third.MaxThreeLetterString = "hello world";

נצטרך איכשהו לאכוף את זה ש-MaxThreeLetterString הוא אכן לכל היותר 3 אותיות.
אנחנו כבר מתורגלים ב-VAB אז נוסיף StringLengthValidator ל-MaxThreeLetterString.

    public class FirstClass

    {

        private SecondClass _second = new SecondClass();

 

        public SecondClass Second

        {

            get { return _second; }

            set { _second = value; }

        }

    }

 

    public class SecondClass

    {

        private ThirdClass _third = new ThirdClass();

 

        public ThirdClass Third

        {

            get { return _third; }

            set { _third = value; }

        }

    }

 

    public class ThirdClass

    {

        private string _maxThreeLetterString;

        [StringLengthValidator(3)]

        public string MaxThreeLetterString

        {

            get { return _maxThreeLetterString; }

            set { _maxThreeLetterString = value; }

        }

    }

נריץ את הקוד הבא שמאתחל את המחלקה FirstClass הלא וולידית, שולח אותה לוולידציה ומדפיס אם היא וולידית או לא.

            FirstClass firstClassWithNonValidString = new FirstClass();

            firstClassWithNonValidString.Second.Third.MaxThreeLetterString = "hello world";

 

            ValidationResults results = Validation.Validate(firstClassWithNonValidString);

            Console.WriteLine(results.IsValid);

והתוצאה - ברור שהיא בסדר גמור.

image_thumb46

אז מה עם הוספנו StringLengthValidator על MaxThreeLetterString?
לכל וולידציה שתתבצע על FirstClass אין שום דרך או סיבה להכיר את הוולידציות של ThirdClass.

נצטרך להגיד לוולידציות של FirstClass לכלול את הוולידציות של SecondClass שבתוכה, ולוולידציות של SecondClass לכלול את הוולידציות של ThirdClass שבתוכה.

    public class FirstClass

    {

        private SecondClass _second = new SecondClass();

        [ObjectValidator()]

        public SecondClass Second

        {

            get { return _second; }

            set { _second = value; }

        }

    }

 

    public class SecondClass

    {

        private ThirdClass _third = new ThirdClass();

        [ObjectValidator()]

        public ThirdClass Third

        {

            get { return _third; }

            set { _third = value; }

        }

    }

 

    public class ThirdClass

    {

        private string _maxThreeLetterString;

        [StringLengthValidator(3)]

        public string MaxThreeLetterString

        {

            get { return _maxThreeLetterString; }

            set { _maxThreeLetterString = value; }

        }

    }

ורק עכשיו כאשר נריץ את הקוד הבא, נראה שהוא אכן לא וולידי:

            FirstClass firstClassWithNonValidString = new FirstClass();

            firstClassWithNonValidString.Second.Third.MaxThreeLetterString = "hello world";

 

            ValidationResults results = Validation.Validate(firstClassWithNonValidString);

            Console.WriteLine(results.IsValid);

image_thumb51

למעשה, הוולידציה של המחלקות גולשת ממחלקה למחלקה ומכילה בתוכה את הלוגיקה של אותה מחלקה.

image_thumb43

החצים הם לא רק קשרי אסוצאיציה, אלא הם גם הכיוון אליו תגלוש הוולידציה שלנו.

 

Validation Application Block with Strippers: DomainValidator (Overview of all VAB Validators)

image

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, פורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, פורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, פורסם ב-22.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, פורסם ב-24.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , פורסם ב-26.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, פורסם ב-29.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-31.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-2.7.2007

 

DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?

הוחלט במועדון החשפנות שלנו שצריך למטרות Data Mining לקחת את סוג המשקה האלכוהולי האהוב על המבקרים בפאב.
התקנו שרת Business Intelligne עם SQL Server Integration Services וישר מתחת לבמה עם המוט שמנו אותו, ככה שכל מה שחסר זה באמת נתונים.

image37

נוסיף את המאפיין הרלוונטי לישות העסקית שלנו.

        private string _favoriteAlcoholicBeverage;

 

        public string FavoriteAlcoholicBeverage

        {

            get { return _favoriteAlcoholicBeverage; }

            set { _favoriteAlcoholicBeverage = value; }

        }

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

נוסיף על המשקה האלכוהולי המועדף של המבקר במועדון את ה-DomainValidator.

image_thumb19

אפשר לראות בקונסטרקטור שצריך לשלוח לו מה ה-Domain (או בעברית: "קבוצה") שיהיה וולידי לערך של המאפיין הזה.
במקרה שלנו, אמרנו כבר מה הערכים הוולידים אז נשלח אותם ל-DomainValidator.

        private string _favoriteAlcoholicBeverage;

        [DomainValidator("Beer", "Whiskey", "Taquila", "Vodka",

          MessageTemplate="Please select either: Beer, Whiskey, Taquila or Vodka")]

        public string FavoriteAlcoholicBeverage

        {

            get { return _favoriteAlcoholicBeverage; }

            set { _favoriteAlcoholicBeverage = value; }

        }

image

נראה איך היינו מוסיפים את ה-DomainValidator הזה מכלי הקונפיגיורציה הגרפי:

image_thumb20

image_thumb21

נלחץ כל השלוש נקודות תחת Domain ונפרט ב-Collection Editor הברירת מחדל של Visual Studio אילו אלמנטים נרצה להוסיף ל-Domain שלנו.

image_thumb22

ה-XML שמתווסף לקובץ הקונפיוגרציה BL.config נראה כך:

          <property name="FavoriteAlcoholicBeverage">

            <validator negated="false" messageTemplate="Please select either: Beer, Whiskey, Taquila or Vodka"

              messageTemplateResourceName="" messageTemplateResourceType=""

              tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.DomainValidator`1[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Domain Validator">

              <domain>

                <add name="Beer" />

                <add name="Whiskey" />

                <add name="Taquila" />

                <add name="Vodka" />

              </domain>

            </validator>

          </property>

קל לראות את הפירוט של ה-Domain שלנו תחת אלמנט <domain>.
חדי העין ישימו לב שנוצר בקובץ קונפיגיורציה משהו שנקרא DomainValidator עם כל מיני סימנים מוזרים אחריו.
מה שמסתבר ש-DomainValidator הוא למעשה ג'נארי והעורך הגרפי מייצר כברירת מחדל <DomainValidator<System.Object. כל עוד משתמשים בעורך הגרפי, לא ניתן לשנות את ההגדרה שה-DomainValidator הוא ג'נארי של System.Object.

נראה ב-GUIים שלנו שה-DomainValidator מתנהג כמצופה:

image_thumb23

image_thumb25

נרחיב את הדיון על DomainValidator קצת ונביט על ה-Unit tests שלו.
נתחיל ממש מהבסיס וראה על מה מדובר.

        [TestMethod]

        public void NonNegatedAcceptsStringIncludedInTheDomain()

        {

            DomainValidator<string> validator = new DomainValidator<string>(false, "a", "b", "c" );

 

            ValidationResults results = validator.Validate("b");

 

            Assert.IsTrue(results.IsValid);

היות ואנו כבר כבר מתורגלים ב-Unit tests אנחנו מזהים מה הולך כאן.
אנחנו מאתחלים DomainValidator עם המחרוזות b, c ו-a ובודקים אם a נמצא ביניהן. היות וברור לנו שכן אנחנו רואים ש-VAB יחזיר שהתוצאות וולידציה הן IsValid=true.

        [TestMethod]

        public void NonNegatedRejectsStringNotIncludedInTheDomain()

        {

            DomainValidator<string> validator = new DomainValidator<string>(false, "a", "b", "c" );

 

            ValidationResults results = validator.Validate("d");

 

            Assert.IsFalse(results.IsValid);

        }

כאן אנחנו עושים משהו דומה, גם אתחלנו את אותו וולידטור, אבל בדקנו אם d נמצא ב- b, c ו-a. וכפי שראינו התשובה היא IsValid=false.

נראה עוד Unit test דומה:

        [TestMethod]

        public void NegatedAcceptsStringNotIncludedInTheDomain()

        {

            DomainValidator<string> validator = new DomainValidator<string>(true, "a", "b", "c" );

 

            ValidationResults results = validator.Validate("d");

 

            Assert.IsTrue(results.IsValid);

        }

במקום לאתחל DomainValidator עם Negated=false, איתחלנו אותו עם Negated=true.
ולכן ה-DomainValidator שלנו עכשיו לא מחפש "מי חלק מהקבוצה b, c ו-a" אלא "מי לא חלק מהקבוצה b, c ו-a".

וכפי שציפינו, היות ו-d הוא אכן לא חלק מהקבוצה, קיבלנו ש-IsValid=true.

בואו נדבר קצת על הקטע עם ה-Generics שראינו קודם לכן בקובץ קונפיוגרציה.
ה-DomainValidator שמפרטים באמצעות Attributes הוא למעשה <DomainValidator<object.
אנחנו יכולים באמצעות שינוי קבצי קונפיגיורציה לקבוע DomainValidator של סוג אובייקט אחר, למשל System.Int32. או כל סוג מחלקה אחר.

נראה Unit tests שמדגימים את זה:

        [TestMethod]

        public void NonNegatedAcceptsIntIncludedInTheDomain()

        {

            DomainValidator<int> validator = new DomainValidator<int>(false, 1, 2, 3 );

 

            ValidationResults results = validator.Validate(2);

 

            Assert.IsTrue(results.IsValid);

        }

 

        [TestMethod]

        public void NonNegatedRejectsIntNotIncludedInTheDomain()

        {

            DomainValidator<int> validator = new DomainValidator<int>(false, 1, 2, 3 );

 

            ValidationResults results = validator.Validate(4);

 

            Assert.IsFalse(results.IsValid);

        }

בשני ה-Unit Tests אנחנו מתאחלים DomainValidator של 3, 2 ו-1.
ב-Unit test הראשון בדקנו אם 2 חלק מהקבוצה ובהתאם קיבלנו ש-IsValid=true.
ב-Unit test השני בדקנו אם 4 חלק מהקבוצה ובהתאם קיבלנו ש-IsValid=false.

Unit test אחרון ששווה לציץ עליו הוא הטיפול ב-null.

        [TestMethod]

        public void NonNegatedRejectsNull()

        {

            DomainValidator<object> validator = new DomainValidator<object>(false);

 

            ValidationResults results = validator.Validate(null);

 

            Assert.IsFalse(results.IsValid);

        }

ואפשר לראות שכברירת מחדל null הוא לא ערך וולידי ל-DomainValidator.

 

Validation Application Block with Strippers: RegexValidator (Overview of all VAB Validators)

image

 

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, פורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, פורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, פורסם ב-22.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, פורסם ב-24.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , פורסם ב-26.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, יפורסם ב-29.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-31.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-2.7.2007

 

RegexValidator - בודקים את הדוא"ל של המבקר

אמרנו הרי שהמבקר במועדון מקבל Lap dance חינם ביום-הולדת שלו, אז חייבים דרך ליצור איתו קשר!
ודרך לשלוח לו ניוזלטר תקופתי, וניוזלטר לא תקופתי והצעה לתרופות ושעונים מזוייפים.
בשביל זה נצטרך את הכתובת דוא"ל שלו.

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

        private string _email;

 

        public string EMail

        {

            get { return _email; }

            set { _email = value; }

        }

מה הבעיה עם כתובות דוא"ל? זה כבר לא חוקיות פשוטה של "מספר בין 1 ל-10" או "מחרוזת עם יותר מחמישה תווים".

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

בואו נראה מה החוקיות של כתובת דוא"ל:
1. בחלק הראשון (לפני סימן הכרוכית, @) מכילה לכל הפחות סימן אלפא-נומרי אחד (כולל הסימן נקודה . והסימן קו תחתון _)
2. באמצע יש את סימן הכרורכית - @  
3.  צריך שם דומיין שזה אומר לפחות סימן אלפא-נומרי אחד
4. נקודה . לפני סיומת דומיין.
5. סיומת לדומיין היא בין 2 ל-4 סימנים אלפא נומריים (com, net, info, uk וכך הלאה).

בואו ננסח את זה כ-Regex.
מי שזמנו יקר לו (כפי שזמני יקר לי ואני משוכנע שזמנכם יקר לכם) לא כותב Regex בפנקס רשימות.
לביטויי Regex יש סביבות פיתוח משלהן.
במאמר זה נשתמש בתוכנה The Regulator של רועי אושרוב שהיא הסטנדרט דה-פקטו לכתיבת ובדיקת Regex בדוט נט.
http://tools.osherove.com/CoolTools/Regulator/tabid/185/Default.aspx

נתרגם את הדרישות הטכניות שכתבנו לכתובת דוא"ל לביטוי Regex.

Regex מתחיל עם הסימן ^ ומסתיים עם הסימן $ כאשר אנחנו רוצים שכל הביטוי שהוזן יעבור וולידציה ולא רק חלקים ממנו.

image_thumb1

אמרנו שאנחנו רוצים שבאמצע יהיה סימן הכרוכית @ אז נכתוב אותו.

image_thumb2

נרצה שלפני סימן הכרוכית יהיה רצף של אותיות, מספרים, הסימן נקודה . והסימן קו תחתון _.
ב-Regex הסימן w\ מסמן רצף אלפא-נומרי והסימן קו תחתון _, אבל לא הסימן נקודה.
את הסימן נקודה נסמן באמצעות .\ (סלאש ונקודה).

image_thumb3

את העובדה שמדובר ברצף חוזר על עצמו נסמן עם בכך שנכתוב את שני הביטויים בתוך סוגריים מרובעות [ ].

image_thumb4

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

image_thumb5

אחרי סימן הכרוכית @ נרצה שיופיע כל רצף אלפא נומרי עם הסימנים נקודה וקו תחתון.

image_thumb6

הרצף הזה צריך לחזור על עצמו גם לפחות פעם אחת.

image_thumb7

עכשיו נרצה להוסיף מקום עבור סיומת הדומיין com. או net. וכך הלאה. צריך נקודה אחת לפחות בכל הביטוי הזה.
נשתמש בסימן .\ שהכרנו כדי לסמל נקודה ונציין אחריה באמצעות {1} שמדובר בדיוק במופע אחד בדיוק של נקודה.

image_thumb8

בתור הסיומת עצמה מותר כל סימן אלפא נומרי בין 2 ל-4 אותיות.
את הסימן w\ אנחנו מכירים, ונשתמש ב-{2,4} כדי לסמן שמדובר בין 2 ל-4 סימן.

image_thumb9

וזהו.
נבדוק בתוך The Regulator את הביטויים הבאים שנצפה שיהיה בסדר: J@JustinAngel.Net, Israel@Israel.co.il
ונבדוק גם את הביטויים שלא אמורים לעבור: @, a@a, @a.com

image_thumb10

כתבנו את הביטוי הרגולרי שלנו בתוך החלון המרכזי.
בצד הימני התחתון תחת Input כתבנו את הקלטים שאנחנו רוצים לבדוק.
לחצנו על כפתור Match (מודגש בקו אדום) ונקבל בחלק השמאלי התחתון את כל המחרוזות שהתאימו ל-Regexp שלנו.

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

נוסיף למאפיין דוא"ל שלנו RegexValidator.

image_thumb11

ונראה כי נוכל לכתוב ישירות לתוכו את ה-Regex שלנו.

        private string _email;

        [RegexValidator(@"^[\w\.]+@[\w\.]+\.{1}(\w{2,4})$",

                        MessageTemplate="Email is not valid")]

        public string EMail

        {

            get { return _email; }

            set { _email = value; }

        }

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

נראה איך היינו מוסיפים את הוולידטור הזה דרך עורך הקונפיוגרציה הגרפי.

image_thumb12

image_thumb13

ומולנו ה-XML שיצר כלי הקונפגיורציה הגרפי בתוך קובץ BL.config:

          <property name="EMail">

            <validator pattern="^[\w\.]+@[\w\.]+\.{1}(\w{2,4})$" options="None"

              patternResourceName="" patternResourceType="" messageTemplate="Email is not valid"

              messageTemplateResourceName="" messageTemplateResourceType=""

              tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RegexValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Regex Validator" />

          </property>

אגב, שמתם לב לשלוש נקודות מצד ימין למאפיין Pattern? אם נלחץ עליו נקבל את התיבת עריכה ברירת המחדל של VS2005 ל-Regex.
כאן נקבל מספר אפשרויות ל-Regexים מוכנים שנשלחים עם VS2005.

image_thumb14

נראה באמת שה-GUIים שלנו מקיימים את החוקיות שהכתבנו.

image_thumb15

image38

 

חשוב לשים Wלב לכוח העצום מאחורי Regex היות ואנחנו יכולים להביע באמצועתם כל לוגיקה הקשורה לתוכן של מחרוזת.

נראה עוד כמה ביטויי Regex לדוגמה מתוך האתר RegexLib.com.

Regex תיאור

^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$

בודק כתובות IP
1.1.1.1
255.255.255.255
<script[^>]*>[\w|\t|\r|\W]*</script> מחפש תגיות <script> ב-HTML
^\{?[a-fA-F\d]{8}-([a-fA-F\d]{4}-){3}[a-fA-F\d]{12}\}?$ תואם ל-GUIDים.
e02ff0e4-00ad-090A-c030-0d00a0008ba0
{e02ff0e4-00ad-090A-c030-0d00a0008ba0}
^0(5[012345678]|6[47]){1}(\-)?[^0\D]{1}\d{5}$ אימות מספרי פלאפון בישראל.
0520-346634
0528633633
064-2228226
^(\+[1-9][0-9]*(\([0-9]*\)|-[0-9]*-))?[0]?[1-9][0-9\- ]*$ מספרי טלפון בין לאומיים.
+123(45)678-910
+123-045-67 89 10
01-234-56-78

הערכה חשובה, להדוגמה שנתנו כאן לאימות דוא"ל היא לפי סט כללים מעניין שמספיק לרוב סוגי כתובות הדוא"ל, אבל לא לכולם. ישנו תקן RFC 2822 לכתובות דוא"ל וניתן למצוא מימוש שלו ב-Regex בכתובת הבאה - http://www.regular-expressions.info/email.html.

יש עוד המון דוגמאות ושימושים אפשריים ל-Regex, מה שחשוב לזכור זה שלא צריך לפחד מלהשתמש ב-Regex שצריך את העוצמה שלו.

 

Validation Application Block with Strippers: PropertyComparisonValidator (Overview of all VAB Validators)

image

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, פורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, פורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, פורסם ב-22.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, פורסם ב-24.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , יפורסם ב-25.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, יפורסם ב-26.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-29.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-30.7.2007

 

 

PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון

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

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

נוסיף PropertyComparisonValidator ל-BirthDate.

image_thumb63_thumb1

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

נפרט ל-PropertyComparisonValidator שאנחנו רוצים להשוות את התאריך לידה לתאריך ביקור

        private DateTime _birthDate;

        [RelativeDateTimeValidator(0, DateTimeUnit.None, RangeBoundaryType.Ignore,

                                   -18, DateTimeUnit.Year, RangeBoundaryType.Inclusive,

                                   MessageTemplate = "You must be 18 years old or older")]

        [PropertyComparisonValidator("VisitDate")]

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

עכשיו נצטרך לפרט מה היחס שנרצה בין BirthDate ל-VisitDate ואת היחס הזה נפרט באמצעות ComparisonOpearator.

image_thumb64_thumb1

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

        private DateTime _birthDate;

        [RelativeDateTimeValidator(0, DateTimeUnit.None, RangeBoundaryType.Ignore,

                                   -18, DateTimeUnit.Year, RangeBoundaryType.Inclusive,

                                   MessageTemplate = "You must be 18 years old or older")]

        [PropertyComparisonValidator("VisitDate", ComparisonOperator.LessThan,

                                    MessageTemplate="Ahmm.. Ahmm..")]

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

נראה איך היינו מגדירים את הוולידטור דרך כלי הקונפיוגרציה הגרפי.

image_thumb711_thumb1

image_thumb72_thumb1

נראה את ה-XML שהעריכה הגרפית יוצרת בקובץ BL.config:

          <property name="BirthDate">

            <validator lowerUnit="None" upperUnit="Year" lowerBound="0" lowerBoundType="Ignore"

              upperBound="-18" upperBoundType="Inclusive" negated="false"

              messageTemplate="You must be 18 years old or older" messageTemplateResourceName=""

              messageTemplateResourceType="" tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RelativeDateTimeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Relative Date Time Validator" />

            <validator operator="LessThan" propertyToCompare="VisitDate"

              negated="false" messageTemplate="" messageTemplateResourceName=""

              messageTemplateResourceType="" tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.PropertyComparisonValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Property Comparison Validator" />

          </property>

שימו לב, יש לנו אלמנט <property> אחד עם כמה אלמנטי <validator> שנצטרך.

נבדוק שבאמת מתבצעת וולידציה ב-GUI:

image_thumb66_thumb1

image_thumb70_thumb1

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

בנוסף לקוד הוולידציה הקיים של תאריך לידה :

        private DateTime _birthDate;

        [RelativeDateTimeValidator(0, DateTimeUnit.None, RangeBoundaryType.Ignore,

                                   -18, DateTimeUnit.Year, RangeBoundaryType.Inclusive,

                                   MessageTemplate = "You must be 18 years old or older")]

        [PropertyComparisonValidator("VisitDate", ComparisonOperator.LessThan,

                                    MessageTemplate="Ahmm.. Ahmm..")]

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

נוכל להוסיף קוד וולידציה על תאריך ביקור:

        private DateTime _visitDate;

        [DateTimeRangeValidator("2010-01-01T00:00:00",

                               MessageTemplate = "Visit date has to be before 1/1/2010")]

        [PropertyComparisonValidator("BirthDate", ComparisonOperator.GreaterThan,

                            MessageTemplate = "Ahmm.. Ahmm..")]

        public DateTime VisitDate

        {

            get { return _visitDate; }

            set { _visitDate = value; }

        }

נוכל לבחור לאיזה מהמאפיינים נרצה להוסיף את ה-PropertyComparisonValidator או אפילו לשניהם.

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

Validation Application Block with Strippers: RelativeDateTimeValidator (Overview of all VAB Validators)

image

 

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, פורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, פורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, פורסם ב-22.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, יפורסם ב-23.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , יפורסם ב-24.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, יפורסם ב-25.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-26.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-29.7.2007

 

 

RelativeDateTimeValidator - מתי היום-הולדת שלך?

כחלק מהמדיניות של המועדון חשפנות שלנו החלטנו לתת ללקוחות תמריץ להגיד איך היה ה-Lap dance שלהם.
החלטנו שבתאריך היום-הולדת שלהם ניתן להם Lap dance מתנה אם הם יגיעו למועדון!

 

   אז בהתאם נוסיף לאפליקציה שלנו BirthDate:

        private DateTime _birthDate;

 

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

אבל אז הגיעו עורכי הדין ואמרו לבעל המועדון "תקשיב, אתה לא יכול לתת Lap dances במוצהר לילדים שלא מלאו להם 18 שנים".
מדיניות מקובלת והגיונית, ועכשיו נצטרך ליישם אותה בקוד שלנו.

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

בדיוק בשביל בדיקות של תאריך ביחס לתאריך הנוכחי קיים הוולידטור RelativeDateTimeValidator.

image_thumb40_thumb1

אנחנו כבר מתורגלים ב-RangeValidators למיניהם ולכן אנחנו יודעים שנצטרך להשתמש ב-RangeBoundryType.Ignore על הגבול העליון (היות ובאי המועדון יכולים להיות 18+). אז נאלץ להשתמש בקונסטרקטור הראשון שבו נפרט גבול עליון של 18- שנה (כולל) וגבול תחתון שממנו נתעלם.

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

נתחיל בלהוסיף את הגבול התחתון הריק.

        private DateTime _birthDate;

        [RelativeDateTimeValidator(0, DateTimeUnit.None, RangeBoundaryType.Ignore,

                                    )]

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

נכתוב את המספר 18 של "18 שנה".

        private DateTime _birthDate;

        [RelativeDateTimeValidator(0, DateTimeUnit.None, RangeBoundaryType.Ignore,

                                   18)]

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

אסור לנו לשכוח שהגבול העליון שלנו הוא "לפני 18 שנה" ולכן 18 הופך למינוס 18.

        private DateTime _birthDate;

        [RelativeDateTimeValidator(0, DateTimeUnit.None, RangeBoundaryType.Ignore,

                                   -18)]

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

עכשיו נצטרך להוסיף DateTimeUnit שמסביר למה ה-18 הזה מתייחס.

image_thumb43_thumb1

 נפרט שאנו מעוניינים כי הגבול העליון יהיה לפני 18 שנה (כולל) ועם גבול עליון ריק.

        private DateTime _birthDate;

        [RelativeDateTimeValidator(18, DateTimeUnit.Year, RangeBoundaryType.Inclusive,

                                   0, DateTimeUnit.None, RangeBoundaryType.Ignore,

                                   MessageTemplate="You must be 18 years old or older")]

        public DateTime BirthDate

        {

            get { return _birthDate; }

            set { _birthDate = value; }

        }

ה-RelativeDateTimeValidator מקבל גבול עליון וגבול תחתון ומאמת אותו ביחס לתאריך הנוכחי.

נראה איך היינו מגדירים את הוולידטור בכלי קונפיוגרציה הגרפי.

image_thumb58_thumb1

image_thumb59_thumb1

והנה ה-XML שקיבלנו בקובץ הקונפיוגרציה BL.config:

          <property name="BirthDate">

            <validator lowerUnit="None" upperUnit="Year" lowerBound="0" lowerBoundType="Ignore"

              upperBound="-18" upperBoundType="Inclusive" negated="false"

              messageTemplate="You must be 18 years old or older" messageTemplateResourceName=""

              messageTemplateResourceType="" tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RelativeDateTimeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Relative Date Time Validator" />

          </property>

נראה באמת שב-GUIים שלנו נוכל להכניס תאריכים שקדמו לתאריך הנוכחי ב-18 שנה, אבל לא תאריכים אחרי לאחר מכן.

image_thumb61_thumb1

image_thumb62_thumb1

 

נראה דוגמאות נוספת מתוך ה-Unit tests על שימוש ב-RelativeDateTimeValidator.

        [TestMethod]

        public void NonNegatedSuccessForDateTimeBetweenLowerAndUpper()

        {

            RelativeDateTimeValidator validator =

                new RelativeDateTimeValidator(-5, DateTimeUnit.Day, RangeBoundaryType.Inclusive,

                                              3, DateTimeUnit.Day, RangeBoundaryType.Inclusive, false);

 

            ValidationResults results = validator.Validate(DateTime.Now.AddDays(2));

 

            Assert.IsTrue(results.IsValid);

        }

 

        [TestMethod]

        public void NonNegatedFailureForDateTimeGreaterThenUpperBound()

        {

            RelativeDateTimeValidator validator =

                new RelativeDateTimeValidator(-5, DateTimeUnit.Day, RangeBoundaryType.Inclusive,

                                              3, DateTimeUnit.Day, RangeBoundaryType.Inclusive, false);

 

            ValidationResults results = validator.Validate(DateTime.Now.AddDays(8));

 

            Assert.IsFalse(results.IsValid);

        }

בשני ה-Unit tests אנו מאתחלים RelativeDateTimeValidator על תקופה מלפני חמישה (כולל) ועד תקופה עוד שלושה ימים (כולל).

            RelativeDateTimeValidator validator =

                new RelativeDateTimeValidator(-5, DateTimeUnit.Day, RangeBoundaryType.Inclusive,

                                              3, DateTimeUnit.Day, RangeBoundaryType.Inclusive, false);

ב-Unit Test הראשון אנו בודקים אם "עוד יומיים" נמצא בטווח שהגדרנו.

            ValidationResults results = validator.Validate(DateTime.Now.AddDays(2));

והוא אכן נמצא בטווח הזה היות ואנו רואים כי IsValid=true.

            Assert.IsTrue(results.IsValid);

ב-Unit Test השני אנו בודקים אם "עוד שמונה ימים" נמצא בטווח שהגדרנו.

            ValidationResults results = validator.Validate(DateTime.Now.AddDays(8));

ואנו רואים כי הוא לא נמצא היות ו-IsValid=false.

            Assert.IsFalse(results.IsValid);

 
חשוב לשים לב להבדל בין DateTimeRangeValidator שבודק בין שתי תאריכים קבועים שנרשמו בפירוט של הוולידטור,
לבין RelativeDateTimeValidator שבודק בין שתי תאריכים ששניהם יחסים לתאריך הנוכחי.
 

Question from Tapuz .Net forum: Control Extenders in Microsoft AJAX Control toolkit with PagingBulletedList

שאלה:

אני צריך  להציג מידע ברשימה ממויינת אפלבתית.
בשביל זה בחרתי לעבוד עם PagingBulltedList של AJAX Control Toolkit.

איך אפשר לגרום לרשימה להציג את הסימן "#" שעל פיו יסנן את כל המספרים?

איך אפשר לקבל הודעה שהמשתמש בוחר אינדקס מסויים ולדעת איזה אינדקס נבחר?

 להדגמה חיה ניתן ללחוץ כאן - http://ajax.asp.net/ajaxtoolkit/PagingBulletedList/PagingBulletedList.aspx.

 

תשובה:

 השאלה הזו נוגעת לבדיוק כיצד ראוי לעבוד עם AJAX Control Toolkit כאשר צריך להרחיב אותו ולשנות את ההתנהגות הקיימת.

בואו נתחיל בלהכיר את ה-PagingBulletedList.
מדובר ב-AJAX Control Extender, כלומר זה פקד שמתלבש על פקד קיים.

במקרה שלנו ה-PagingBulletedList מתלבש על <asp:BulletedList> שזה פקד של ASP.Net 2.0 שמתרנדר לרשימה של <ul> ו-<li> ב-HTML. למשל, הפקד הבא:

    <asp:BulletedList ID="BulletedList1" runat="server" DisplayMode="Text">

                <asp:ListItem>writeln</asp:ListItem>

                <asp:ListItem>1566</asp:ListItem>

                <asp:ListItem>4556</asp:ListItem>

                <asp:ListItem>9556776</asp:ListItem>

                <asp:ListItem>12234</asp:ListItem>

                <asp:ListItem>1566</asp:ListItem>

                <asp:ListItem>abort</asp:ListItem>

                <asp:ListItem>add</asp:ListItem>

                <asp:ListItem>addBehavior</asp:ListItem>

                <asp:ListItem>AddChannel</asp:ListItem>

                <asp:ListItem>AddDesktopComponent</asp:ListItem>

                <asp:ListItem>addElement</asp:ListItem>

                <asp:ListItem>AddFavorite</asp:ListItem>

                <asp:ListItem>addImport</asp:ListItem>

                <asp:ListItem>addPageRule</asp:ListItem>

                <asp:ListItem>addReadRequest</asp:ListItem>

                <asp:ListItem>addRule</asp:ListItem>

                <asp:ListItem>AddSearchProvider</asp:ListItem>

                <asp:ListItem>alert</asp:ListItem>

                <asp:ListItem>appendChild</asp:ListItem>

                <asp:ListItem>appendData</asp:ListItem>

                <asp:ListItem>applyElement</asp:ListItem>

                <asp:ListItem>assign</asp:ListItem>

                <asp:ListItem>attachEvent</asp:ListItem>

                <asp:ListItem>AutoCompleteSaveForm</asp:ListItem>

         

                ...

            </asp:BulletedList>

יתרנדר לתצוגה הזו בצד לקוח בדפדפן:

image

עכשיו נרצה איכשהו למיין את הרשימה הקיימת הזו שלנו, שרק לפי האות הראשונה (או X אותיות ראשונות) נציג אותן ביחד.
כלומר, נוכל לראות ביחד רק את כל המילים שמתחילות ב-a, או כל המילים שמתחילות ב-b וכך הלאה.

בשביל מטרה זו יש את PagingBulltedList של AJAX Control Toolkit ש"מתלבש" על BulltedList רגיל ומאפשר דפדפוף בו לפי אינדקס.
למשל הפקד הבא:

            <ajaxToolkit:PagingBulletedListExtender runat="server"

                ID="PagingBulletedListExtender1"

                BehaviorID="PagingBulletedListBehavior1"

                TargetControlID="BulletedList1"

                ClientSort="true"

                IndexSize="1"

                Separator=" - " />

יגרום ל-BulltedList להיות מוצגת בצד לקוח כך: (לאחר שנלחץ על E)

image

 

 לחיצה על האות O למשל תגרום לסינון חדש:

image

 

 להדגמה חיה ניתן ללחוץ כאן - http://ajax.asp.net/ajaxtoolkit/PagingBulletedList/PagingBulletedList.aspx.

 

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

חשוב להבין שה-AJAX Control toolkit זה לא סתם עוד אוסף של ג'אווה סקריפטים.
מדובר במחלקות ופקדים צד-לקוח שכתובים בג'וואה סקריפט, אבל הם עדיין פקדים ומחלקות.

הפקדים האלו חושפים אירועים (Events), חושפים מאפיינים (Properties), חושפים קונסטרקטור (Constructor), חושפים מתודות (Methods) וכך הלאה.
רק במקרה זה כתוב בג'אווה סקריפט וכפוף למודל הכתיבה של Microsoft AJAX.

בואו נביט לתוך הקוד מקור של PagingBulltedList ונראה את זה.
נוריד מ-codeplex (אתר הפרוייקטים של מיקרוסופט) את הקוד העדכני - http://www.codeplex.com/AtlasControlToolkit/Release/ProjectReleases.aspx?ReleaseId=4923.
ובתוך התיקייה \AjaxControlToolkit\PagingBulletedList נוכל למצוא את קוד המקור מאחורי PagingBulletedList.

מאפיינים אמרנו שיש? בואו נראה אחד.

 AjaxControlToolkit.PagingBulletedListBehavior.prototype = {

 {

    ...

   this._heightValue = null;

 

    get_Height : function() {

        /// <value type="Number" integer="true">

        /// Height of the bulleted list

        /// </value>

        return this._heightValue;

    },

    set_Height : function(value) {

        if (this._heightValue != value) {

            this._heightValue = value;

            if (this.get_isInitialized()) {

                //Change Height in the DOM

                this._changeHeightDivContent();

            }

            this.raisePropertyChanged('Height');

        }

    },

    ...

}

תזכרו, כל זה Javascript.
אפשר לראות שיש שדה פנימי heightValue_, ויש שני מתודות שחושפות Getter ו-Setter למאפיין.
get_Height מחזיר את הגובה הנוכחי של האלמנט, ו-set_Height מעדכן את השדה הפנימי ודואג לשנות את הגובה בפועל של האלמנט.

אמרנו שיש קונסטרקטור?

AjaxControlToolkit.PagingBulletedListBehavior.prototype = {

    initialize : function() {

        /// <summary>

        /// Initialize the behavior

        /// </summary>

        AjaxControlToolkit.PagingBulletedListBehavior.callBaseMethod(this, 'initialize');

 

        // do alot of stuff

    },

    ...

}

אמרנו שיש אירועים?

    add_indexChanged : function(handler) {

        /// <summary>

        /// Add a handler to the indexChanged event

        /// </summary>

        /// <param name="handler" type="Function">

        /// Handler

        /// </param>

        this.get_events().addHandler('indexChanged', handler);

    },

    remove_indexChanged : function(handler) {

        /// <summary>

        /// Remove a handler from the indexChanged event

        /// </summary>

        /// <param name="handler" type="Function">

        /// Handler

        /// </param>

        this.get_events().removeHandler('indexChanged', handler);

    },

    raiseIndexChanged : function(eventArgs) {

        /// <summary>

        /// Raise the indexChanged event

        /// </summary>

        /// <param name="eventArgs" type="Sys.EventArgs">

        /// Event Arguments

        /// </param>

        var handler = this.get_events().getHandler('indexChanged');

        if (handler) {

            if (!eventArgs) {

                eventArgs = Sys.EventArgs.Empty;

            }

            handler(this, eventArgs);

        }

    },

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

עכשיו נראה איך מתממשקים למשל לאירוע של שינוי אינדקס.

בדף לדוגמה שמגיע עם Control Toolkit אפשר לראות את הקוד ג'אווה סקריפט:

    <script language="javascript" type="text/javascript">

        var bl;

 

        function pageLoad() {

            bl = $find('PagingBulletedListBehavior1');

        }

    </script>

זה הסינטקס להשיג את המופע צד לקוח של ה-PagingBulltedList שלנו לפי ID.

עכשיו נרצה לקבוע ולהדפיס למסך את הגובה של ה-Extender:

       function pageLoad() {

            bl = $find('PagingBulletedListBehavior1');

            bl.set_Height(250);

            alert(bl.get_Height());

            }

על המסך נראה:

image

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

עכשיו נירשם לאירוע שמדבר על שינוי אינדקס בלקוח.

        function onIndexchanged(sender, eventArgs)

        {

            alert(eventArgs.innerHTML + " " + eventArgs.tag);

        }

 

       function pageLoad() {

            bl = $find('PagingBulletedListBehavior1');

        // bl.set_Height(250);

        // alert(bl.get_Height());

            bl.add_indexChanged(onIndexchanged);

        }

אמרנו איזה פונקציה צד לקוח נרצה שתרוץ שהגולש בדף בוחר אינדקס ונדפיס את הפרטים של האלמנט שנלחץ.

image

קיבלנו שלחצנו על האות B באינדקס במקום 4 (היות ואנחנו סופרים מערך מ-0 אז B באמת נמצא במקום 4).

 

השאלה השנייה שקיבלנו היא איך ניתן לשנות את הסידור של ה-PagingBulltedList כך שיציג את הסימן "#" לכל קבוצת המספרים.

נפתח את קובץ הג'אווה סקריפט של PagingBulletedList ואחרי קריאה זריזה שלו נוכל למצוא שבאמצעות הוספה של השורה הבאה בשורה 258 בתוך המתודה _generateIndexAndTabForView נוכל לבצע את זה.

        if (index == "1" || index == "2" || index == "3" || index == "4" || index == "5" || index == "6" || index == "7" || index == "8" || index == "9" || index == "1" || index == "1")

       {

        index = "#";

       }

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

עכשיו השאלה היא איך נוסיף את השינוי הזה לפרוייקט שלנו.

אפשרות אחת היא לשנות את קובץ ה-JS של PagingBulletedListBehavior.js, לקמפל מחדש את Microsoft AJAX Control Toolkit ולהשתמש ב-DLL החדש שלנו.

אפשרות נוספת שהייתי רוצה להדגים היא שינוי בזמן ריצה של המתודה.

       function pageLoad() {

            bl = $find('PagingBulletedListBehavior1');

        // bl.set_Height(250);

        // alert(bl.get_Height());

        // bl.add_indexChanged(onIndexchanged);

            bl._generateIndexAndTabForView = function() {

            ...          

                if (index == "1" || index == "2" || index == "3" || index == "4" || index == "5" || index == "6" || index == "7" || index == "8" || index == "9" || index == "1" || index == "1")

                {

                    index = "#";

                }

            ...

            };

            bl._generateIndexAndTabForView();

        }

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

שנינו בזמן ריצה אצל את הלקוח את הפונקציה!

נראה את התוצאה של הקוד הזה:

image 

ובאמת בצד-לקוח קיבלנו קבוצה שמבוססת על הסימן # שמייצג את כל המספרים!

 

מתי נשנה בזמן ריצה ומתי נדאג לקמפל מחדש את ה-AJAX Control toolkit?

נתחיל בלדבר נגד שינוי בזמן ריצה של ה-AJAx Control Toolkit:
1. שינוי בזמן ריצה יוצר מצב שלא יודעים איזה קוד מריץ מה. כלומר, לא יודעים בכלל מול איזה מתודה רצים באותו רגע.
2. שינוי בזמן ריצה יוצר ערבוב של אחריות בין הפקד צד-לקוח לבין המחלקה שמשתמשת בה שבפועל קובעת מה המחלקה מריצה.
3. מדובר על דרך גרועה לבצע שינויים מאסיביים ב-toolkit.
4. היות ושנינו מתודה פרטית (אנו יודעים זאת כי המתודה ששנינו מתחילה עם קו תחתון _) יכול להיות שבגירסה הבאה היא בכלל לא תהיה קיימת והשינוי שלנו לא יעבוד אם נרצה לשדרג לגירסה הבאה של AJAX Control toolkit.

עכשיו נדבר בעד שינוי בזמן ריצה של ה-AJAX control toolkit:
1.  זה ממש מגניב. שנינו בזמן ריצה מתודה של מחלקה.
2. אם נשנה ישירות את הקובץ JS של ה-toolkit, נקמפל מחדש ונעבוד מול גירסה שלנו של ה-AJAX Control Toolkit כל שדרוג גירסה ל-AJAX Control Toolkit (שבערך פעם בחודש מתווספים לו פקדים) יגרור סט מאוד רציני של בדיקות ושינויים אצלנו. יש טכניקות שמאפשרות להתגבר על זה (לבצע Merge של הקבצים בגירסה הפרטית שלנו לתוך הגירסה הציבורית, אבל מדובר בתהליך ידני שיכול לקחת מספר שעות כל פעם).

 

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

Validation Application Block with Strippers: ContainsCharctersValidator and Negated (Overview of all VAB Validators

image

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, פורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, פורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, יפורסם ב-19.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, יפורסם ב-22.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , יפורסם ב-23.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, יפורסם ב-24.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-25.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-26.7.2007

 

 

ContainsCharctersValidator ו-Negated - איך קוראים לך?

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

        private string _customerName;

 

        public string CustomerName

        {

            get { return _customerName; }

            set { _customerName = value; }

        }

נשתמש ב-ContainsCharcterValidator כדי לבדוק את האותיות בשם הלקוח.

        private string _customerName;

        [ContainsCharactersValidator("אבגדהוזחטיכלמסעפצקרשת",

                               MessageTemplate="Customer name cannot be in hebrew")]

        public string CustomerName

        {

            get { return _customerName; }

            set { _customerName = value; }

        }

נרחיב את ההגדרה לרגע (למרות שהקוד הזה מספיק ורץ כמו טאטאלה) שיכיל ContainsCharcters.

        private string _customerName;

        [ContainsCharactersValidator("אבגדהוזחטיכלמסעפצקרשת", ContainsCharacters.Any,

                               MessageTemplate="Customer name cannot be in hebrew")]

        public string CustomerName

        {

            get { return _customerName; }

            set { _customerName = value; }

        }

המאפיין ContainsCharcters מפנה ל-Enum בעל אחד משתי אפשרויות: ContainsCharcters.Any ו-ContainsCharcters.All.

ContainsCharcters.Any הוא הברירת מחדל ומגדיר שהלוגיקה של הוולידטור הוא לחפש שהשם מכיל לפחות תו אחד מהתווים שסיפקנו.
ContainsCharacters.All משנה את הלוגיקה של הוולידטור שהמחרוזת חייבת להכיל את כל האותיות שסיפקנו.

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

        private string _customerName;

        [ContainsCharactersValidator("אבגדהוזחטיכלמסעפצקרשת", ContainsCharacters.Any,

                                        Negated=true,

                                        MessageTemplate="Customer name cannot be in hebrew")]

        public string CustomerName

        {

            get { return _customerName; }

            set { _customerName = value; }

        }

המאפיין Negated נמצא בכל וולידטור ומאפשר להפוך את הלוגיקה של הוולידטור.
כלומר, אם כאשר Negated=false הוולידציה מחפשת "לפחות אות א"ב אחת בשם הלקוח" עכשיו היא תחפש שלא יהיה "לפחות אות א"ב אחת בשם הלקוח".

נוכל לקבוע לכל וולידטור באשר הוא שהוא Negated ובכך להפוך את הלוגיקה שלו.

נראה איך היינו מוסיפים את הוולידטור דרך כלי הקונפיגיורציה ב-GUI.

image_thumb33_thumb1 

image_thumb34_thumb1

ומולנו ה-XML שמתקבל בקובץ הקונפיוגרציה BL.config:

          <property name="CustomerName">

            <validator characterSet="אבגדהוזחטיכלמסעפצקרשת" containsCharacter="Any"

              negated="true" messageTemplate="Customer name cannot be in hebrew"

              messageTemplateResourceName="" messageTemplateResourceType=""

              tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.ContainsCharactersValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Contains Characters Validator" />

          </property>

נבדוק ב-GUI שבאמת לא ניתן להזין אותיות בעברית.

image_thumb35_thumb1

image_thumb37_thumb1

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

 

נראה דוגמה מתוך ה-Unit tests לשימוש ב-ContainsCharcterValidator עם ContainsCharcters.All שהוא negated=false.

        [TestMethod]

        public void NonNegatedWithAllCharactersAcceptsStringWithAllCharacters()

        {

            ContainsCharactersValidator validator = new ContainsCharactersValidator("abc", ContainsCharacters.All, false);

 

            ValidationResults results = validator.Validate("asdfbc");

 

            Assert.IsTrue(results.IsValid);

        }

בשורה הראשונה יצרנו ContainsCharctersValidator שבודק אם מחרוזת מכילה בתוכה את האותיות b, c ו-a. כלומר, את כל שלושתן.

בשורה הבאה אנחנו בודקים את "asdfbc".

בשורה האחרונה אנחנו רואים ש-IsValid=true. כלומר, אכן "asdfbc" מכיל את שלושת האותיות "abc".

Validation Application Block with Strippers: DateTimeRangeValidator (Overview of all VAB Validators)

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, פורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, יפורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, יפורסם ב-19.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, יפורסם ב-22.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , יפורסם ב-23.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, יפורסם ב-24.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-25.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-26.7.2007

 

 

DateTimeRangeValidator - מתי ביקרת אצלנו?

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

נוסיף מאפיין בשם VisitDate.

        private DateTime _visitDate;

 

        public DateTime VisitDate

        {

            get { return _visitDate; }

            set { _visitDate = value; }

        }

נרצה שלא ניתן יהיה לדווח על תאריך שמאוחר מה-1.1.2010.
נשתמש ב-DateTimeRangeValidator כדי לבצע את הוולידציה.

DateTimeRangeValidator הוא RangeValidator בתחפושת.

image_thumb25_thumb1

כן, גם היא מחופשת.

אם אתם זוכרים RangeValidator מאפשר לנו לפרט טווח תאריכי שבו נוכל לפרט בדיקה.

image_thumb26_thumb1

בואו נראה איך נראה ה-DateTimeRangeValidatorAttribute.

image_thumb27_thumb1

וכאן עומד ההבדל.

ההבדל הוא אחד פשוט ומעניין מאוד - אי-אפשר באמת להשתמש ב-RangeValidator לוולידציה על DateTime.
נניח שהיינו מנסים:

image_thumb28_thumb1

ההסבר הטכני: היות והמאפיינים שנשלחים לקונסטרקטור של Attributes חייבים להיות Constant ואי-אפשר בדוט נט ליצור const DateTime, אין באמת אפשרות לשלוח ל-Attribute בתוך הקונסטרקטור טיפוס מסוג DateTime.

הפירוש הטכני: חייבים לעבוד עם DateTimeRangeValidator אם רוצים לבדוק מאפיין מול ערך תאריכי קבוע כלשהו.

והיות וכרגע אמרנו שאי-אפשר להשתמש ב-DateTime ב-Attributes, נפרט את התאריכים בפורמט כמחרוזת (ובסופו של דבר הם יעברו Parsing ל-DateTime).

        private DateTime _visitDate;

        [DateTimeRangeValidator("2010-01-01T00:00:00",

                                MessageTemplate="Visit date has to be before 1/1/2010")]

        public DateTime VisitDate

        {

            get { return _visitDate; }

            set { _visitDate = value; }

        }

שימו לב ששלחנו מחרוזת לתוך DateTimeRangeValidator כגבול עליון וזו תעבור Parsing ותהפוך ל-DateTime.

ועכשיו נשאלת השאלה - מה זה הפורמט המזעזע הזה שאנחנו נתנו פרמטרים בו? מסתבר שצריך לשלוח ל-DateTimeRangeValidator תאריך ושעה באיזה פורמט מוזר של ISO. התבנית יחסית פשוטה.

Year-Month-Day"T"Hour:Minute:Second

למה דווקא בחרו בפורמט הזה? שאלה נהדרת. כנראה מאוד בגלל שהצורה שלו קבועה ולא משתנה בין תרבויות. הרי, אנחנו היינו שמחים לרשום 30/1/2007, אבל יש לזה משמעות שונה לחלוטין בתרבויות שבהן התאריך נכתב בצורה שונה (למשל, בארה"ב התאריך הזה מתייחס ליום הראשון בחודש ה-30 בשנת 2007). אז כדי למנוע את הבלבול הזה, עובדים עם הפורמט ה-ISOי הזה.

נראה איך נוסיף את ה-DateTimeRangeValidator הזה דרך כלי הקונפיוגרציה הגרפי.

image_thumb29_thumb1

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

image_thumb30_thumb1

והקונפיוגרציית XML שנקבל מזה בקובץ BL.config תהיה:

          <property name="VisitDate">

            <validator lowerBound="" lowerBoundType="Ignore" upperBound="2010-01-01"

              upperBoundType="Inclusive" negated="false" messageTemplate="Visit date has to be before 1/1/2010"

              messageTemplateResourceName="" messageTemplateResourceType=""

              tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.DateTimeRangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Date Range Validator" />

          </property>

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

נראה אם באמת ב-GUI הנתונים עוברים וולידציה כמו שצריך:

image_thumb31_thumb1

image_thumb32_thumb1

שימו לב בבקשה שהשתמשנו בשני סוגים שונים לחלוטין של פירוט תאריכים. פעם כתבנו ה-1/1/2011 ופעם השנייה 12:30 13.2.2012 ועדיין הפריימוורק ידע איך לטפל במחרוזות האלו שמגיעות מתרבויות שונות לחלוטין, לפרמט אותן לתאריך ובצע עליהן וולידיה.

אז כמו שאמרנו, הסיבה לשימוש ב-DateTimeRangeValidator היא שכאשר יש לנו תאריך קבוע שנרצה לכתוב בקובץ קונפיוגרציה או ב-Attribute, לא נוכל לעשות את זה ב-RangeValidator רגיל.

Question from Tapuz .Net forum: How do I secure my application?

שאלה:

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

1. כמובן SQL INJECTION
2. עוגיות (שניתן יהיה לקרוא אותן רק מהשרת)
3. לא לאפשר להעלות קבצים עם סקריפטים למינהם.
4. אני יודעת שאפשר דרך הלוגים של האתר לזהות טרולים למינהם ש"אינם חפצים ביקר" האתר, איך מזהים אותם?


--------מה עוד?
אשמח לדעתכם המקצועית.
תודה!

 

תשובה:

אין גבול לאבטחה.

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

צורת המחשבה שאני, כמפתח מהישוב ולא כמומחה אבטחה, מאמץ היא - "איזה נכסים יש לי שאני צריך להגן עליהם?"
למשל, נכס (Asset) אחד שיש לי הוא רוחב פס של השרת. ניצול (Exploit) של הנכס הזה יכולה להיות מישהו שגונב לי רוחב פס. הפרצה (Attack Route) שתאפשר את זה יכולה להיות למשל משתמש שמעלה קבצים לשרת, מקשר החוצה ובכך למעשה גונב לי רוחב פס.
.
עוד נכס שיש לי הוא למשל מסד הנתונים עצמו (הריק לחלוטין) על מבנה הטבלאות שלו.
לנכס הזה אין ערך בפני עצמו, אבל ניצול של הנכס הזה בהתקפות נוספות יאפשרו למשתמש להכיר את המסד נתונים שלנו ובכך למעשה הוא השיג מידע מודיעני.
פרצה שמבוססת על הניצול הזה יכולה להתבסס למשל על להשיג את ה-Assembly של האפליקציה המקומפלת או קבצי המקור שמכילים בצורה מסויימת את השאילתות שהולכות למסד הנתונים ובכך להסיק את מבנה המסד.

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

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

דוגמה שאני מאוד אוהב להראות היא של Rocky שהוא Security MVP והוא מדגים איך דרך פרצה של Sql Injection באפליקציה דוט נטית, אפשר להשתמש בפרצת FTP במסד נתונים סיקוול סרבר, כדי להעלות אסמבלי חדשה לאפליקצה הדוט נטית שלנו שמדווחת לפורץ על כל מה שקורא באפליקציה.
Assembly Hijacking

 בארץ יש קבוצת Net Security. שפעם בחודש נפגשים וסוקרים איום אחר.
ניתן לראות העתק של כל המצגות מאז שהקבוצה התחילה להיפגש ובהחלט מומלץ לקרוא את המצגות ולהגיע לפגישות.
צירפתי את סלייד מספר 8 מהמצגת של נימרוד לוריה מהפגישה האחרונה של הקבוצה שממחישה כמה מהפרצות היותר מוכרות בתחום ה-web.

 WebAttackVectors

 

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

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

Validation Application Block with Strippers: RangeValidator (Overview of all VAB Validators)

image

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, פורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, יפורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, יפורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, יפורסם ב-19.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, יפורסם ב-22.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , יפורסם ב-23.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, יפורסם ב-24.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-25.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-26.7.2007

 

 

RangeValidator - נו, תן ציון ל-Lap dance שקיבלת

אחרי שקיבלנו שם חשפנית נרצה לדעת בדיוק איך היית מדרג את הביקור שלך.
נרצה לקבל מספר בין 1 ל-10.

נוסיף מאפיין חדש בשם Rating.

        private int _rating;

 

        public int Rating

        {

            get { return _rating; }

            set { _rating = value; }

        }

נוסיף RangeValidator על Rating ונראה מה האפשרויות שלנו כחלק מ-RangeValidatorAttribute.

image_thumb14_thumb6

אפשר לראות שניתן להפעיל RangeValidator על מגוון סוגים שאינטואטיבית אנחנו מבינים שניתן ליצור בהם טווח, ביניהם טווחים מספריים שונים (float, int, decimal, long ו-double) והטווחים התאריכיים.
בנוסף, אפשר להוסיף טווח בין שתי מחרוזות וההשוואה תתבצע לפי string.Compare, למשל המחרוזת "ג'סטין" נמצאת בין המחרוזת "אא" למחרוזת "תת".

ועוד דבר מעניין אפשר לראות שב-RangeValidatorAttribute אפשר לתת לו בהתחלה Type ושני ערכי גבול כ-string והוא יבצע את ההשוואה. ההגבלה היחידה היא שה-Typeים האלו יממשו את ממשק ה-IComparable. (כך שבאמת יהיה ניתן לגלות אם ערך מסויים הוא בטווח מסויים)

נחזור לענינו, אנחנו רוצים מספר בין 1 ל-10. אז נוכל להגיד כי:

        private int _rating;

        [RangeValidator(1, RangeBoundaryType.Inclusive, 10, RangeBoundaryType.Inclusive,

                        MessageTemplate="Rating has to be between 1 and 10")]

        public int Rating

        {

            get { return _rating; }

            set { _rating = value; }

        }

כלומר "תבדוק ש-Rating הוא מספר בין 1 (כולל) ו-10 (כולל)".

נוכל גם להגיד את הדבר הבא:

        private int _rating;

        [RangeValidator(0, RangeBoundaryType.Exclusive, 10, RangeBoundaryType.Inclusive,

                        MessageTemplate="Rating has to be between 1 and 10")]

        public int Rating

        {

            get { return _rating; }

            set { _rating = value; }

        }

או:

        private int _rating;

        [RangeValidator(1, RangeBoundaryType.Inclusive, 11, RangeBoundaryType.Exclusive,

                        MessageTemplate="Rating has to be between 1 and 10")]

        public int Rating

        {

            get { return _rating; }

            set { _rating = value; }

        }

וגם אפשר:

        private int _rating;

        [RangeValidator(0, RangeBoundaryType.Exclusive, 11, RangeBoundaryType.Exclusive,

                        MessageTemplate="Rating has to be between 1 and 10")]

        public int Rating

        {

            get { return _rating; }

            set { _rating = value; }

        }

וכל אלו הצהרות שקולות.

כלומר, שנאמר אם בודקים "מספר בין 1 (כולל) ל-10 (כולל)" או "מספר בין 0 (לא כולל) ל-11 (לא כולל)" אנחנו למעשה אומרים אותו דבר.
ההחלטה על איך לכתוב בקוד את הוולידציה צריכה לבוא מהמקור העסקי שלה. כלומר, איזה אופן כתיבה הופך את הקוד ליותר קריא. וכמובן שבמקרה הזה, הכי הגיוני לכתוב שאנחנו רוצים מספר בין 1 ל-10 ולכן נבחר באפשרות הזו.

        private int _rating;

        [RangeValidator(1, RangeBoundaryType.Inclusive, 10, RangeBoundaryType.Inclusive,

                        MessageTemplate="Rating has to be between 1 and 10")]

        public int Rating

        {

            get { return _rating; }

            set { _rating = value; }

        }

נראה גם איך נוכל לפרט את זה באמצעות כלי הקונפיוגרציה.

image_thumb20_thumb1

image_thumb19_thumb1

נראה את ה-XML הנוצר ב-BL.config:

          <property name="Rating">         

            <validator lowerBound="1" lowerBoundType="Inclusive" upperBound="10"

              upperBoundType="Inclusive" negated="false" messageTemplate="Rating has to be between 1 and 10"

              messageTemplateResourceName="" messageTemplateResourceType=""

              tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="Range Validator" />

          </property>

נראה שהקוד הזה באמת מריץ וולידציה כמו שצריך.

image_thumb22_thumb1

image_thumb23_thumb1

image_thumb24_thumb1

הייתי רוצה שנראה מקור נוסף לידע על התנהגות הוולידטורים השונים.

כחלק ההתקנה של Enterprise Library 3.1 ניתנת לנו האפשרות להתקין QuickStarts ו-Sourceים.
כחלק מהקוד שיותקן במצב זה נקבל Unit testing של VAB.
כלומר, נוכל לראות קוד שמפעיל את הקוד של VAB מלבד הקוד שלנו.
ספציפית, נראה פרוייקט בשם Microsoft.Practices.EnterpriseLibrary.Validation.Tests.

היות ו-Unit testing הוא לא הנושא שלנו, נתמקד יותר בלקרוא את הקוד הזה כתיעוד הרשמי של VAB, מאשר להבין את המטרה שלו ואיך הוא רץ ומה ספציפית כל מתודה עושה.

נפתח את הפרוייקט Microsoft.Practices.EnterpriseLibrary.Validation.Tests, ובתוך ספריית Validators נפתח את הקובץ RangeValidatorFixture.cs שמכיל את ה-Unit testing של RangeValidator. נראה מתודה אחת מתוך הקובץ הזה:

        [TestMethod]

        public void NonNegatedRejectsNull()

        {

            RangeValidator validator = new RangeValidator(2);

 

            ValidationResults results = validator.Validate(null);

 

            Assert.IsFalse(results.IsValid);

        }

בשורה הראשונה, מאתחלים RangeValidator עם גבול עליון של 2.

            RangeValidator validator = new RangeValidator(2);

בשורה השנייה ,שולחים לו null לוולידציה.

            ValidationResults results = validator.Validate(null);

ובשורה השלישית, בודקים שאכן הוולידציה החזירה ש-IsValid=false, כלומר - null הוא לא מספר שקטן מ-2.

            Assert.IsFalse(results.IsValid);

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

נראה עוד Unit test:

        [TestMethod]

        public void NonNegatedSuccessWithStringBeetweenLowerAndUpper()

        {

            RangeValidator inclusiveValidator = new RangeValidator("cccc", RangeBoundaryType.Inclusive, "rrrr", RangeBoundaryType.Inclusive, null);

 

            Assert.IsTrue(inclusiveValidator.Validate("cccc").IsValid);

            Assert.IsTrue(inclusiveValidator.Validate("cccca").IsValid);

            Assert.IsTrue(inclusiveValidator.Validate("gggg").IsValid);

            Assert.IsTrue(inclusiveValidator.Validate("rrrr").IsValid);

        }

נוכל לראות כי איתחלו RangeValidator שהוא בין cccc (כולל) לבין rrrr (כולל).
ה-RangeValidator יחזיר IsValid=true ל-gggg (שהוא בבירור בין c ל-r), ל-cccca (שהוא בין cccc ל-rrrr).
בנוסף, היות והגבולות שלנו הם Inclusive נוכל לראות כי גם cccc ו-rrrr עצמם הם IsValid=true.

ואם הגבולות לא היו RangeBoundryType.Inclusive אלא RangeBoundryType.Exclusive? גם לזה יש Unit Test!

        [TestMethod]

        public void NonNegatedFailureWithStringBeetweenLowerAndUpper()

        {

            ...

 

            RangeValidator exclusiveValidator = new RangeValidator("cccc", RangeBoundaryType.Exclusive, "rrrr", RangeBoundaryType.Exclusive, null);

 

            Assert.IsFalse(exclusiveValidator.Validate("cccc").IsValid);

            Assert.IsFalse(exclusiveValidator.Validate("rrrr").IsValid);

 

        }

ואם נרצה לראות מה קורה עם RangeBoundryType.Ignore שראינו קודם? גם לזה יש Unit Test!

        [TestMethod]

        public void NonNegatedSuccessWithStringLower()

        {

            RangeValidator validator = new RangeValidator(null, RangeBoundaryType.Ignore, "cccc", RangeBoundaryType.Inclusive, null);

 

            Assert.IsTrue(validator.Validate("aaaa").IsValid);

            Assert.IsTrue(validator.Validate("cccc").IsValid);

        }

נוכל לראות שיש כאן RangeValidator שמחפש כל מחרוזת ש"קטנה" או "שווה" ל-"cccc".
ושה-RangeValidator יחזיר IsValid=true ל-"aaaa" ול-"cccc".

למעשה, כל התיעוד של VAB ובכלל EntLib יושב כחלק מה-Unit Testים האלו וקריא לחלוטין לכל מי שרוצה לשבת ולעיין בו.
במהלך המאמר, נשתמש ב-Unit tests האלו כדי להדגים פיצ'רים מעניינים ב-VAB.

Question from Tapuz .Net forum: Dynamic datasource for ASP.Net 2.0 Menu control using Provider model

שאלה:

 לא נראה לי שכאן מתאים להשתמש בפקד ה-Menu של ASP.Net 2.0.

את כל מבנה התפריטים אני שומר ב-DB מה שמכריח אותי בזמן ריצה ליצור "ידנית" Menu Nodes על פי ערכים מה-DB.

 

תשובה:

להפך, זה בדיוק המצב שלשמו נבנה ASP.Net 2.0.

אחת מהבשורות הגדולות של ASP.Net 2.0 היא מה שנקרא מודל ה-Providers.
הרעיון אומר ככה "אנחנו נספק לכם אפשרויות ברירת מחדל למקורות מידע של בעיות נפוצות ואתם תוכלו להחליף אותם אם תצטרכו".
למשל, זיהוי משתמשים (Membership), תפקידים (Roles), מידע עסקי על משתמשים (Profiles), קיסטום של WebParts ע"י משתמשים ופירוט מבנה האתר (SiteMap).
לכל אחד מאלו ולמספר נוספים יש Provider.

מה זה ה-Provider הזה? זה סט של פונקציות שמייצגות פעולות שצריך לעשות ביחס למקור מידע.
למשל, MembershipProvider שאחראי על משתמשים נותן מתודות לקבל רשימת משתמשים, להוסיף משתמש חדש, לבדוק שם משתמש וסיסמה, למחוק משתמש, לאפס סיסמה וכך הלאה. עצם העובדה שרשימת הדרישות לא תלויה במימוש, אומר שאפשר להחליף בין מקורות מידע. כברירת מחדל בנושא ה-Membership יש SqlServerMembershipProvider שממש את הפונקצוית האלו ביחס לסיקוול סרבר 2005. אם נרצה נוכל לממש MembershipProvider שמבוסס על מכונת מספרים אקראית שתדחה באופן אקראי את אימות המשתמשים שלא ירגישו בנוח מדי (או אם נרצה לעשות משהו פרודקטיבי נוכל לבסס Membership Provider על mySql, אקסס, קבצי XML או מצב הלבנה).

כנ"ל תקף ל-SiteMapProvdier כברירת מחד משתמשים ב-XmlSiteMapProvider שמבוסס על קריאת פורמט קבצי SiteMap אחיד.
אפשר לראות את הפירוט הזה בקובץ ה-web.config של הפריימוורק.

        <!-- default at: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\web.config-->

        <siteMap>

            <providers>

                <add siteMapFile="web.sitemap" name="AspNetXmlSiteMapProvider" type="System.Web.XmlSiteMapProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

            </providers>

        </siteMap>

כלומר, זה ה-Provider הברירת מחדל שיהיה זמין בכל אפליקציה על המכונה (ובכלל בכל מכונה שמותקן בה דוט נט).
אם נרצה, נוכל ליצור SiteMapProvider מבוסס על מסד נתונים סיקוול סרבר וברמת האפליקציה להחליף בקובץ ה-web.config אותו להיות הברירת מחדל לאפליקציה.

        <siteMap enabled="true" defaultProvider="AspNetSqlSiteMapProvider">

            <providers>

                <add name="AspNetSqlSiteMapProvider" type="CustomProvidersProject.SqlSiteMapProvider, CustomProvidersProject" securityTrimmingEnabled="true" connectionStringName="LocalSqlServer" sqlCacheDependency="CommandNotification"/>

            </providers>

        </siteMap>

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

לסיכום, נשאיר אותכם עם הדיאגרמה הבאה שמדגימה בצורה גרפית את מודל ה-Providerים:

image

בחלק העליון נוכל לראות את ה-Providerים האבסטרקטיים, (למשל SiteMapProvider)
בפס האמצעי נוכל לראות את המימוש הספציפי שלהם בדוט נט, (למשל, XmlSiteMapProvider שראינו)
ובחלק התחתון נוכל לראות את מקורות המידע עצמם (מסדי נתונים ו-XML).

הנה סרטון המדגים איך להשתמש ב-Membership Provider הברירת מחדל מבוסס הסיקוול.
How Do I: Set Up the SQL Membership Provider

למעבר כללי על מודל ה-Providerים:
MSDN Architecture Webcast: Extending and Adding Flexibility to ASP.NET 2.0 Using the Provider Model (Level 200)

 

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

Validation Application Block with Strippers: StringLengthValidator (Overview of all VAB Validators)

image

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. StringLengthValidator - בודקים שהכניסו שם חשפנית, פורסם ב-15.7.2007

8. RangeValidator - נו, תן ציון ל-Lap dance שקיבלת, יפורסם ב-16.7.2007

9. DateTimeRangeValidator - מתי ביקרת אצלנו?, יפורסם ב-17.7.2007

10. ContainsCharctersValidator ו-Negated - איך קוראים לך?, יפורסם ב-18.7.2007

11. RelativeDateTimeValidator - מתי היום-הולדת שלך?, יפורסם ב-19.7.2007

12. PropertyComparisonValidator ושני וולידטורים על אותו מאפיין - נבדוק שהמבקר נולד לפני שהוא ביקר במועדון, יפורסם ב-22.7.2007

13.  RegexValidator - בודקים את הדוא"ל של המבקר , יפורסם ב-23.7.2007

14. DomainValidator - מה הסוג משקה אלכוהולי האהוב עלייך?, יפורסם ב-24.7.2007

15. ObjectValidator - מה המספר פלאפון שלך?, יפורסם ב-25.7.2007

16. ObjectCollectionValidator - מה המספרי פלאפון של החברים שלך?, יפורסם ב-26.7.2007

 

 

חלק חמישי: כל ה-Validatorים שמגיעים עם VAB (או: "איך היה הביקור שלך במועדון החשפנות שלנו?")

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

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

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

image_thumb2_thumb_thumb1

 

ניצור טופס חדש באפליקציית ASP.Net שלנו וטופס חדש באפליקציית Winfroms שלנו.

image_thumb5_thumb_thumb1 

 

image_thumb7_thumb_thumb1

(מעצב גרפי או באונסר אני כבר לא אהיה)

 

נוסיף מחלקה חדשה לאפליקציה בשם BL.VisitingRating.

namespace BL

{

    public class VisitRating

    {

    }

}

 

StringLengthValidator - בודקים שהכניסו שם חשפנית

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

    public class VisitRating

    {

        private string _stripperName;

        public string StripperName

        {

            get { return _stripperName; }

            set { _stripperName = value; }

        }

    }

נדאג גם להוסיף שדה מתאים ל-ASP.Net עם PropertyProxyValidator.

image_thumb8_thumb_thumb1

image_thumb9_thumb_thumb1

ב-Winforms נדאג להוסיף שדה מתאים.

image_thumb10_thumb_thumb1

היות וזהו טופס חדש, נוסיף גם ValidationProvider עם ErrorProvider שמכוון על BL.VisitRating.

image_thumb14_thumb_thumb1

image_thumb13_thumb_thumb1

על שדה ה-TextBox נקבע כי הוא מבצע וולידציה לפי הכללים שנקבעו ב-VistingRating.StripperName.

image_thumb15_thumb_thumb1

בשלב זה, טיפלנו ב-GUI שלנו כך שיריץ כללי וולידציה על BL.VisitRating.StripperName.

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

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

נבחר להוסיף StringLengthValidator על VisitRating.StripperName.

        private string _stripperName;

        [StringLengthValidator()]

        public string StripperName

        {

            get { return _stripperName; }

            set { _stripperName = value; }

        }

בואו נראה מה האפשרונות שלנו ל-StringLengthValidatorAttribute:

image_thumb_thumb1

מהסוף להתחלה:

1. נוכל לציין רק מספר יחיד והוא יהיה המקסימום תווים בתוך המחרוזת - לא מתאים לנו.

2. נוכל לציין שני מספרים: אחד כמינימום תווים והשני כמקסימום תווים. אבל, אמרנו שאנחנו לא רוצים מקסימום תווים - לא מתאים לנו.

3. נוכל לציין שני מספרים ושני Enumים מוזרים מסוג RangeBoundryType. שני המספרים הם המינימום והמקסימום. בואו נביט על ה-RangeBoundryType הזה או מה שזה לא יהיה.

image_thumb1_thumb1

אפשרות אחת היא RangeBoundryType.Inclusive, כלומר "כולל" אותו גבול.
אפשרות נוספת היא RangeBoundryType.Exclusive, כומר "לא כולל" \ "עד" אותו גבול.
אפשרות אחרונה היא RangeBoundryType.Ignore, שאומר שכלל לא תתבצע וולידציה על גבול זה.

הגבול המינימלי שלנו מבחינת כמות תווים הוא 1 תו אחד (כולל).
הגבול המקסימלי שלנו מבחינת כמות תווים הוא RangeBoundryType.Ignore.

        private string _stripperName;

 

        [StringLengthValidator(1, RangeBoundaryType.Inclusive, 1000000, RangeBoundaryType.Ignore,

                               MessageTemplate = "Stripper name cannot be empty")]

        public string StripperName

        {

            get { return _stripperName; }

            set { _stripperName = value; }

        }

במקום 1,000,000 היינו יכולים לרשום כל דבר (למשל, 0) היות ולא מתבצעת שום וולידציה על הגבול העליון של הוולידטור.

 

נראה איך ניתן להוסיף את ה-StringLengthValidator דרך הכלי הגרפי ואת ה-XML שהוא מייצר. (אם במקום הוספת ה-Attribute החלטנו להשתמש בקבצי קונפיוגרציה).

היות ועוד לא הוספנו את VisitRating לקובץ הקונפיוגרציה BL.config נוסיף אותו בפעם הראשונה.

image_thumb2_thumb6

image_thumb3_thumb1

 

image_thumb4_thumb1

image_thumb7_thumb6

נוסיף וולידציה מסוג StringLengthValidaor.

image_thumb9_thumb6

ונקבע לו את המאפיינים ונשמור שינויים.

image_thumb10_thumb6

והנה ה-XML שקיבלנו בקובץ הקונפיגיורציה BL.config.

    <type defaultRuleset="default" assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

      name="BL.VisitRating">

      <ruleset name="default">

        <properties>

          <property name="StripperName">

            <validator lowerBound="1" lowerBoundType="Inclusive"

              upperBound="0" upperBoundType="Ignore"

              messageTemplate="Stripper name cannot be empty"

              messageTemplateResourceName="" messageTemplateResourceType=""

              tag="" negated="false"

 type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

              name="String Length Validator" />

          </property>

        </properties>

      </ruleset>

    </type>

נריץ את האפליקציות שלנו ונוכל לראות שלא משנה אם בחרנו ב-Attributes או\ו קונפיגיורציה באמת נקבל שגיאה כאשר לא נכתוב שם חשפנית.

image_thumb13_thumb6

image_thumb11_thumb1

Question from Tapuz .Net forum: Authentication and Impersonation flow in ASP.Net and Sql Server

שאלה:

באפליקציית ASP.Net החיבור ל-DB לא עובד כשמנסים מרחוק, להבדיל מכאשר מנסים לוקלית (F5). למטה יש את הודעת השגיאה: 

Server Error in '/aa' Application.
--------------------------------------------------------------------------------
Cannot open database "pubs" requested by the login. The login failed.

Login failed for user 'MOSHE-F91B90F39\ASPNET'.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.SqlClient.SqlException: Cannot open database "pubs" requested by the login. The login failed.
Login failed for user 'MOSHE-F91B90F39\ASPNET'.

 

תשובה:

לפי מה שאתה מתאר סביר להניח שמדובר ב-Impersnation עם Windows Authentcation של ASP.Net בשילוב עם Windows Authentication של סיקוול סרבר.

image

ל-ASP.Net יש שלושה אפשרויות לזיהוי משתמשים:
- None, כלום, אין שום מנגנון ברירת מחדל של ASP.Net שאחראי לזיהוי משתמשים ובהתאם אין יכולת להשתמש ב-Membership, Roles ו-Profiles

        <authentication mode="None" />

- Forms, מספק מנגנון כללי לתוכניתן שיקבע בעצמו מתי המשתמש מחובר. למרות שאת מלאכת האימות בפועל מבצע התוכניתן אחראי שהמשתמש אומת נוכל להשתמש בכל האפשרויות ש-ASP.Net מציע למשתמשים מחוברים.

        <authentication mode="Forms" />

- Windows, מאמת את המשתמש לפי משתמש של חלונות. מבוסס על זיהוי בין שני מחשבים או באמצעות Domain Controller של Active Directory. כלומר, הזיהוי שלך בחלונות הוא הזיהוי שלך לאפליקציית ASP.Net. מיותר לציין שהיות ושרת בצד השני של העולם לא יכיר את שם המשתמש שלך, האפשרות הזאת אינה בשימוש באינטרנט אלא רק ברשתות פנימיות.

        <authentication mode="Windows" />

- Passport, מאפשר אימות לפי .Net Passport. (וזאת הסיבה שאמרתי שיש רק שלוש אפשרויות, כי עוד לא נתקלתי באתר מחוץ למיקרוסופט שמשתמש בזה)

        <authentication mode="Passport" />

 

ASP.Net צריך לרוץ בתוך השרת תחת ההרשאות של משתמש מסויים (למשל כי הוא ניגש לקבצים ובכלל כי הפריימוורק צריכה הרשאות של משתמש כלשהו). המשתמש הזה כברירת מחדל הוא משתמש בשם ASPNET שמותקן על המחשב ומקושר ל-IIS במהלך התקנת דוט נט פריימוורק.
ב-ASP.Net יש מנגנון בשם Impersnation שמאפשר לאפליקציה ספציפית לרוץ תחת הקונטקסט של משתמש אחר.
אפשרות אחת היא  לפרט שם משתמש, סיסמה (ודומיין של Active Directory) ואפשרות שנייה היא להשתמש ב-Windows Authentication שסופק ע"י המשתמש ולהתחזות למשתמש הזה. כלומר, ASP.Net ירוץ תחת קונטקסט האבטחה של המשתמש שהתחבר לאפליקציה.  

<?xml version="1.0"?>

<configuration>

    <appSettings/>

    <connectionStrings/>

    <system.web>

         <identity impersonate="true"/>

        <authentication mode="Windows"  />

    </system.web>

</configuration>

 

סיקוול סרבר מאפשר שתי אפשרויות לזיהוי משתמשים:
Windows Authentication שבדומה ל-ASP.Net לוקח את המשתמש המחובר של חלונות ובודקת ביחס לשרת הסיקוול ולמסד נתונים ספציפי בתוכו אם יש למשתמש הרשאות.
Mixed Mode מאפשר זיהוי לפי Windows Authentication וגם לפי שם משתמש וסיסמה שהם Hard-coded בתוך הסיקוול סרבר עצמו. כלומר, סיקוול סרבר ינהל לעצמו רשימת שמות משתמש וסיסמאות.

image 


כחלק ממחרוזת החיבור מאפליקציה לסיקוuל סרבר ניתן לפרט שם משתמש וסיסמה או שרוצים Integrate Security שבפועל זה Windows Authentication.

מה שקורה אצלך באפליקציה זה ככה:
משתמש ניגש לאתר ה-ASP.Net.
המשתמש מזוהה לפי Windows Authentication. (כי ככה פורט ב-web.config)
ASP.Net מתחזה למשתמש שכרגע התחבר לאתר. (גם, כי ככה מפורט ב-web.config)
ASP.Net מנסה להתחבר לסיקוול סרבר עם Integrated Security. כלומר, ASP.Net שולח לסיקוול את קונטקסט האבטחה שתחתיו הוא רץ ובפועל זה המשתמש שהתחבר לאתר.
סיקוול סרבר רואה איזה שם משתמש מוזר של חלונות שהוא לא מכיר ודוחה את בקשת ההתחברות.

יש הרבה דרכים לפתור את "השגיאה" שקיבלת.
להתחבר לסיקוול עם שם משתמש וסיסמה קבועים שיקבעו בתוך המסד נתונים ויפורטו במחרוזת החיבור למסד.
לחבר את סיקוול סרבר ל-Domain Controller של Active Directory ואז לאפשר לו להכיר את כל המשתמשים המחוברים פנימית.
ועוד הרבה, תלוי בארכיטקטורת האבטחה המועדפת אלייך.
כ-כלל אצבע באפליקציות ASP.Net אינטרנט ציבוריות נהוג לקבוע impersonate=false ו-Authentication כ-Forms.
ובסיקוול סרבר במצבים כאלו מומלץ להשתמש ב-Mixed Mode Authentication עם שם משתמש וסיסמה קבוע לאפליקציה. (מה שגם נותן בוסט גדול בביצועים לעומת Windows Authentication)

 

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

Validation Application Block with Strippers: Specifying Business Entities Validation in Configuration files

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-12.7.2007

7. המשך יבוא.  

image

 

חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד")

נושא נוסף שנרצה לראות הוא כללי וולידציה בתוך קבצי קונפיגיורציה.

image_thumb61_thumb2

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

איפה היינו? אהה, כן, קבצי קונפיוגרציה.

זוכרים את קבצי ה-App.config וה-web.config? מסתבר שאפשר להוסיף לתוך קבצי קונפיוגרציה כאלו את רשימת הכללי וולידציה של הישות העסקית שלנו. כלומר, במקום הישות כפי שהיא נראית עכשיו:

namespace BL

{

    public class Stripper

    {

        public Stripper()

        {

        }

 

        public Stripper(string Name, int Age)

        {

            this.Name = Name;

            this.Age = Age;

        }

 

        private string _name;

        [StringLengthValidator(1, 100,

           MessageTemplate = "Stripper name is between 1 to 100 charcters")]

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

 

        private int _age;

        [RangeValidator(18, RangeBoundaryType.Inclusive, int.MaxValue,  RangeBoundaryType.Inclusive,

                        MessageTemplate="Stripper has to be at least 18")]

        public int Age

        {

            get { return _age; }

            set { _age = value; }

        }

    }

}

נוכל לכתוב את כללי הוולידציה בתוך קובץ ה-config שלנו עם איזה תחביר XMLי מוזר.

ניצור בפרוייקט ה-BL שלנו קובץ config בשם BL.config.

image_thumb63_thumb2

ונקבל בקובץ ה-BL.config:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

</configuration>

נרצה להוסיף תגית <validation> בתוך התגית <configuration> שבתוכה נוכל לכתוב את כללי הוולידציה שלנו.
בשביל זה נוסיף בתוך תגית ה-<configsections> (שהיא תמיד התגית הראשונה בתוך תגית ה-<configuration>) הצהרה על כזו תגית <validation>.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="validation"

             type="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <validation>

 

 

    </validation>

</configuration>

 

הוספנו תגית <validation> שנקבע לפי ה-name וההגדרה שלה מפורטת בתוך המחלקה Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings.

נרצה עכשיו להוסיף וולידציה ל-BL.Stripper.
נוסיף תגית <type> חדשה לתוך <validation> ובה נקבע Assembly ו-Type שמתאים לטיפוס BL.Stripper.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="validation"

                type="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <validation>

        <type    assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

                name="BL.Stripper" >

 

        </type>

    </validation>

</configuration>

עכשיו נוסיף משהו שנקרא ruleset. אנחנו לא מכירים את החיה המוזרה הזו ונכיר רק בהמשך המאוד רחוק של המאמר. אז כרגע תאמינו לי שזה טוב ליהודים.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="validation"

                type="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <validation>

        <type    assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

                name="BL.Stripper"

                defaultRuleset="default" >

            <ruleset name="default">

 

            </ruleset>

        </type>

    </validation>

</configuration>

ונחליט עכשיו לתרגם את כלל הוולידציה ש-Stripper.Name הוא עם StringLengthValidator בין תו אחד ל-100 תווים. ה-Attribute הבאה תהפוך מהקוד הבא:

        [StringLengthValidator(1, 100,

           MessageTemplate = "Stripper name is between 1 to 100 charcters")]

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

לקונפיוגרציה הזאת:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="validation"

                type="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <validation>

        <type    assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

                name="BL.Stripper"

                defaultRuleset="default" >

            <ruleset name="default">

            <properties>

                <property name="Name">

                    <validator type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                               lowerBound="1"

                               upperBound="100"

                               messageTemplate="Stripper name is between 1 to 100 charcters"

                               />

                </property>

            </properties>

            </ruleset>

        </type>

    </validation>

</configuration>

קודם הגדרנו שאנחנו מבצעים וולידציה על מאפיינים של המחלקה באמצעות תגית ה-<properties>. (ניתן גם לבצע וולידציה על מתודות ושדות פרטיים)

הגדרנו על איזה מאפיין נרצה לבצע וולידציה באמצעות קביעת name ל-<property>.

ובתוך <property> הגדרנו איזה וולידטורים אנחנו נרצה להריץ.

כדי להשלים את התהליך נצטרך להוסיף עוד כמה מאפיינים ל-<validator>:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="validation"

                type="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <validation>

        <type    assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

                name="BL.Stripper"

                defaultRuleset="default" >

            <ruleset name="default">

            <properties>

                <property name="Name">

                    <validator type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                               name="stripperNameLength"

                               lowerBound="1"

                               lowerBoundType="Inclusive"

                               upperBound="100"

                               upperBoundType="Inclusive"

                               messageTemplate="Stripper name is between 1 to 100 charcters"

                               />

                </property>

            </properties>

            </ruleset>

        </type>

    </validation>

</configuration>

name מיועד לזיהוי חח"ע של כלל הוולידציה. ובנוסף פירטנו שה-stringLengthValidator כולל את "1" ו-"100".

נהפוך את ה-RangeValidator מ-Attribute לקונפיגיורציה.

        [RangeValidator(18, RangeBoundaryType.Inclusive, int.MaxValue, RangeBoundaryType.Inclusive,

                        MessageTemplate = "Stripper has to be at least 18")]

        public int Age

        {

            get { return _age; }

            set { _age = value; }

        }

יהפוך ל:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="validation"

                type="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <validation>

        <type    assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

                name="BL.Stripper"

                defaultRuleset="default" >

            <ruleset name="default">

            <properties>

                <property name="Name">

                    <validator type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                               name="stripperNameLength"

                               lowerBound="1"

                               lowerBoundType="Inclusive"

                               upperBound="100"

                               upperBoundType="Inclusive"

                               messageTemplate="Stripper name is between 1 to 100 charcters"

                               />

                </property>

                <property name="Age">

                    <validator type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                               name="stripperAgeRange"

                               lowerBound="18"

                               lowerBoundType="Inclusive"

                               upperBound="0"

                               upperBoundType="Ignore"

                               messageTemplate="Stripper has to be at least 18"

                               />

                </property>

            </properties>

            </ruleset>

        </type>

    </validation>

</configuration>

בנקודה הזו כל מה שעשינו זה ליצור קובץ XML מאוד מתוחכם.

הקובץ XML הזה במקרה (או שלא) תואם לקובץ קונפיוגרציה ש-EntLib3.1 יכול לטעון ולהשתמש ב-VAB.

צריך גם להגיד לאפליקציות שלנו שצריך לטעון איכשהו את הקובץ קונפיורציה BL.config.

נוסיף לפרוייקט ה-Winforms שלנו קובץ App.config.

image_thumb65_thumb2

וככה נראה קובץ ה-App.config שלנו:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

 

</configuration>

נרצה עכשיו איכשהו לציין באמצעות קובץ הקונפיוגרציה של האפליקציה שלנו שיילך ויטען את BL.config לקונפיוגרציה של EntLib.

נוכל ציין FileConfigurationSource שיושב בתוך ConfigurationSourceSection של EntLib. נוסיף את ה-ConfigSection הרלוונטי לקובץ ה-App.config:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="enterpriseLibrary.ConfigurationSource"

                type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <enterpriseLibrary.ConfigurationSource selectedSource="File Configuration Source">

 

    </enterpriseLibrary.ConfigurationSource>

</configuration>

ונוסיף source שהולך לקובץ BL.config:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <configSections>

        <section name="enterpriseLibrary.ConfigurationSource"

                type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <enterpriseLibrary.ConfigurationSource selectedSource="File Configuration Source">

        <sources>

            <add type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource, Microsoft.Practices.EnterpriseLibrary.Common, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                name="File Configuration Source"

                filePath="..\..\..\BL\BL.config" />

        </sources>

    </enterpriseLibrary.ConfigurationSource>

</configuration>

(שמנו שלושה סלאשים אחרונית היות ומבנה התיקיות שלנו הוא קודם ללכת אחרונית את ה-debug, אז את bin, אז את תיקיית הפרוייקט ומשם ניכנס לפרוייקט BL ורק שם נמצא את BL.config).

עכשיו שנריץ את האפליקציה נראה ש:

image_thumb67_thumb2

צריך לזכר שהכלל הזה מגיע מתוך קובץ קונפיוגרציה ולא מתוך ה-Attributes של BL.Stripper.

כנ"ל נוכל להוסיף לקובץ ה-web.config של ה-ASP.Net שלנו. נוסיף את ההפנייה לקובץ BL.config ל-web.config: 

<?xml version="1.0"?>

 

<configuration>

    <configSections>

        <section name="enterpriseLibrary.ConfigurationSource"

                type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

 

    <enterpriseLibrary.ConfigurationSource selectedSource="File Configuration Source">

        <sources>

            <add type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource, Microsoft.Practices.EnterpriseLibrary.Common, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                name="File Configuration Source"

                filePath="..\BL\BL.config" />

        </sources>

    </enterpriseLibrary.ConfigurationSource>

 

    <appSettings/>

    <connectionStrings/>

 

    <system.web>

         ...

    </system.web>

</configuration>

(יש סלאש אחרונית אחת יחיד היות ואנחנו הולכים מתיקיית הפרוייקט של ה-Web Project ולא מתוך ה-project\bin\deug).

וכאשר נריץ את הפרוייקט, נכניס את הקלט הבא ונלחץ על כפתור ה-submit נראה:

image_thumb69_thumb2

ושוב, ההגדרות וולידציה האלו מגיעות מקבצי וולידציה.

אין שום Attributes על Stripper.

namespace BL

{

    public class Stripper

    {

        public Stripper()

        {

        }

 

        public Stripper(string Name, int Age)

        {

            this.Name = Name;

            this.Age = Age;

        }

 

        private string _name;

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

 

        private int _age;

        public int Age

        {

            get { return _age; }

            set { _age = value; }

        }

    }

}

 

עכשיו, אני לא יודע מה איתכם, אבל אני לא אוהב לכתוב XML ידנית. אבל אני כן מעדיף לעבוד עם GUI או לתכנת בסיטואציות האלו:

image_thumb71_thumb3

בואו נמחוק את קובץ ה-BL.config שעבדנו בעמל רב לכתוב אותו, ונוציא את ההפניות לקובץ ה-BL.config מקובץ ה-app.config של ה-Wifnroms ומקובץ ה-web.config של ה-ASP.Net.

ניצור מחדש את קובץ ה-BL.config כך שכל מה שהוא מכיל אלו תגיות <configuration>.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

</configuration>

נלחץ על הקובץ כפתור ימני ונבחר Edit Enterprise Library Configuration.

image_thumb73_thumb2

ונראה את אפליקציית עריכת ה-Configuration של EntLib שתמיד הייתה תוכנה חיצונית ל-Visual Studio עכשיו בתוך Visual Studio.

image_thumb75_thumb2

כפתור ימני על BL.config ונבחר להוסיף ConfigSection שמתאים ל-VAB.

image_thumb77_thumb2

וקיבלנו:

image_thumb79_thumb2

כפתור ימני על Validation Application Block ונבחר להוסיף וולידציה ל-Type נוסף. ספציפית, ל-BL.Stripper.

image_thumb81_thumb2

נקבל את ה-Type Selector של כלי הקונפיוגרציה של EntLib.

image_thumb83_thumb2

נפתח את האסמבלי BL, נפתח את ה-Namespace בשם BL ונבחר את Stripper.

image_thumb85_thumb2

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

image_thumb87_thumb2

נשנה את השם של ה-ruleset ל-"default".

image_thumb90_thumb2

ונקבע את "default" כ-ruleset ברירת מחדל של Stripper.

image_thumb92_thumb6

(שנגיע לחלק על Rulesetים אני מבטיח שתבינו מה זה Ruleset, עד אז זה לא תורם לנו הרבה כרגע)

כפתור ימני על default ונדאג להוסיף את המאפיינים Stripper.Name ו-Stripper.Age. 

image_thumb94_thumb2

image_thumb99_thumb2

ונקבל:

image_thumb101_thumb2

נזכור שאנחנו מנסים לקבוע וולידציה על Stripper.Age מסוג StringLengthContainer בין 1 ל-100.

כך זה נראה ב-Attributes:

        [StringLengthValidator(1, 100,

           MessageTemplate = "Stripper name is between 1 to 100 charcters")]

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

ובעורך GUI נוסיף StringLengthValidator:

image_thumb107_thumb2

נוסיף גם Range Validator ל-Stripper.Age. כך שיתאים ל-Attribute הזו:

        [RangeValidator(18, RangeBoundaryType.Inclusive, int.MaxValue, RangeBoundaryType.Inclusive,

                        MessageTemplate = "Stripper has to be at least 18")]

        public int Age

        {

            get { return _age; }

            set { _age = value; }

        }

ונקבל ב-GUI:

image_thumb109_thumb2

נקבע מאפיינים בהתאם לכללי האימות:

image_thumb105_thumb2 image_thumb120_thumb2

נשמור את השינויים בקובץ BL.config ונראה את ה-XML שנוצר:

<?xml version="1.0" encoding="utf-8"?>

<configuration>

    <configSections>

        <section name="validation" type="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

        <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

    </configSections>

    <validation>

        <type defaultRuleset="default" assemblyName="BL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

            name="BL.Stripper">

        <ruleset name="default">

        <properties>

            <property name="Name">

                <validator lowerBound="1" lowerBoundType="Inclusive" upperBound="100"

                    upperBoundType="Inclusive" negated="false" messageTemplate=""

                    messageTemplateResourceName="" messageTemplateResourceType=""

                    tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                    name="String Length Validator" />

            </property>

            <property name="Age">

                <validator lowerBound="18" lowerBoundType="Inclusive" upperBound=""

                    upperBoundType="Ignore" negated="false" messageTemplate="Stripper has to be at least 18"

                    messageTemplateResourceName="" messageTemplateResourceType=""

                    tag="" type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                    name="Range Validator" />

            </property>

        </properties>

        </ruleset>

        </type>

    </validation>

</configuration>

מאוד דומה ל-XML שעמלנו קשות כדי לכתוב!

עכשיו נרצה להוסיף הפנייה לקובץ BL.config שלנו מקובץ ה-App.config של אפליקציית ה-Winforms וקובץ ה-Web.config של אפליקציית ה-ASP.Net.

כפתור ימני על קובץ ה-Web.config ונבחר Edit Enterprise Library Configuration.

image_thumb134_thumb2

image_thumb125_thumb2

נבחר להוסיף  Configuration Sources:

image_thumb126_thumb2

image_thumb128_thumb2

נמחק את ה-System Configuration Source ונבחר להוסיף File Configuration Source.

image_thumb130_thumb2

image_thumb139_thumb2

נקבע במאפיינים שלו שהוא יילך לקובץ ה-BL.config.

image_thumb137_thumb2

וכאשר נפתח ונסגור את ה-Enterprise Library Configuration על קובץ ה-Web.config נוכל לראות שהמאפיינים של VAB זמינים לעריכה בעורך הגרפי. זאת למרות שהם לא פיזית נמצאים בתוך קובץ ה-web.config.

image_thumb141_thumb2

ניתן לחזור על התהליך עבור אפליקציות Winforms, אך עקב בעיות של כלי הקונפיוגרציה אני ממליץ בחום שתישארו בעריכה ידנית של קובץ ה-XML. הבעיה היא שכלי הקונפיוגרציה דורש יצירת קובץ Runtime.App.Config ויצירת קובץ Designtime.App.Config שהראשון מפנה עם שלושה סלאשים אחרונית לקובץ BL.config והשני מפנה עם סלאש יחיד. שני הקבצים כמובן לא מסונכרנים אחד עם השני ולכן עדיף להימנע ממדיניות זו.

בואו נראה אם באמת כל העריכה הגרפית הזו הניבה תוצאה טובה.

image_thumb143_thumb2

image_thumb145_thumb2

ואכן כללי הוולידציה שהזנו דרך הממשק ה-GUI זמינים ב-ASP.Net וב-Winforms.

 

מתי נשתמש בקבצי קונפיוגרציה מול Attributes?

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

בנוסף אם נצטרך לשנות את החוקים העסקיים שלנו בלי לקמפל את האפליקציה נוכל לערוך את קובץ הקונפיוגרציה אך לא נוכל לשנות Attributes.

כלל האצבע בבחירה בין קבצי קונפיגיורציה לבין Attributes היא השאלה הבאה: "האם הישויות העסקיות שלנו צריכות לדעת מהן כללי הוולידציה שלהן?".
נוכל למשל לשאול "האם BL.Stripper צריך לדעת ש-Name הוא בין תו אחד ל-100 תווים, והאם הוא צריך לדעת ש-Age הוא לכל הפחות 18?".

אם ענינו "כן" לשאלה למעלה, אז הרי שנרצה למען קריאות הקוד ולמען חלוקת אחריות להכניס את הוולידטורים באמצעות Attributes לישויות העסקיות שלנו.

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

 

 

מה עשינו בחלק הזה:

- ישמנו אינטגרציה בסיסית של VAB עם Winforms בזה שהוספנו ValidationProvider על הישות העסקית שייכנו אותו ל-ErrorProvider, וקבענו לכל פקד גרפי מאפיין על הישות העסקית שעליו תתבצע הוולידציה.

- הוספנו אינטגרציה בסיסית של VAB עם ASP.Net עם זה שלכל שדה ב-GUI הוספנו PropertyProxyValidator ואמרנו לאיזה ישות עסקית ומאפיין שלה היא קשורה.

- ראינו שאפשר לכתוב כללי וולידציה בתוך קבצי קונפיגיורציה ואיך ניתן להפנות לאותם קבצי קונפיגיורציה מהאפליקציות שלנו.

- ראינו את התהליך הידני שנצטרך לעשות כדי לכתוב כללי וולידציה בקבצי קונפיגיורציה (הוספת configSection, הוספת Type, הוספת ruleset, הוספת Properties, הוספת Property והוספת Validator).

- ראינו כיצד Enterprise Library Configuration tool מקל עלינו ביצירת אותם קבצי קונפיגיורציה.

Validation Application Block with Strippers: Introduction to ASP.Net Intergration and Winforms Intergration

סדרת לומדים Validation Application Block עם חשפניות

1. קדם דבר: למה צריך וולידציה? (או: "קופים. פשוט קופים."), פורסם ב-26.6.2007

2. חלק ראשון: הפתרון הקיים והמצוי ב-Winforms, Console ו-ASP.Net (או: "איך קוראים לך ובת כמה את?"), פורסם ב-8.7.2007

3. חלק שני: שימוש ישיר ב-Validatorים של VAB (או: "איך לגרום לקוד יחסית קריא להפוך להיות ארוך ומעיק"), פורסם ב-9.7.2007

4. חלק שלישי: הכנסת נתוני הוולידטורים לתוך מחלקות (או: "ממלכתי עבור וולידטור ג'נארי"), פורסם ב-10.7.2007

5. חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), פורסם ב-11.7.2007

6. חלק רביעי המשך: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד"), יפורסם ב-12.7.2007

7. המשך יבוא.  

image

 

חלק רביעי: אינטגרציה בסיסית עם ה-GUI ו-וולידציה מתוך קבצי קונפיוגרציה (או: "קצת יותר Drag&Drop, קצת פחות קוד")

מה שכן מוזר לי מאוד שאנחנו נאלצים ב-Winforms וב-ASP.Net לדאוג להציג את התוצאות של הוולידציה בעצמנו.
הרי מדובר גם במשימה מאוד פשוטה ונפוצה יחסית.
הרי תחת ההנחה שכל שדה ב-GUI מתכתב למאפיין במחלקה שלנו אפשר בקלות מאוד לבצע וולידציה פר-שדה.

כלומר, עכשיו אנחנו מנסים לראות איך נוכל להוריד את הלולאת foreach על כל התוצאות שלנו.

 

נתחיל מאינטגרציה בסיסית עם Winforms.

נוסיף ל-Toolbox שלנו פקד ValidationProvider שיושב ב-DLL בשם Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WinForms.dll.

כפתור ימני על ה-Tab הרלוונטי עבורנו ב-Toolbox ונבחר Choose Items.

image_thumb3_thumb2

נבחר את ה-ValidationProvider ונדאג שה-CheckBox שלו מסומן.

image_thumb7_thumb2

וקיבלנו את ה-ValidationProvider ב-toolbox.

image_thumb91_thumb2

נוסיף ValidationProvider לטופס בזה שנגרור אותו על הטופס.

image_thumb111_thumb2

וגם נגרור ErrorProvider לטופס.

image_thumb13_thumb2

נשנה את ה-IDים של ה-Providerים שהוספנו.

image_thumb15_thumb2

 

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

Providerים מוסיפים לנו ב-Design Time מאפיינים על פקדים גרפים.

נראה את המאפיינים שהוסיף ה-StripperValidation וה-errProvider על tbxName.

image_thumb20_thumb2

image_thumb22_thumb2

אפשר לראות שכל אחד מה-Providerים שלנו הוסיף ב-Design Time מאפיינים על הפקדים הגרפים שלנו.

נקנפג את ה-ValidationProvider שלנו.

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

image_thumb241_thumb2

קבענו שני דברים - ErrorProvider ל-ErrorProvider שלנו, וגם כתבנו ב-SourceTypeName את ה-Fully Qualified Name של הישות העסקית שלנו שמכילה נתוני וולידציה.

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

image_thumb261_thumb2

image_thumb28_thumb2

דבר ראשון קבענו ש-PerformValidation=true ביחס לוולידציה של חשפנית.
דבר שני קבענו איזה SourcePropertyName קשורה ספציפית לאותה TextBox.

ל-tbxName קבענו ש-Stripper.Name קשור.
ל-tbxAge קבענו ש-Stripper.Age קשור.

ומה כל העבודה הקשה הזו עם ValidationProvider ו-ErrorProvider הביא לנו? נראה את ה-GUI שלנו:

image_thumb30_thumb2

עכשיו ניצא מ-tbxName בלי להכניס טקסט (באמצעות לחיצה על Tab במקלדת או לחיצה על פקד גרפי אחר)

image_thumb33_thumb2

ואם נשים את העכבר על ה-Icon של ה-ErrorProvider (שאגב, ניתן להחליף את ה-Icon במאפיינים של ה-ErrorProvider) נראה ש:

image_thumb35_thumb2

נכניס גיל חשפנית שהוא קטן מ-17 ונראה דבר דומה:

image_thumb38_thumb2

וזה הבסיס של אינטגרציה של VAB עם Winforms. אל תדאגו, עוד נרחיב על הנושא הזה בהמשך.

 

עכשיו נראה אינטגרציה בסיסית של VAB עם ASP.Net.

נוסיף בצורה דומה לאיך שהוספנו ValidationProvider את ה-PropertyProxyValidator שיושב ב-Microsoft.Practices.EnterpriseLibrary.Validation.Integration.AspNet.dll.

image_thumb40_thumb2

image_thumb42_thumb2

בשונה מה-ValidatorProvider של Winforms ובדומה ל-Validatorים הקלאסיים של ASP.Net נוסיף PropertyProxyValidator לכל שדה ב-GUI.

image_thumb44_thumb2

נוסיף PropertyProxyValidator אחת ל-tbxAge ואחת עבור tbxName.

image_thumb46_thumb2

נראה אילו מאפיינים נחשפים לנו מתוך ה-PropertyProxyValidator.

image_thumb48_thumb2

נדבר קצת על ארבעת המאפיינים הרלוונטיים לנו כרגע:

שם מאפיין משמעות ערך עבור tbxName
ID vldName
ControlToValidate שם הפקד שעובר וולידציה tbxName
SourceTypeName שם הישות העסקית שקשורה לוולידטור הזה BL.Stripper, BL
PropertyName שם המאפיין בישות העסקית שקשור לוולידטור הזה Name

 

 

 

 

 

 

ובהתאם גם נקבע המאפיינים של vldName ו-vldAge:

image_thumb51_thumb2 image_thumb55_thumb2

נריץ את הדף:

image_thumb59_thumb2

ולאחר לחיצה על כפתור ה-submit נראה ש:

image_thumb57_thumb2

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

האינטגרציה עם ASP.Net ו-Winforms הולכת הרבה יותר עמוק וניתן לקנפג ולשנות הכל בה, ובנוסף יש גם אינטגרציה עם סוגי UI שונים. אנחנו נתמקד בכל הנושאים האלו בהמשך המאמר.

 

נושא נוסף שנרצה לראות הוא כללי וולידציה בתוך קבצי קונפיגיורציה.

More Posts Next page »