יום שישי, 11 באוקטובר 2013

Linq Deep Dive - Where

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

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

למשל: יש לנו collection של מספרים:

var nums = new[] {1,2,3,4,5,6,7,8,9,10};

אם נרצה לקבל את כל המספרים האי זוגיים, אז באמצעות Linq נבצע:

result = nums.Where(n => n % 2 != 0);

בואו נעשה drill-down ונבין איך זה ממומש. נתחיל מהתחלה:

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


/// <summary>
        /// return IEnumerable that contains only the numbers that are not Even
        /// </summary>
        /// <param name="nums"></param>
        /// <returns></returns>
        private static IEnumerable<int> FilterIntegers(IEnumerable<int> nums)
        {
            var result = new List<int>();

            foreach (var item in nums)
            {
                if (Predicate(item))
                {
                    result.Add(item);
                }
            }

            return result;
        }

        /// <summary>
        /// return true if the item is not Even.
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public static bool Predicate(int item)
        {
            return item % 2 != 0;
        }

וכעת אם נבצע את הקריאה הבאה:

var result = FilterIntegers(nums);

אז נקבל אל result את רשימת המספרים האי זוגיים.

איך אפשר לשפר את הפונקציה?

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

זה ייראה כך:

private static IEnumerable<int> FilterIntegers(IEnumerable<int> nums,PredicateDelegate<int> predicateDelegate )
        {
            var result = new List<int>();

            foreach (var item in nums)
            {
                if (predicateDelegate(item))
                {
                    result.Add(item);
                }
            }

            return result;
        }


נוסיף עוד פונקציית מיון (לשם הדוגמא נכתוב פונקצייה שמחזירה מספרים זוגיים:
public static bool IsEven(int item)
{
    return item % 2 == 0;
}

וכמובן ניצור delegate מתאים:

public delegate bool PredicateDelegate<T1>(T1 value);

כעת נוכל לבצע פילטור כך:

//point the delegate to any function
PredicateDelegate<int> myDelegate = IsEven;

var result = FilterIntegers(nums,myDelegate);

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

בואו נתקדם עוד שלב:

נשנה את הפונקציה FilterIntegers כך שתהיה יותר גנרית, ונשנה לה את השם ל Filter, כי כעת היא תוכל לסנן רשימה של כל Type:

public static IEnumerable<T1> Filter<T1>(IEnumerable<T1> nums, PredicateDelegate<T1> predicateDelegate)
{
    var result = new List<T1>();

    foreach (T1 item in nums)
    {
            if (predicateDelegate(item))
            {
              result.Add(item);
            }
    }

       return result;
 }

כעת רק כדי שיהיה לנו יותר קל (ולא נצטרך ליצור delegate) נשתמש ב Func כמו שראינו בפוסט הקודם:


public static IEnumerable<T1> Filter<T1>(IEnumerable<T1> list, Func<T1, bool> predicateDelegate)
{
    var result = new List<T1>();

    foreach (T1 item in nums)
    {
            if (predicateDelegate(item))
            {
              result.Add(item);
            }
    }

       return result;
 }

ונקרא כך לפונקציה שלנו:
ה delegate שלנו יצביע על פונקציית הסינון (הפעם נסנן מספרים אי-זוגיים):
Func<int, bool> predicateDelegate = IsEven;

ונפעיל את הפונקציה:
result= Filter(nums,predicateDelegate)

אז מה יש לנו עד עכשיו?

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

נמשיך-

במקום שה delegate שלנו יצביע לפונקציה שהיא בעצם ממומשת ע"י שורה אחת, נוכל להשתמש ב anonymous methods:

predicateDelegate = item => { return item % 2 == 0 ; };

ואפשר לכתוב את הנ"ל בכתיב מקוצר:

predicateDelegate = item =>  item % 2 == 0;

אז במקום לשלוח לפונקציה שלנו את predicateDelegate, אנחנו יכולים ישר לשלוח את ה  anonymous method שלנו:

result= Filter(nums, item =>  item % 2 == 0);

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

בעצם נותר לנו לבצע 2 פעולות:

  1. לשנות את הפונקציה ל  Extension method- כדי להפוך את הפונקציה ל Extension method פשוט נוסיף את המילה this לפני הארגומנט הראשון, ונעביר את הפונקציה ל class סטטי.
  2. לבצע yield return מהפונקציה- כך לא צריך לחכות עד שהפונקציה תסיים לחשב את מה שהיא צריכה, ועל כל איבר שהיא החליטה אם הוא תקין, אז היא תחזיר אותו (או יותר נכון Ienumerable )
  public static IEnumerable<T1> Filter<T1>( this IEnumerable<T1> list, Func<T1, bool> predicateDelegate)
{
    var result = new List<T1>();

    foreach (T1 item in nums)
    {
            if (predicateDelegate(item))
            {
              yield return item;
            }
    }
}

וזהו! סיימנו! מימשנו את פונקציית Where.

כעת נוכל לקרוא לפונקציית Filter:

result= nums.Filter( item =>  item % 2 == 0);

שזה בדיוק כמו:

result= nums.Where( item =>  item % 2 == 0);


תהנו!

יום חמישי, 3 באוקטובר 2013

Delegate, Action, Func ומה שבינהם

חבר בעבודה שאל אותי השבוע: מה זה Func?

אז בשביל להסביר לו, עניתי לו שזה פשוט צורה יותר קלה להגדרת Delegate.

אז נתחיל מההתחלה: מה זה Delegate?

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

הגדרת Delegate:

הגדרת ה Delegate תהיה תחת ה namespace שלנו, ולא תחת Class מסוים. לדוגמא:

delegate void PrintDelegate(string st);

מה הגדרנו כאן: הגדרנו שה Delegate שלנו יכול להצביע לכל פונקציה שהחתימה שלה: מחזירה Void ומקבלת String.

נאמר שיש לנו בתוכנית את שתי הפונקציות הנ"ל:

static void PrintOne(string st)
        {
            System.Console.WriteLine("{0} is in PrintOne Function", st);
        }

        static void PrintTwo(string st)
        {
            System.Console.WriteLine("{0} is in PrintTwo Function", st);
        }

לאחר שנריץ את הקוד הבא:

static void Main(string[] args)
        {
            PrintDelegate p = new PrintDelegate(PrintOne);
            
            p("Eyal");
        }

נקבל:
Eyal is in PrintOne Function

בשורה מספר 3 הגדרנו שה Delegate שלנו יצביע לפונקציה PrintOne, ובשורה 5 פשוט קראנו לDelegate שלנו בדיוק כמו שהיינו קוראים אם היינו קוראים לפונקציה.

Multycast Delegate

Delegate יכול להצביע גם על מספר פונקציות (נקרא Multycast Delegate). וכאשר "מפעילים" את ה Delegate, הוא יקרא לפונקציות בסדר שהם נרשמו. את הרישום נעשה באמצעות אופרטור =+

למשל:

static void Main(string[] args)
        {
            PrintDelegate p = new PrintDelegate(PrintOne);
            p += PrintTwo;
            p("Eyal");
        }


ונקבל:
Eyal is in PrintOne Function
Eyal is in PrintTwo Function

Closure

שימוש נפוץ ל Delegate הוא ביצוע Closure, כלומר העברת פונקציה כפרמטר.

נניח ויש לנו את הפונקציה הנ"ל:

static void MyPrintMethod(PrintDelegate p)
        {
            p("MyPrintMethod");
        }

הפונקציה מקבלת כפרמטר Delegate ומפעילה אותו.

ואז בתכנית שלנו פשוט נוכל לאתחל את ה Delegate עם הפונקציה שאנו רוצים לשלוח, וכך להפעיל את הפונקציה:

MyPrintMethod(new PrintDelegate(PrintTwo));

ונקבל:
MyPrintMethod is in PrintTwo Function

Generic Delegate

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

לשם ההדגמה נניח ויש לנו פונקציית הדפסה חדשה:


static void PrintThree(int num)
        {
            System.Console.WriteLine("{0} is in PrintThree Function", num);
        }

עכשיו תחת ה namespace נגדיר את ה Delegate הגנרי הבא:

delegate void PrintGenericDelegate(T t);

וכעת נוכל להשתמש עם אותו ה Delegate שיצביע על פונקציות עם חתימות שונות:



PrintGenericDelegate<string> pgs = new PrintGenericDelegate<string>(PrintTwo);

 pgs("Eyal");

 PrintGenericDelegate<int> pgi = new PrintGenericDelegate<int>(PrintThree);

 pgi(111);
ונקבל:
Eyal is in PrintTwo Function
111 is in PrintThree Function

<Action<T

יופי, אז ראינו שימושים שונים ל Delegate.
אבל זה קצת מסורבל, לא?

החל מ NET 2.0 קיבלנו את Action, שזהו פשוט סוג של Delegate, שיודע לקבל עד 16 פרמטרים, ומחזיר void. השימוש ב Action מקל לנו על כתיבת הקוד.

תראו איזה פשוט נהיה השימוש ב Delegate, באמצעות Action:
אין צורך להגדיר Delegate ב namespace, אלא פשוט יוצרים Action, ונותנים לו הצבעה לפונקציה:


Action<string> MyAction = PrintTwo;

 MyAction("action");

ונקבל:
action is in PrintTwo Function

ונראה איזה פשוט זה לשלוח פונקציה כפרמטר באמצעות Action (ביצוע Closure):

נניח ויש לנו את הפונקציה:

static void MyPrintMethodAction(Action<string> action)
        {
            action("MyPrintMethodAction");
        }

נפעיל את הקריאה ע"י:

Action<string> MyAction = PrintTwo;

MyPrintMethodAction(MyAction);

ונקבל:
MyPrintMethodAction is in PrintTwo Function

<Func<T,Result

ולשאלתנו, מה זה Func? זה פשוט כל מה ש Action, רק ש Func מחזיר פרמטר.

יש לנו לדוגמא את הפונקציה הפשוטה הבאה שמקבלת פרמטר מסוג int, ומחזירה int:

static int MultiplyTwice(int num)
        {
            return num * 2;
        }


נשתמש עם ה Delegate הבא:

Func<int,int> MyFunc = MultiplyTwice;

Console.WriteLine(MyFunc(4));


ונקבל: 8

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

תהנו!!

יום שישי, 27 בספטמבר 2013

Memory Allocation in the CLR

אז מה קורה בזכרון כאשר אנחנו יוצרים אובייקט מסוג ReferenceType בזיכרון?

נניח ויש לנו אובייקט מסוג Student שמכיל מספר פונקציות ומספר משתנים.

לאחר שנבצע ()new Student , הייצוג של האובייקט בזיכרון יראה כך:


ועכשיו להסבר:

כשנאתחל את האובייקט - מכיוון שהוא מסוג ReferenceType ,יוקצה לו מקום על גבי ה GC Heap ויהיה לעברו מצביע (בציור הנ"ל המצביע הוא על גבי הStack, אך לא מן הנמנע שהמצביע יהיה על גבי ה GC Heap)

לכל אובייקט שנמצא על גבי ה GC Heap יש שני שדות שמתווספים אוטומטית לאובייקט (ושגודל של כל אחד מהם 4bytes) השדות הם:

  1.   Sync Block Index -SBI - מצביע לטבלת synchronisation הבנויה מ Sync Blocks. הטבלה מכילה את המידע האם בוצע על האובייקט lock או לא. כל פעם שמבוצע lock על האובייקט, נוצר Sync Block חדש שמכיל מידע אודות ה lock.
  2.  Type Object Pointer - TOP- מצביע למקום שנקרא Method Table הנמצא על ה High Frequency Heap, המתאר את האובייקט. (ארחיב בהמשך)
High Frequency Heap- מקום על הHeap המכיל נתונים שניגשים אליהם בתדירות גבוהה. הוא לא מנוהל ע"י ה GC, הוא נוצר עם יצירת ה AppDomain, לכל AppDomain יש אחד כזה, ומטבע הדברים הוא מתנקה כאשר ה AppDomain "מת". דבר חשוב מאוד לדעת - כל המשתנים הסטטיים נמצאים על גבי ה High Frequency Heap, ומכאן שהם לא מתנקים ע"י ה GC..

Method Table - לפני שאנחנו יוצרים את ה Instance הראשון של האובייקט, כאשר כל ה Class וה Interface נטענים לראשונה ל AppDomain, נוצר על גבי ה High Frequency Heap ה Method Table, שמכיל את כל הנתונים על האובייקט, כמו: מאיזה type הוא, איזה פונקציות הוא מכיל (כולל כל ה ctor) ואת המשתנים הסטטיים.
מכאן הקומפיילר יודע איזה פונקציה האובייקט יפעיל (במקרים של הורשה למשל).
 לדוגמא: אם ניצור אובייקט בלי שנדרוס את ToString, בMethod Table תופיע הפונקציה ToString שה MethodDescription שלו יהיה שייך למחלקה System.Object, ומכאן - כאשר האובייקט יפעיל את ToString, אז יופעל ה ToString הדיפולטי שהמחלקה שלנו יורשת ממנו (במקרה הנ"ל זה System.Object). אך אם ניצור אובייקט שכתבנו לו פונקציה שדורסת את ToString, אז ה MethodDescription שלנו יהיה שייך למחלקה שלנו (Student), וכאשר האובייקט יפעיל את ToString, אז יופעל ה ToString שאנחנו כתבנו..


מי שרוצה לרדת עוד יותר לעומק הדברים, מאמר מצוין נמצא כאן

תהנו!

יום שישי, 20 בספטמבר 2013

יצירת Performance Counter

בהמשך לפוסטים האחרונים שלי על Tracing, הנה דרך מצוינת לאסוף סטטיסטיקות על האפליקציה שלנו:

בכל זמן נתון מערכת ההפעלה של Windows אוספת סטטיסטיקות על המערכת (כגון CPU, IO, סטטיסטיקות על IIS ועוד המון אחרים..). את הסטטיסטיקות אפשר לראות דרך ה Perfmon.exe.
בדרך זו אפשר ללמוד המון על המערכת שלנו (למשל כאשר היא רצה בסביבת Production).
יתרון גדול שמערכת ההפעלה מאפשרת לנו הוא שאנחנו יכולים ליצור Counter's משלנו, וכך יכולים לעקוב אחר ביצועי האפליקציה שלנו (למשל נוכל לראות כמה זמן בממוצע לוקח לשרות שלנו לעבד בקשה, כמו login בוצעו לאפליקציה, וכל סטטיסטיקה אחרת שאפשר לדמיין).

באמצעות ה PerformanceCounter class ש .Net מספק לנו, אנחנו יכולים לגשת לנתוני Counter שונים, ואף ליצור Counter חדשים לאפליקציה שלנו.

יש כל מיני Counter Types, אך השכיחים ביותר:
  1. NumberOfItems32- סופר מספר פריטים (למשל אפשר לספור כמה בקשות בוצעו)
  2. AverageTimer32- סופר את הזמן הממוצע שנדרש לפעולה מסוימת להיות מבוצעת
  3. RateOfCountsPerSecond32- סופר את מספר הפעולות שבוצעו בכל שנייה
יצירת Performance Counter חדש:

הדרך הראשונה (והפחות עדיפה) היא דרך ה Server Explorer  ב VS:



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

ניצור את ה Counter באמצעות הקוד (ככה שנראה אותו ב Perfmon..):

 
        if (!PerformanceCounterCategory.Exists("MyNewCategory"))
            {
                CounterCreationDataCollection counters = new CounterCreationDataCollection();

                //counting totals: PerformanceCounterType.NumberOfItems32
                CounterCreationData totalOperations = new CounterCreationData();
                totalOperations.CounterName = "# operations executed";
                totalOperations.CounterType = PerformanceCounterType.NumberOfItems32;
                counters.Add(totalOperations);

                //counting operations per second: PerformanceCounterType.RateOfCountsPerSecond32
                CounterCreationData opsPerSecond = new CounterCreationData();
                opsPerSecond.CounterName = "# operations / sec";
                opsPerSecond.CounterType = PerformanceCounterType.RateOfCountsPerSecond32;
                counters.Add(opsPerSecond);

                //counting average time per operation: PerformanceCounterType.AverageTimer32
                CounterCreationData avgDuration = new CounterCreationData();
                avgDuration.CounterName = "average time per operation";
                avgDuration.CounterType = PerformanceCounterType.AverageTimer32;
                counters.Add(avgDuration);

                //base counter for counting average time per operation: PerformanceCounterType.AverageBase
                CounterCreationData avgDurationBase = new CounterCreationData();
                avgDurationBase.CounterName = "average time per operation base";
                avgDurationBase.CounterType = PerformanceCounterType.AverageBase;
                counters.Add(avgDurationBase);

                // create new category with the counters above
                PerformanceCounterCategory.Create("MyNewCategory", 
                        "Sample category", counters);
            }

שימו לב!! אם יצרתם  AverageTimer אז חייבים מיד אחריו ליצור AverageBase.

לאחר שנריץ את הקוד הנ"ל יהיה אפשר להכנס ל Perfmon ולקבל:


עכשיו כל אפליקציה תוכל לכתוב/לקרוא נתונים על ה Counter שיצרנו (MyNewCategory)

יצירת האובייקט של ה Performance Counter באפליקציה שלנו:

כעת באפליקציה שלנו נרצה לאתחל את ה Counter's כדי שנוכל להתממשק לקטגוריה שיצרנו

     _TotalOps = new PerformanceCounter();
            _TotalOps.CategoryName = "MyNewCategory";
            _TotalOps.CounterName = "# operations executed";
            _TotalOps.MachineName = ".";
            _TotalOps.ReadOnly = false;

            _OpsPerSecond = new PerformanceCounter();
            _OpsPerSecond.CategoryName = "MyNewCategory";
            _OpsPerSecond.CounterName = "# operations / sec";
            _OpsPerSecond.MachineName = ".";
            _OpsPerSecond.ReadOnly = false;

            //another way to create...
            _AvgDuration = new PerformanceCounter("MyNewCategory",
            "average time per operation",
            false);

            _AverageDurationBase = new PerformanceCounter("MyNewCategory",
            "average time per operation base",
            false);

שימו לב!! כשאתחלנו את המשתנים באפליקציה שלנו- אם אחד מהפרמטרים לא תואם את מה שקיים על השרת (על פי מה שיצרנו בשלב הראשון- למשל ה Category שונה או ה CounterName שונה אז נקבל InvalidOperationException

הכנסת הנתונים ל Counter:

מה שנותר לנו כעת זה להכניס נתונים ל Counter..

לדוגמא בעת ריצת האפליקציה הבאה:

      Stopwatch s = new Stopwatch();
    
            for (int i = 0; i < 1000; i++)
            {
                s.Start();
                
                // simply increment the counters
                _TotalOps.Increment();

                _OpsPerSecond.Increment();

                Thread.Sleep(300+10*i);

                // increment the timer 
                _AvgDuration.IncrementBy(s.ElapsedTicks);
                _AverageDurationBase.IncrementBy(1);
            
                s.Stop();
                s.Reset();
            }      
יהיה אפשר לראות ב Perfmon את הסטטיסטיקה הבאה (למשל):


תהנו!

יום חמישי, 19 בספטמבר 2013

Best practices for Instrumenting Biztalk solution

בהמשך לפוסט האחרון שלי על Tracing high performance applications , היכן אנחנו צריכים לכתוב Tracing?

Pipeline Components
  • בעת כניסה ויציאה מהקומפוננטה.
  • פירוט של ה runtime exception שנזרק.
  • מדידת הזמן שלקח לפונקצייה מסוימת לרוץ/ במקרים מסוימים נרצה לדעת כמה לקח לכל הקומפוננטה לרוץ
  • ביצועTrace למצבים מסוימים בתוך הקומפוננטה (בתוך if) או למשתנה מסוים (שיעזור לנו בהמשך במקרה של בחינת תקלות)
Biztalk Maps
  • נרצה לדעת ערכים מסוימים שאנחנו רוצים למפות. נבצע באמצעות Scripting functoid. 

Biztalk Orchestrations

ביצוע Trace לאורכסטרציות זהו המפתח למעקב, דיאגנוסטיקה ולפתרון תקלות. זאת במיוחד כי חלק מהאורכסטרציות שלנו הופכות להיות למאוד מסובכות. (גם בעת פיתוח יותר קל לדאבג אורכסטרציות שיש עליהם Tracing)
נבצע:
  • מיד כאשר נכנסים לאורכסטרציה (לאחר receive shape)
  • ביצועTrace למצבים מסוימים בתוך האורכסטרציה (בתוך if) או למשתנה מסוים (שיעזור לנו בהמשך במקרה של בחינת תקלות)
  • פירוט של ה runtime exception שנזרק.
  • מדידת הזמן שלקח ל scope מסויים לרוץ/ במקרים מסוימים נרצה לדעת כמה לקח לכל האורכסטרציה לרוץ
  • לפני שיוצאים מהאורכסטרציה (לפני ה exit point)
מעקב מהנה!


יום ראשון, 18 באוגוסט 2013

Tracing High Performance Applications - Event Tracing for Windows - ETW

ביצוע instrumentation אפליקטיבי הוא חלק הכרחי במערכות תוכנה סבוכות. כתיבה עשירה של tracing ו logging מאפשר לנו לעקוב אחר ההתנהגות של האפליקציה שלנו - במיוחד בסביבות פרודקשין שבו אנחנו לא יכולים להתחיל לדאבג את המערכות.
זה מתחיל להיות קריטי עבור מערכות High-performance (למשל אפליקציות BIZTALK)שבהם כל כתיבה ללוג או ל Trace יכול להפחית את ביצועי המערכת.

האופציות השכיחות לכתוב ל Trace (באמצעות קומפוננטות צד שלישי):

  1. System.Diagnostic.Trace.Component
  2. Enterprise Library Tracing Component
  3. Log4Net
אבל:
  • כאשר נפתח את DebugView כדי לצפות בEvent-ים הרבים , הכלי יצרוך כ 85% ממשאבי ה CPU. (הכלי  DebugView מתפקד  כ Debugger  ונרשם ל OUTPUT_DEBUG_STRING_EVENT) כתוצאה מכך יש פגיעה משמעותי ב Performance של האפליקציה. אם מדברים על אפליקציית Biztalk, יתכן מצב שכתוצאה מכך יכנסו לנו תהליכים למצב של Suspended.
  • ביצועים נמוכים-System.Diagnostic.Trace.Component יודע לכתוב כ 2000-8000 כתיבות בשניה (כאשר DebugView Event Capture במצב Enabled)
  • כל שינוי ב tracing configuration דורש אתחול של האפליקציה (לרוב..). בפרויקט ביזטוק נדרש לאתחל את ה Host Instance- לפעמים זה לא מתאפשר בסביבת ה Prod.
לכן, הפתרון המומלץ:

Event Tracing For Windows-ETW- תשתית לוגים סקאלאבילית שמערכת ההפעלה מספקת לנו (מאז שנת 2000). החסרון זה שלא קל לעבוד מול התשתית הזאת, אך יש המון דוגמאות באינטרנט כיצד לעבוד מול התשתית.

אז מה התשתית מספקת לנו:
  • בניסוי שנערך על מחשב עם 4 ליבות, התשתית כתבה כ 1.4 מיליון!!! של Trace event לשניה!
  • כל שינוי ב tracing configuration לא דורש אתחול של האפליקציה
  • צריכה נמוכה של משאבי CPU- התשתית משתמשת בפתרון Buffering שממומש על גבי הKernel, שכותב באופן א-סינכרוני לדיסק ע"י Thread נפרד.
להלן בדיקות הביצועים שנעשו על גבי כל 4 התשתיות שציינתי:

הבדיקה נעשתה ע"י הרצה של מטודה שכותבת ל Trace רשומה אחת, ואותה הריצו בלולאה 100,000 פעמים. בסיום ההרצה התשתיות היו צריכות לספק קובץ לוג מבוסס Text


אפשר לראות בבירור את השימוש ב ETW עבור מערכות High-Performance.


תהנו!

יום חמישי, 1 באוגוסט 2013

בדיקות אוטומאטיות באמצעות BizUnit

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

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

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

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

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

את כל ה step נכתוב כמובן בתוך פונקציית בדיקה רגילה- [TestMethod]

שלב 1-נגדיר TestCase שהוא בעצם מייצג את הסנריו שלנו:

public class CarShop
 var totalItemsTest = new TestCase { Name = "my test case" };
 string dbConnectionString = @"Data...";

שלב 2- מחיקת כל הקבצים (במקרה שלנו קבצי XML) מהנתיב שבו נוצר הקובץ שאנחנו רוצים לבדוק (למשל לאחר ה send-port)


DeleteStep deleteStep = new DeleteStep();
            deleteStep.FilePathsToDelete = new Collection<string> { @"c:\out\*.xml" };
            totalItemsTest.SetupSteps.Add(deleteStep);

שלב 3- מחיקת רשומות מה DB שלנו (במקרה הזה SQL SERVER)


     var dbTestStep = new BizUnit.TestSteps.Sql.DbQueryStep();
            dbTestStep.ConnectionString = dbConnectionString;

            dbTestStep.SQLQuery = new SqlQuery
            {
                RawSqlQuery = @"Delete From SomeDb.dbo.table; select 1;"
            };

            dbTestStep.NumberOfRowsExpected = 1;
            dbTestStep.DelayBeforeCheck = 10;

            totalItemsTest.ExecutionSteps.Add(dbTestStep);


שלב 4- בעצם כאן מתחילים את הבדיקה עצמה- העברת קובץ מתיקיית מקור ליעד ( לתיקייה שהביזטוק יקרא ממנה)
     
            var moveTeststep = new CreateStep();
            moveTeststep.CreationPath = @"c:\destination";

            var dataLoader = new FileDataLoader();

            dataLoader.FilePath = @"c:\fromLocation";
            moveTeststep.DataSource = dataLoader;

            totalItemsTest.ExecutionSteps.Add(moveTeststep);

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

var waitTestStep = new DelayStep();
            waitTestStep.DelayMilliSeconds = 10000;
            totalItemsTest.ExecutionSteps.Add(waitTestStep);


שלב 6- נתשאל רשומה ב DB ונבדוק אם קיבלנו מה שציפינו לקבל:

     var dbSelectTestStep = new DbQueryStep();

            dbSelectTestStep.ConnectionString = dbConnectionString;
            dbSelectTestStep.SQLQuery = new SqlQuery
            {
                RawSqlQuery = @"select top(1) from tbl where cell='11' "
            };

            dbSelectTestStep.NumberOfRowsExpected = 1;
            dbSelectTestStep.DbRowsToValidate = new Collection();
            DbRowToValidate row = new DbRowToValidate();
            DbCellToValidate cell = new DbCellToValidate();
            cell.ColumnName = "colName";
            cell.ExpectedValue = "12312";
            row.Cells.Add(cell);
            dbSelectTestStep.DbRowsToValidate.Add(row);
            totalItemsTest.ExecutionSteps.Add(dbSelectTestStep);

שלב 7- נבדוק שקיבלנו קובץ XML אחד בתיקיית ה OUTPUT שלנו, ושהוא בנוי על פי סכמה מסויימת:


      var validatingFileStep = new FileReadMultipleStep()
            {
                DirectoryPath=@"c:\out",
                SearchPattern="*.xml",
                Timeout=1000,
                ExpectedNumberOfFiles=1
            };

            var validation = new XmlValidationStep();
            var schemaSummary = new SchemaDefinition
            {
                XmlSchemaPath=@"c:\schema.xsd",
                XmlSchemaNameSpace="http://Schemas/namespace"
            };

            validation.XmlSchemas.Add(schemaSummary);

שלב 8- נבדוק שקיבלנו ב XML ערכים כפי שציפינו - על ידי XPATH


      var xpathStep = new XPathDefinition() 
            {
            
            Description="type",
            XPath="/*[local-name().....",
            Value="expected value"
            };



            validatingFileStep.SubSteps.Add(validation);
totalItemsTest.ExecutionSteps.Add(validatingFileStep);

ובשלב האחרון פשוט מריצים את כל ה STEP'S:

       var bizUnit = new BizUnit.BizUnit(totalItemsTest);
            bizUnit.RunTest();

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

בדיקות מהנות!

יום שבת, 13 ביולי 2013

Most important Design Patterns

לאחרונה יצא לי לרענון את הראש עם כמה מה-Design patterns היותר נפוצים.
אני אציג את הרשימה של ה Design pattens שלדעתי כדאי להכיר, וכמובן להשתמש בעת הצורך:
  1. Singleton- אני חושב שאין מתכנת שלא יצא לו להשתמש לפחות פעם אחת בתבנית העיצוב הנ"ל. נשתמש כאשר אנחנו צריכים לייצר רק instance אחד של Class במערכת. יש שני מימושים ליצירת Singleton. האחד יצירה סטטית של Singleton- המימוש המועדף, אך החסרון בו זה שאין שליטה על תזמון יצירת האובייקט. ויש את האפשרות השניה- יצירת Singleton הקלאסי- כאשר המתכמת מחליט מתי האובייקט נוצר, אך במימוש זה יש להיזהר מבעיית ה multy-thrading. לעוד פרטים

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

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

  4. Builder- מחלקה שיוצרת עבור המשתמש את האובייקט (בד"כ מורכב). ההבדל מתבנית Factory זה שב Factory יוצר את האובייקט בקריאה יחידה (מעין מעטפת לבנאי), בעוד ה Builder יודע ליצור את האובייקט עם כל הפרמטרים האפשריים. לדוגמא: אם המשתמש רוצה מכונית חדשה, ה Factory יחזיר "הונדה" או "טויוטה". אך ה Builder יחזיר "הונדה קבריולט עם מנוע 6 צילינדרים" או "טויוטה 4 דלתות ו ארבע צילינדרים". לעוד פרטים בפוסט הנפרד שלי על ה Builder design pattern

  5. Observer- כאשר אנחנו רוצים לקבל נוטיפיקציה כאשר אובייקט כלשהו משנה את המצב שלו. יש כאן יכולת לבצע קשר של יחיד- רבים, כאשר מספר רב של אובייקטים יכולים "להרשם" לשינוי באובייקט היחיד. יש דוגמאות רבות: למשל אפליקצייה בורסאית שרשומה לאובייקט מנייה, וכל שינוי במחיר המניה שולח נוטיפיקציה לכל האפליקציות שרשומות אליה.  עוד דוגמא זה כאשר קוראים נרשמים לקבל נוטיפקציה מבלוג מסויים, וכאשר מתווספת רשומה חדשה לבלוג, כולם מקבלים נוטיפיקציה שהתווספה רשומה חדשה. צריך לשים לב לאופן המימוש: יש את האפשרות הקלאסית של מימוש באמצעות Collection שנמצא באובייקט שנרשמים אליו, ויש את האפשרות שה CLR מאפשר לנו- על ידי שימוש ב event ו delegate. לעוד פרטים

  6. Template method- כאשר יש לנו אלגוריתם שמבצע מספר פעולות. כאשר אנחנו נרצה שפעולה מסויימת (או יותר) יהיו שונות ממימוש למימוש, אנחנו נשתמש ב pattern הזה. להבדיל מ strategy, לא כל האלגוריתם שונה אלא פעולה אחת בו שונה. למשל אלגוריתם שמבצע כמה פעולות, ובסוף שומר את התוצאה. שמירת התוצאה יכולה להיות לקובץ pdf, text או אחר. שמירת התוצאה תהיה שונה ממימוש למימוש, ומי שיחליט על אופן שמירת התוצאה יהיה מי שקורא לאלגוריתם.. לעוד פרטים

  7. Iterator- כאשר אנחנו רוצים לעבור על collection מסויים (למשל לולאת foreach), אנחנו חייבים לממש את ה pattern. למזלנו כל ה collections ב .NET   מממשים אותו. אם יש לנו אובייקט מורכב, שאנחנו רוצים להחליט איך ה iterator יעבוד, אז אנחנו צריכים שהאובייקט יממש את IEnumerable. לעוד פרטים

  8. Adapter- מתאים לעבודה בעיקר במערכות "מדף" שאנחנו לא רוצים לפתוח את הקוד ולשנות את המימוש. נעבוד כאשר נרצה שאובייקט אחד יעבוד מול אובייקט שני, ללא שינוי בקוד של אף אחד מהאובייקטים. ניצור מחלקת Adapter שיורשת מאובייקט אחד, ומממשת interface של אובייקט אחר, וכך יכולה "לחבר" בין שני האובייקטים. לעוד פרטים

  9. Proxy- ה Proxy נותן לנו Interface לשימוש באובייקטים אחרים על ידי יצירת מחלקה שעוטפת את אובייקט. המחלקה העוטפת יכולה גם להוסיף פונקציונאליות נוספת לאובייקט, ללא שום שינוי בקוד האובייקט. דוגמאות שימושיות - הוספת מעטפת אבטחה לשימוש באובייקט, יצירת API נוח יותר לשימוש באובייקט, מתן גישה נוחה ליצירת אובייקטים מורכבים. ועוד.. לעוד פרטים

  10. Dynamic Proxy- כמו הסעיף הקודם, אך באופן דינמי ע"י חיפוש Attribute מתאים.  פירטתי בפוסט הבא: Dynamic Proxy

  11. Decorator- כאשר נרצה להוסיף פונקציונאליות, או מצב חדש לאובייקט קיים, בלי לשנות את הקוד של האובייקט. למשל יש לנו אובייקט עוגה, ובעתי כנראה שנרצה להוסיף לעוגה קצפת, דובדבנים, ריח... אם לא היינו משתמשים בתבנית העיצוב, כנראה שהיה לנו אובייקט מסוג "עוגת דובדבנים", "עוגת קצפת", "עוגת קצפת עם דובדבנים", ותחשבו עוד כמה אופציות...  זהו pattern מאוד נפוץ ושימושי. לעוד פרטים
תהנו!

יום שישי, 12 ביולי 2013

Dynamic Proxy - Design Pattern

האמת, זה אחד ה Pattern שיותר אהבתי.
אז מה זה? כאשר אנחנו כותבים רכיבים תשתיתיים, ה Pattern עוזר לנו, במתן תשתית נוחה למשתמשים לשימוש בקוד שכתבנו כ Proxy לפעולות שונות בדרך מאוד נוחה.
למשל- אנחנו רוצים שכל מי שרוצה שהפונקציה שלו תהיה טרנזקציונית- לא יצטרך לממש את הטרנזקציה, אלא יוסיף מעל הפונקציה שלו Custom Attribute שאנחנו כתבנו (למשל [Transacion]). אפשר לחשוב על עוד מיליון דברים שאנחנו יכולים לממש בתשתית שלנו. למשל כתיבה ללוגים, הצגת משך זמן ביצוע הפונקציה, או כל דבר אחר.
אנחנו כמפתחי תשתיות צריכים לדאוג למשתמשים שלנו, שלא יצטרכו לממש הכל. אנחנו נספק להם תשתית נוחה.

אז איך זה עובד:

קודם כל ניצור Custom Attribute. כאן ניצור Benchmarks, כאשר המשתמש ירצה לדעת כמה זמן לקח לפונקציה לפעול, הוא ישתמש ב Attribute הזה.

 [System.AttributeUsage(System.AttributeTargets.Class
                  | System.AttributeTargets.Struct | System.AttributeTargets.Method)]
        public class Benchmarks : Attribute { }


נכתוב את ה Class הדינמי הבא, שמשתמש ב Reflection כדי למצוא את סוג האובייקט שהפעיל את הפונקציה, הוא מפעיל את הפונקציונאליות שקבענו (על פי ה Attribute), ואז עושה Invoke למתודה המקורית שהמשתמש הפעיל.

          public class Wrapper<T> : DynamicObject
        {
            private readonly T _wrappedObject;

            public Wrapper(T obj)
            {
                this._wrappedObject = obj;
            }

            public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
            {           
                //get the method that invoked
                MethodInfo method = _wrappedObject.GetType().GetMethod(binder.Name);

                //create a new instance of the object that invoked
                object o = Activator.CreateInstance(_wrappedObject.GetType());

                // Check if the method has the benchmarks attribute
                if (method.GetCustomAttributes(typeof(Benchmarks)).Count() > 0)
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();

                    result = _wrappedObject.GetType().InvokeMember(binder.Name, System.Reflection.BindingFlags.InvokeMethod, null, o, args);
                  
                    sw.Stop();

                    Console.WriteLine(binder.Name + ": " + sw.ElapsedMilliseconds + "ms");
                }
                else
                {
                    result = _wrappedObject.GetType().InvokeMember(binder.Name, System.Reflection.BindingFlags.InvokeMethod, null, o, args);
                }


                return true;
            }
        }

ניצור Factory שיוצר למשתמש את האובייקט.

   // Factory.cs
        class Factory
        {
            public static Factory Instance = new Factory();

            private Factory() { }

            public dynamic New<T>(params object[] args) where T : class
            {
                T obj = (T)Activator.CreateInstance(typeof(T), args);
                return new Wrapper<T>(obj);
            }
        }

זהו האובייקט של המשתמש

        class MyClass
        {
            [Benchmarks]
            public void doSomething()
            {
                System.Console.WriteLine("My .NET Diary Blog");
            }

            [Benchmarks]
            public void doSomething2(string text)
            {
                System.Console.WriteLine(text);
            }
        }

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

      static void Main(string[] args)
        {
            var myClass = Factory.Instance.New();
            myClass.doSomething();
            myClass.doSomething2("By Eyal Chapnik");
        }
והפלט:


שבוע טוב!

יום רביעי, 26 ביוני 2013

טיפים לקינפוג IIS

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

הטיפים שאציג נאמרו ע"י מרטין שוורצמן, מומחה IIS במיקרוסופט.
  1. החל מ IIS7 כשבוחרים Managed pipeline ב Application pool יש לבחור ב Integrated. זה מאפשר עבודה יותר מהירה ב .net וביצועים יותר טובים. Classic נמצא לשם תאימות לאחור עם IIS6
  2. נשים אפליקציות באותו application pool רק כאשר האפליקציות קשורות אחת לשנייה ע״י כך שחולקות מידע משותף.
  3. נבודד אפליקציה ל application pool משלה כאשר הקוד לא מושלם והוא גורם לדליפת זכרון, במצב זה נגביל את כמות הזכרון שמוקצה לאפליקציה.
  4. לא עושים iisreset. אם כבר בחרנו לעשות, אז לעשות דרך הממשק.
  5. אם יש במערכת רכיב שמשתמש ב 32 ביט ורכיב אחר שמשתמש ב 64 אז צריך להפריד את הרכיבים לapplication pool שונים. אין סיבה שרכיב 64 ירוץ על 32 ביט.
  6. IIS Compresion- פוסט של מרטין המסביר בהרחבה איך לבצע
  7. בקובץ global.asax ב application_start יש להוסיף: System.Net.ServicePointManager.DefaultConnectionLimit=int.MaxValue
תקנפגו נכון!

יום שני, 17 ביוני 2013

Ajax call is not working when script is on .js file

בעת ביצוע refactoring לדף ה view שלנו בפרוייקט ה mvc, העברתי את פונקציות ה JavaScript לקובץ js נפרד, שיושב בתיקיית הסקריפטים בפרוייקט.
ברגע שביצעתי זאת, כל פונקציות ה js כמובן נקראו, אבל קריאות ה Ajax לשרת הפסיקו לפעול.
אחרי חיפוש קצר מאוד בgoogle מצאנו את הבעיה ואת הפתרון
הבעיה היתה בשימוש ה relative path שבקובץ הjs. אם מעבירים את הפונקציה לדף ה view עצמו, הקריאה תתבצע כמו שצריך.
שימו לב, בעיה כזאת יכולה גם לקרות כאשר מעבירים את הפרוייקט לסביבת production, כאשר האתר לא רץ על ה localhost..

בהתחלה פונקציית ה js שלי נראתה כך:
$Post('Url.Action("ActionName","Controller")',.........

הפתרון הוא ליצור משתנה js בדף ה view שלנו, ולהשתמש איתו בקובץ ה js:

var myUrl='Url.Action("ActionName","Controller")';

קובץ ה js:

$Post(myUrl,.........

וזה יעבוד.

תהנו!

יום ראשון, 2 ביוני 2013

אני לא מכיר מה זה Switch- פתרון בעזרת Reflection

בהמשך לפוסט הקודם שלי "אני לא מכיר מה זה Switch- Case", היום נלמד עוד דרך לכתוב קוד ללא switch-case, והפעם באמצעות Reflection ובעזרת יצירת Custom Attributes . גם כאן Evgeny Borisov  לימד אותי את הפתרון הנכון.

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

ניצור Custom Attribute שנקרא לו "FoodType":

   
    [System.AttributeUsage(System.AttributeTargets.Class
                      | System.AttributeTargets.Struct)]
    public class FoodType : System.Attribute
    {
        private int dbCode;

        public FoodType(int dbCode)
        {
            this.dbCode = dbCode;
        }

        public int GetDBCode()
        {
            return dbCode;
        }
    }

ניצור interface:
 
    public interface FoodHandler
    {
        void handleFood();
    }
עכשיו קטע מעניין: כל מחלקה שמבצעת לוגיקה עבור סוג מאכל מסוים, תממש את ה interface שיצרנו, ותקבל את ה Attribute שיצרנו, וכמובן שנחליט מה הקוד המתאים של המחלקה:
 
    [FoodType(1)]
    public class MeatHandler : FoodHandler
    {
        public void handleFood()
        {
            System.Console.WriteLine("40 lines of code handling meat");
        }
    }


    [FoodType(2)]
    public class FishHandler : FoodHandler
    {
        public void handleFood()
        {
            System.Console.WriteLine("60 lines of code handling fish");
        }
    }

עכשיו נממש פונקציה, שבעת "הרמת המערכת" היא נגשת ב Reflection לקבצי ה dll, מחפשת שם את כל ה class שקיבלו את ה attribute שיצרנו, ויוצרת dictionary  שבו יש מיפוי של קוד מאכל - וה instance המתאים של ה class:

 
   public class MainHandler
    {
        Dictionary<int, FoodHandler> dic = new Dictionary<int, FoodHandler>();

        public MainHandler()
        {
            //get the full path
            string currentDirectory = Directory.GetCurrentDirectory();
            
            //get all the dll
            var files = Directory.GetFiles(currentDirectory, "*.dll");
         
            foreach (string file in files)
            {  
                //load each assembly
                var assembly = Assembly.LoadFile(file);

                foreach (var type in assembly.GetTypes())
                {
                    if (!type.IsClass || type.IsNotPublic) continue;

                    //if the class has our custom attribute
                    var foodTypeClass = type.GetCustomAttributes(typeof(FoodType), true);

                    FoodType foodType = (FoodType)foodTypeClass.FirstOrDefault();

                    if (foodType!=null)
                 {
                        int dbCode=foodType.GetDBCode();

                        //create new instance
                        var instance = (FoodHandler)Activator.CreateInstance(type);

                        //add the instance and his dbCode to the dictionery
                        dic.Add(dbCode, instance);
                     
                 } 
                }
                
            }
        }

וזהו! יצרנו את התשתית!

עכשיו כל מתכנת שירצה להוסיף לוגיקה עבור מאכל נוסף (או handler), הוא רק צריך לממש את ה interface, ולתת למחלקה Attribute מתאים.

איך מתבצעת קריאה לדוגמא (ללא שימוש ב Switch):

 
  //init the main handler
  MainHandler handler = new MainHandler();

  //handle the food by his db code (it could be also by his name..)
  handler.handleFood(2);

תהנו!

את קובץ ה cs אפשר להוריד מכאן

יום שלישי, 21 במאי 2013

Code smells - קוד מסריח

כולנו עושים refactoring לקוד שכתבנו (מקווה), חלקנו מבצעים code review.
יש כמה סימנים לזיהוי מקומות ״מסריחים" שאנחנו צריכים לטפל בהם (code smell) ולהלן העיקריים שבהם:

  1. הערות-צריכות לתאר ״למה״ ולא ״מה״. ה״מה״ צריך להיות מובן משמות המשתנים והפונקציות.
  2. פונקציות ארוכות - פונקציות קצרות יותר קל להבין, לקרוא, לתקן במידת הצורך. פונקציה אחראית על פעולה לוגית אחת (תפקיד אחד). קצרו את הפונקציות בהתאם.
  3. מספר רב של פרמטרים - ככל שלפונקציה יש מספר רב של פרמטרים, כך היא הופכת למסובכת יותר. תגבילו את מספר הפרמטרים, או השתמשו באובייקט שמרכיב כמה פרמטרים.
  4. שכפול קוד - אל תחזרו על עצמכם. שכפול קוד הוא אחד הטעויות הכי גדולות..
  5. תנאים מורכבים - המנעו מקינונים ארוכים ככל האפשר. במיוחד כאשר יש אפשרות שהקינונים יגדלו עם הזמן. במידת הצורך השתמשו בתבניות העיצוב: decorator, strategy, state.
  6. שימוש בswitch - תקראו את הפוסט שלי "אני לא יודע מה זה switch״
  7. העברת בוליאני כמשתנה לפונקציה - מצביע שלפונקציה יותר מתפקיד אחד.
  8. מחלקות ארוכות - כמו פונקציות ארוכות, מחלקות ארוכות קשות לקריאה, הבנה, ותחזוקה. אם המחלקה ארוכה תבדקו אם היא מכילה יותר מדי תפקידים ואחריות. אם כן תפצלו את המחלקה למספר מחלקות קצרות.
  9. השם של הפונקציה מורכבת מה type - עדיף שהשם של הפונקציה לא יהיה מורכב מה type שהיא מחזירה. זה מכריח אותנו לשנות את שם הפונקציה אם נשנה את ה type שהיא מחזירה
  10. שמות משמעותיים לפונקציות - האם השם מתאר את מה שהפונקציה עושה? האם מישהו שלא מכיר מה הפונקציה עושה, יכול להבין מה היא עושה רק על פי השם?
  11. שמות קונסיסטנטים - תצמדו לסטנדרט מסויים בכתיבת שמות משתנים ופונקציות
  12. שימוש ב ref ו out - אם אתם צריכים שהפונקציה תחזיר יותר ממשתנה אחד השתמשו ב class members . אם אין קשר בין המשתנים החוזרים, אז הפונקציה מבצעת יותר מתפקיד אחד.
  13. קוד ללא שימוש - מוחקים קוד שאין בו שימוש. זו הסיבה שיש source control.
  14. להימנע מתכנון של משהו שאף פעם לא יקרה - תכתבו קוד שאמור לפתור את הבעיה של היום. ברוב המוחלט של המקרים, אין צורך בגנריות כאשר לא צריך.. זה מסבך את הקוד, ופחות קריא (אלא אם באמת יש דרישה לגנריות)
  15. פרמטרים ללא שימוש - המנעו מלכתוב אובייקטים עם פרמטרים שאף אחד לא משתמש בהם.
  16. מימוש interface זהה - אם יש שני מחלקות שזהות במרכיבים הפנימיים שלהם, אז שיממשו אותו interface 
  17. מחלקות נתונים - המנעו מכתיבת מחלקות שמכילות רק נתונים. מחלקות צריכות להכיל גם פונקציות שעובדות על הנתונים.
  18. הורשה לא רצויה - אם אתם יורשים ממחלקה כלשהי, אבל אין שום שימוש בפונקציונליות שההורשה מספקת אז כנראה שלא מתאימה פה הורשה.
  19. המעיטו בחשיפת מטודות - המחלקות צריכות להיות כמה שפחות חשופות. צריכה להיות סיבה ממש טובה אם יש מטודה שהיא public.
  20. מטודות שמשתמשות במחלקות אחרות - אם המטודה שכתבתם משתמשת באופן רציף בפונקציונליות של מחלקה אחרת, כנראה שמקומה במחלקה שהיא משתמשת בה, ולא מקומה הנוכחי. 
  21. מחלקות בשימוש מועט - כל מחלקה נוספת בפרויקט גורמת לקוד שלנו להיות יותר מורכב אם יש לנו מחלקה שלא עושה הרבה, אז בדקו אם אפשר לאחד אותה למחלקה אחרת
  22.  Shotgun surgery - כל שינוי קטן בקוד שלכם גורם לבעיות בכמה מחלקות אחרות? כנראה שיש לכם בעיה. תנסו לתקן את הקוד. 
  23. פנייה לכלי בצד שלישי - כשפונים לספק חיצוני (למשל oracle) תעטפו את הפנייה במחלקה דקה כלשהי, כי במידה ותשנו את הספק (למשל sql) אז נצטרך לתקן רק במקום אחד.
מבוסס על Smells to refactoring
תהנו!

יום שלישי, 14 במאי 2013

Integration Patterns catalog

בהמשך לפוסטים הקודמים שלי: Scatter-Gather ו Claim Check, החלטתי להציג תרשים שמצאתי, ובו מפורטים באופן די טוב את סוגי ה Integration Patterns. 
קיימות עוד תבניות עיצוב שלא מופיעות בתרשים. (עם הזמן מפתחים עוד..)

תבחנו את עצמכם כמה אתם מכירים:

 תהנו!

יום שבת, 11 במאי 2013

WPF - שימוש ב Converter

שימוש מאוד נפוץ ב WPF הוא שימוש ב Converter.
במידה ואנחנו רוצים לעשות Bind לשני Properties שאינם בעלות אותו ה Type אנחנו חייבים להשתמש ב converter.
לדוגמא: לעשות Bind בין אורך הטקסט שהוכנס לבין צבע פקד ה TextBox (נראה פתרון לדוגמא זו).


 אפשר לחשוב על עוד המון אפשרויות bind יותר מורכבות (למשל בין התכונה IsChecked של CheckBox לבין Visibility של תמונה כלשהי.

נראה את הדוגמא הבאה:

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



ניצור Class חדש, שהוא יהיה בעצם המחלקת Convertor שלנו. הוא יממש את IValueConverter.

יש 2 פונקציות. הראשונה ממירה מערך המקור לערך היעד (ואותה נממש). הפונקציה השניה ממירה מערך היעד לערך המקור. פונקציה זו רלוונטית רק כאשר מבצעים Binding בעל Mode מטיפוס TwoWay או OneWayToSource.


    class Converter: IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string text;
          
            text = System.Convert.ToString(value);


            if (text.Length < 4)
            {
                return Brushes.Red;
            }
            else
            { 
                return Brushes.Green;
            }
            
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

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

ה Xaml שלנו יראה כך:
    <Window x:Class="first_converters.WindowTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:first_converters"
        Title="WindowTest" Height="300" Width="300">
    <Window.Resources>
        <local:Converter x:Key="length_converter"/>
    </Window.Resources>
    <Grid>
        <TextBox Name="txt" HorizontalAlignment="Left" Height="23" Margin="86,122,0,0" 
                 TextWrapping="Wrap"  VerticalAlignment="Top" Width="120" 
                 Background="{Binding ElementName=txt,Path=Text,Converter={StaticResource length_converter}}"/>
    </Grid>
</Window>

מה אנחנו רואים כאן:

קודם כל נוסיף Resource ל Class שהרגע כתבנו ונגדיר לו Key.
וכעת נעשה Bind-מהטקסט ב TextBox אל הצבע של ה TextBox.

והתוצאה:





תהנו!

יום רביעי, 1 במאי 2013

Create different shape to my wpf page

כשאנחנו מפתחים ב WPF, אנחנו לא חייבים שהWindow (או ה Page) יהיו בעלי צורה בנאלית מרובעת:


ב WPF יש אפשרות ליצור את ה Window שלנו באיזה צורה שנרצה:

לצורך העניין יש לי תמונה של דמות אדם, ואני ארצה שה Window שלנו יהיה בצורה של הדמות. כמובן שיכלנו כל צורה (עיגול, מתומן, או כל צורה שנרצה..) :

האפליקציה הסופית שלנו תיראה כך: (האפליקציה שלנו זה הצורה של הבן-אדם. ה screenshot על רקע notepad)

 אז איך זה נראה ב XAML:

<Window x:Class="AmazingWindowDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" 
 WindowStyle="None" AllowsTransparency="True" Background="Transparent">
    <Grid MouseLeftButtonDown="Grid_MouseLeftButtonDown_1">
        <Image HorizontalAlignment="Left" Height="255" Margin="139,68,0,0"
  VerticalAlignment="Top" Width="294" Source="man.png"/>
  <Button Content="Button" HorizontalAlignment="Left"/>
    </Grid>
</Window>

אז מה עשינו פה?
 החלק החשוב זה החלק של ההגדרה של AllowsTransparency,WindowStyle,Background. ואז פשוט ה window "עוטף" כל צורה או תמונה שתגררו לטופס(במקרה שלנו זה man.png)

חשוב לא לשכוח להירשם לevent של MouseLeftButtonDown כדי שהמשתמש יוכל להזיז את החלון..
המימוש של הפונקציה:
  private void Grid_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        }

תהנו!

יום רביעי, 24 באפריל 2013

WCF Concurrency Mode

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

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

האופציות הקיימות לתכונה זו הם:


Multiple- ברירת המחדל. האובייקט של השירות שנוצר בשרת הוא multy-threaded. אנחנו צריכים לטפל בסנכרון בין ה thread-ים.
Single- האובייקט של השירות שנוצר בשרת הוא single threaded. כל הקריאות שיגיעו לשרת בזמן שהשירות עסוק- ימתינו עד סיום הטיפול בבקשה הראשונה, או עד timeout מסויים.
Reentrant- השירות הוא single אבל אם מתבצעת קריאה מהשירות לשירות חיצוני אז הוא מאפשר ל thread חדש להיכנס לשירות. זה נועד למנוע מצב של deadlock.

התכונה מוגדרת מעל הגדרת השרות. לדוגמא:

[(ServiceBehavior(ConcurrencyMode=Concurrency.Reentrant]
Public class SomeService: ISomeService

תהנו!

להשתמש ב int.Parse או Convert.ToInt32 ?

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

כן, יש הבדל:

Convert.ToInt32 קורא ל int.Parse, לאחר שהוא מבצע בדיקה אם הערך ששלחנו לו הוא null. אם הערך null אז הוא מחזיר 0. - מהבחינה הזאת קל לנו לראות יתרון קל בביצועים לטובת int.Parse . כמובן שזה נהיה משמעותי רק אם אנחנו קוראים לפונקציה המון פעמים.

אז למה לא תמיד להשתמש ב int.Parse?
  1. אם לא שלחנו לו string חוקי אז הוא זורק  ArgumentNullException ואז אנחנו צריכים לטפל ב exception שזה טיפול יקר (אולי בעצם נרצה שיזרוק- או שנשתמש ב TryParse?).
  2. int.Parse מקבל רק string,לעומת Convert שמקבל כל אובייקט שמממש IConvertible.
יש לשים לב ששתי הפונקציות יכולות להחזיר  FormatExeption לדוגמא אם שלחנו להם "123.45" , או שהם יכולות להחזיר OverflowExeption לדוגמא אם הstring  ששלחנו לו יותר גדול מ int.MaxValue

במקרה זה נשתמש ב int.TryParse שלא זורק אקספשיין אלא מחזיר במשתנה out אם הצליח או לא, אבל במידה ושלחנו string חוקי, ואין מצב שיזרק אקספשיין אז int.Parse יהיה יותר מהיר. אבל אם כבר אנחנו מעוניינים בביצועים יותר מהירים  - עשיית boxing ל int זוהי הדרך הכי מהירה להפוך string ל int.

אז בקצרה: מתי נשתמש בכל פונקציה-

אם x הוא boxed int אז הכי מהר להשתמש ב (int) 

אם x מספר וואלידי אז נשתמש ב int.Parse

אם לא בטוח ש x מספר וואלידי אז נשתמש ב int.TryParse כי אז לא נצטרך לטפל ב exceptions

אם אנחנו לא יודעים מה הוא x אז נשתמש ב Convert.ToInt32

תהנו!!!

יום שלישי, 23 באפריל 2013

מה זה Rest? או Restful Service

מי שבעצם הגדיר את Rest או Representational State Transfer הוא Roy T. Fielding (היה מבין מעצבי פרוטוקול HTTP). הגדיר זאת כסגנון ארכיטקטוני- שזהו בעצם דרך אבסטרקטית לבטא את הרעיון מאחורי הארכיטקטורה. סגנון ארכיטקטוני מגדיר את המאפיינים של הארכיטקטורה שתשתמש בסגנון הנ"ל... (Client-Server זה סגנון ארכיטקטוני).
הוא עוד הוסיף ואמר ש Rest מנסה להביא למינימום את ה latency ואת התקשורת ברשת, ובאותו הזמן למקסם את יכולת הסקלביליות והעצמאות  של הרכיבים עצמם.
הרעיון המרכזי של Rest זה במקום להשתמש בצורות מסובכות (כמו SOAP,RPC) כדי לתקשר עם מערכת אחרת, נשתמש בבקשת HTTP  פשוטה כדי לבצע את התקשורת שדרושה לנו. (מכיוון שלרוב Rest ממומש על Http, כך אתייחס בבלוג..)


 מאפייני Rest חשובים :
  1. מיושם באריטקטורות client-server
  2. למרות ש Rest פותח במקביל ל Http1.1, אפשר לממש את הסגנון הארכיטקטוני Rest  לא רק על גבי Http.
  3. הContext של הפנייה הוא Stateless
  4. הוא Cacheable- כלומר ניתן לעשות caching של תשובות השרת בזיכרון של מחשב הלקוח

העקרון המרכזי ב Rest הוא לתת לכל משאב ID. ב Web יש רעיון עבור IDs שנקרא URI- שזהו namespace גלובלי, והשימוש בו מבטיח לתת לכל משאב ID ייחודי גלובלי (קחו למשל דוגמא כתובת של אתר אינטרנט...).

Rest משתמש ב Http עבור כל פעולות ה CRUD שהם (Create/Read/Update/Delete), אך כמובן שהוא לא נצמד אליהם ויכול להשתמש בפעולות שהם Non-CRUD

Create- זוהי מתודת ה PUT - כלומר הוספת משאב חדש למערכת
Read- זוהי מתודת ה GET - קבלת משאב מהמערכת
Upadte- זוהי מתודת ה POST- עדכון המשאב במערכת
Delete - מחיקת המשאב מהמערכת

ב Rest אנחנו ניגשים למשאב ולא לשירות. המשאבים שלנו צריכים להיות מוגדרים היטב.

לדוגמא ניקח את הסנריו הבא:

יש לנו משב לקוחות ומשאב הזמנות (של לקוחות). השרות שלנו חושף רק את הפונקציות שמצוינות להלן:


בואו נראה איך המבנה יראה על פי Rest:

המשאבים שלנו יצטרכו לממש את ה Interface שנגדיר (בתרשים הבא אני אציג רק את המטודות שהמשאב משתמש (לצורך פשטות), למרות שבפועל הוא צריך להכיל את כל המטודות מה interface)




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

אם נשתמש ב Web Service וב SOAP הבקשה תראה כך:

<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
 <soap:body pb="http://www.acme.com">
  <pb:GetOrder>
   <pb:OrderID>12345</pb:OrderID>
  </pb:GetOrder>
 </soap:Body>
</soap:Envelope>

וכנראה שגם יחזור לנו SOAP Response עם התשובה..

וע"י שימוש ב Rest הבקשה תיראה כך:

http://www.acme.com/Orders/12345

שתשלח לשרת ע"י בקשת HTTP GET,  ויחזור לי HTTP RESPONSE  עם התשובה.

למשל אם נרצה לקבל את כל הלקוחות:

http://www.acme.com/Customers

או למחוק לקוח מסויים (ע"י שליחת HTTP DELETE)

http://www.acme.com/Customers/1234

או לקבל את כל ההזמנות של לקוח מסויים:

http://www.acme.com/Customers/12345/Orders


אפשר גם לעשות שימוש בבקשות קצת יותר מורכבות:

http://www.acme.com/OrderDetails?ProductName=X&Year=2000

בד"כ בתשובה יחזור לי XML, אך בשונה מ SOAP, זה לא חייב להיות XML. זה יכול להיות גם JSOM,CSV

כלל חשוב שיש לשים לב זה שהגדרת ה URI מחייבת שתהיה אפשרות גישה לכל משאב בהיררכיות.
למשל: אם יש לי גישה ל
http://www.acme.com/Customers/12345/Orders

אז יהיה לי גישה גם ל:

http://www.acme.com/Customers/12345
http://www.acme.com/Customers

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

מתי נשתמש ב REST ומתי ב SOAP?
לזה נקדיש בהמשך פוסט נפרד..

תהנו!

יום ראשון, 21 באפריל 2013

Create session fake for unit test in ASP MVC

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

את הרשאות המשתמש אני שומר במשתנה מסוג Session, כאשר בצד שרת (או בController) נבדק סוג ההרשאה, ועל פי סוג ההרשאה ממשיכים הלאה...

הכל הלך טוב ויפה, עד שהתחלתי לכתוב Unit tests. כשמריצים בדיקות, צריך לדמות את ה Context של ה HTTP.

אחד הדברים שהיה לי יותר קשה (לקח לי כמה שעות למצוא איך עושים זאת) זה לדמות את משתני ה Session ב Context (וכל זאת ללא שום שימוש בספריה חיצונית לבדיקות Unit Test).

אז הנה הפתרון:

נגיד שיש לנו Enum שמבטא את רמת ההרשאות:

    public enum Privilege
    {
        ADMIN,
        READ,
        WRITE,
        NOT_AUTHORIZED
    };

ובואו נאמר שיש לנו בController (נקרא לו SomeController) פונקציה עם הבדיקה הבאה:

    public JsonResult DeleteOrder(int orderId)
    {
        if ((Privilege)Session["Privilege"]==Privilege.ADMIN)
        {
           //TODO
        }
    }


עכשיו, כדי לכתוב Unit test שמדמה גם את אובייקט ה Session נצטרך ליצור את הקונטרולר באופן הבא:

נעשה using לבאים:

using System.Web.SessionItems
using MvcFakes

ונכתוב את הקוד הבא:

        private SomeController GetAdminSomeController()
        {
            SomeController controller = new SomeController();

            //create fake context
            var sessionItems["Privilege"]=Privilege.ADMIN;
            controller.ControllerContext=new FakeControllerContext(controller,sessionItems);

            return controller;
        }

unit test מוצלח!

יום שני, 15 באפריל 2013

למה צריך להבין מה זה Reference Type מול Value Type?

למה אנחנו צריכים להבין מה זה Reference Type ומה זה Value Type?
למה בכלל החברים במיקרוסופט עשו חלוקה ל 2 סוגי Type מרכזיים? יש לזה הצדקה?

אז קודם כל נתחיל בכך שנסביר את ההבדל המרכזי:
Value Type מחזיק את הנתון במקום משלו בזכרון. Reference Type מכיל פוינטר למקום בזכרון שמחזיק את הנתון.



עץ ה Type's

אני חושב שאחת הסיבות העיקריות שביצעו חלוקה כזאת היא מכיוון שלטיפוסי ה Reference הוסיפו שירותים נוספים על ידי הקומפיילר / סביבת זמן ריצה / ביצוע על חשבון ביצועים.
אילו שירותים הם מספקים?
בירושת אובייקטים אנו צריכים תמיכה בפולימורפיזם (שנעשה בהגדרת מטודות וירטואליות). המטודות הוירטואליות מסתמכות על "טבלת מטודות" שמכילות מצביע לאובייקט המכיל את המטודה. המצביע זוהי דוגמא לשירות הנוסף.
עוד שירות נוסף זה היכולת להשתמש ב Reference type בסינכרוניזצית Monitor   ( או C# lock)

חוץ מלקחת בחשבון את ה"שרותים" שהתווספו, אנחנו צריכים לחשוב גם על בעיות ביצועים. כאשר שולחים Value Type כפרמטר לפונקציה, אנחנו מעתיקים את ערכו. תחשבו על הבעיה שנוצרת אם אנחנו קוראים לפונקציה הרבה פעמים בתוכנית שלנו. כמה העתקות מיותרות בזיכרון נעשה.. צריך גם לקחת בחשבון אם אנחנו מעבירים לפונקציה struct מורכב..

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

טבלה מסכמת על ההבדלים בין Value Type ל Reference Type:


Value Types Reference Types
מחזיקים ישירות את הנתונים מחזיקים רפרנס לנתונים
שמורים על ה stack (אלא אם מוגדרים כמשתנים בתוך  Object Class) שמורים על ה heap
לא יכולים להיות null  אלא אם מגדירים אותם כ nullable יכולים להיות null
יש להם ערך דיפולטיבי אין להם ערך דיפולטיבי
אין צורך ב garbage collector, הכל מתבצע במהירות ב runtime ה garbage collector מנקה את הזיכרון 
בהעתקת תוכן של משתנה אחד לשני, שינוי של משתנה אחד לא משפיע על השני בהעתקת תוכן של משתנה אחד לשני, שינוי של משתנה אחד משנה את השני בהתאם
יורשים מ System.ValueType שיורש מ System.Object יורשים מ System.Object
כשמעבירים כפרמטר לפונקציה אז הערך מועתק כשמעבירים פרמטר לפונקציה אז מעבירים את הרפרנס
פונקציית Equal משווה את הערכים פונקציית Equal משווה את הרפרנסים
ממומש כ Struct ממומש כ Class
תהנו!

יום שישי, 12 באפריל 2013

My Builder Design Pattern

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

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

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

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

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

נניח שבפתרון הנאיבי האובייקט שלנו יראה כך:

 public class BankClient
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string City { get; set; }

            public BankClient(string firstName, string lastName, string city)
            {
                FirstName = firstName;
                LastName = lastName;
                City = city;
            }
        }

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

אז מה הפתרון?

נשתמש ב Builder pattern: (אשנה אותו מעט שיתאים לבעיה הנ"ל):

האובייקט לקוח יראה כך:


 public class BankClient
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string City { get; set; }
        }


נצהיר על ה interface של ה Builder:
  interface IBankClientBuilder
        {
            BankClient newInstance();
            BankClientBuilder firstName(string firstName);
            BankClientBuilder lastName(string lastName);
            BankClientBuilder city(string city);
        }


וכך תראה מחלקת ה Builder:
  public class BankClientBuilder:IBankClientBuilder
         {
            private BankClient bankClient;

            public BankClientBuilder()
            {
                bankClient = new BankClient();
            }

            public BankClient newInstance()
            {
                return bankClient;
            }

            public BankClientBuilder firstName(string firstName)
            {
                bankClient.FirstName = firstName;
                return this;
            }

            public BankClientBuilder lastName(string lastName)
            {
                bankClient.LastName = lastName;
                return this;
            }

            public BankClientBuilder city(string city)
            {
                bankClient.City = city;
                return this;
            }

        }

מה יש לנו כאן? הבנאי של ה Builder מאתחל את האובייקט שלנו (במקרה זה אובייקט "לקוח"), ויש לנו פונקציה עבור כל פרמטר. כאן לדוגמא הפונקציות מקבלות string כלשהו, אבל כמובן אפשר לשנות אותם כך שיקבלו enum כלשהו, או אפילו שום פרמטר, ושאנחנו נקבע את הפרמטר. תראו גם איך פישטנו את העניין- אפשר להוסיף עוד פרמטרים בלי לשנות בעצם שום קוד בצד שמבקש לקבל את האובייקט.
איך יראה איתחול האובייקט (או במילים יותר יפות- קבלת האובייקט:)
 static void Main(string[] args)
        {
            BankClient client = new BankClientBuilder().firstName("Eyal").lastName("Cha").city("RL").newInstance();
        }


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

יום חמישי, 21 במרץ 2013

C# Define

למדתי לא מזמן שימוש באפשרות יפה ש#C  מאפשר לנו.

קודם כל אתחיל בסיפור מקרה:
קורה לא פעם שאני מסתכל על קוד שמישהו כתב בעבר, ובקוד כתובים המון מתודות שהשימוש שלהם נכון רק בזמן debug למשל. (למשל כתיבה ל event log לצורך טסט או דיבוג).
אבל כאשר הקוד רץ בסביבת Production אין לנו צורך במטודות, ואף זה משפיע לרעה (כתיבה לא רצויה ל event log משפיעה על ביצועי הדיסק..)

לכן יש לנו את האפשרות הבאה:

#define debug
//#undef debug
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace example
{
    class Program
    {
        static void Main(string[] args)
        {
            #if debug
                Console.WriteLine(1);
            #else
                Console.WriteLine(2);
            #endif
            ForDebug();
        }

        [Conditional("debug")]
        static void ForDebug()
        {
            Console.WriteLine("in debug function");
        }
    }
}
נגדיר בתחילת הדף סמל (במקרה שלנו נקרא לסמל שלנו debug) וכעת בקוד שלנו נוכל לבצע התניות לפעולות אך ורק אם הסמל מוגדר בתחילת הדף. שימו לב שאפשר להגדיר גם על פונקציה שלמה.

ככה למשל לפני ירידה ל production נוכל להסיר את הסמל (ע"י undef), וכעת לא יבוצעו מטודות שלא רצינו שיפעלו בסביבת ה production..

תהנו

יום חמישי, 14 במרץ 2013

Scatter Gather Integration Pattern

Scather Gather זוהי תבנית שימושית מאוד. בתבנית זו שולחים מספר הודעות לשירותים שונים, מחכים לתשובה, ו"מחברים" את התשובות מכל השירותים לתשובה עיקרית אחת.


נלקח מכאן

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

יש כמה דרכים לפתרון הבעיה: (לדוגמא ב Biztalk)

פתרון 1: שימוש ב Parallel Shapes
  1. האורכסטרציה מנויה על ההודעה (בסיפור המקרה שלנו, על זיהוי הלקוח)
  2. שימוש ב Parallel Shape: יהיה לנו מספר Sendport כמספר השירותים, או שנקרא לאורכסטרציה אחרת שאחראית לקרוא לשירות (במקרה שלנו יהיו לנו 3 - משכנתא, הלוואה וחסכונות)
  3. שימוש ב Parallel Shape: יהיה לנו מספר Receive כמספר התשובות שאנחנו מצפים לקבל
  4. ולבסוף שימוש במפה, שהמקור שלה מורכב מ שלושה inputs וה output הוא יחיד (זהו הקובץ XML עם כל התשובות מכל השרותים שנשלח למערכת של נציג השרות)
וזה יראה כך:
נלקח מכאן

פתרון 2: שימוש דינמי ע"י Subscriptions

  1. האורכסטרציה מנוייה על ההודעה (במקרה שלנו כאשר הלקוח מתקשר לבנק ומזדהה)
  2. מתבצע מיפוי להודעה הרצויה, וההודעה נשלחת ל MsgBox
  3. יש מספר SendPorts שמנויים על ההודעה (במקרה שלנו יש 3..). כל SendPort מכיל מפה מתאימה לשירות.
  4. בתוך לולאה (עבור כל ההודעות שאנחנו מחכים לתשובה) מתבצע Listen להודעות תשובה מהשרות (עפ"י קורולציה), ומאגדים את כל התשובות להודעה אחת.
  5. לאחר שקיבלנו את כל ההודעות, שולחים את ההודעה המאוגדת ליעד (במקרה שלנו למערכת נציג הלקוחות)
וזה יראה כך:

scatter scope:

gather scope:
נלקח מכאן

אני אישית מעדיף את הדרך הראשונה לפתרון..

תהנו!