WCF 4 (Windows Communication Foundation) for Beginner – part 12

6 בפברואר 2011

תגיות: ,
5 תגובות


 


ללמוד WCF 4 פרק 12 – Duplex Pattern

 

 

נושאים בפרק 12:


סיכום הפרק הקודם.

מה זה Duplex.

חסרונות ויתרונות בעבודה עם Duplex.

דוגמת קוד.

 

 

 

סיכום הפרק הקודם:


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

 

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

 

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

 

מעבר לכך הזכרנו את המושג Duplex ובפרק זה נרחיב את הנושא.

 

 

מה זה Duplex.


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

 

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

 

 


חסרונות ויתרונות בעבודה עם Duplex.


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

 

 

דוגמת קוד.

 


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

 

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

 

נתחיל כרגיל ב – Contracts

 



namespace Contracts


{


    [ServiceContract(CallbackContract = typeof(IStockClient))]


    public interface IStockService


    {


        [OperationContract(IsOneWay = true)]


        void Regisetr(string stockName);


    }


}


 


נעבור על הדברים השונים.

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

בנוסף אנחנו רואים שהמתודה של השרת מוגדרת כ – IsOneWay וזה עבור ביצועים יותר טובים.

 

כעת נראה את המממשק של הלקוח.

 



namespace Contracts


{


    [ServiceContract]


    public interface IStockClient


    {


        [OperationContract(IsOneWay = true)]


        void GetResult(IEnumerable<StockResponce> results);


    }


}


 


אפשר לראות שהוא מוגדר ב – Contrat לכל דבר (האובייקט StockResponse הינו אובייקט המכיל מחרוזת ומחיר למנייה – תוכלו לראות אותה על ידי הורדת הקוד המלא)

 

ננתח את מה שראינו עד כה.

יש לנו שני Contracts אחד עבור השרת ואחד עבור הלקוח, השרת מכיר את ההגדרה של ה – Contract שהלקוח יממש.

 

כעת נראה את הקונפיג של ה – host

 



<system.serviceModel>


  <services>


    <service name="Service.StockService">


      <endpoint address="http://localhost:1245/Stock"


                binding="wsDualHttpBinding"


                contract="Contracts.IStockService"></endpoint>


    </service>


  </services>


</system.serviceModel>


 


אני משתמש ב – wsDualHttpBinding שזה אחד מאותם bindings המאפשרים עבודה ב – duplex.

 

 


כעת נראה את הלקוח:

 



static void Main(string[] args)


{


    var factory = new DuplexChannelFactory<IStockService>(ClientCallback.Instance, "stock");


    IStockService proxy = factory.CreateChannel();


 


    while (true)


    {


        Console.Write("Register: ");


        string stock = Console.ReadLine();


        proxy.Regisetr(stock);


    }


}


 


ראשית במקום שימוש ב – ChannelFactory השתמשנו ב – DuplexChannelFactory שמקבל מישהו שמממש את IStockClient (שזה אותו ממשק שהוגדר לשרת שהלקוח יממש – מיד נראה אותו.

 

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

 



public class ClientCallback : IStockClient


{


    #region Singelton



    // here is the singelton code…


    #endregion


 


    public void GetResult(IEnumerable<StockResponce> results)


    {


        Console.WriteLine("================");


        Console.WriteLine("Result: " + DateTime.Now.ToLongTimeString());


 


        foreach (var item in results)


        {


            Console.WriteLine(item);


        }


 


        Console.WriteLine("================");


    }


}


 

 


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

 

קונפיג צד הלקוח:

 


<system.serviceModel>


  <client>


    <endpoint name="stock"


              address="http://localhost:1245/Stock"


              binding="wsDualHttpBinding"


              contract="Contracts.IStockService"></endpoint>


  </client>


  <bindings>


    <wsDualHttpBinding>


      <binding clientBaseAddress="http://localhost:33/Client"></binding>


    </wsDualHttpBinding>


  </bindings>


</system.serviceModel>


 

 


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

 

 

מימוש צד השרת:

ראשית נגדיר רשימה שנשמור שם reference לכל הלקוחות שנרשמו (כך שנוכל לפנות אליהם בחזרה)

 



public class StockService : IStockService


{


    private static Dictionary<IStockClient, List<string>> _list;


 


 


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

 

מתודת ה – Register

 



public void Regisetr(string stockName)


{


    var client = OperationContext.Current.GetCallbackChannel<IStockClient>();


    if (!_list.ContainsKey(client))


    {


        _list.Add(client, new List<string>());


    }


 


    if (!_list[client].Contains(stockName))


    {


        _list[client].Add(stockName);


    }


}


 


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

 

כל מה שנשאר לנו לעשות זה מתי שהוא להחזיר תשובה ללקוחות (במימוש שלנו נשתמש ב – timer שירוץ כל 10 שניות ויגדיר מחירים רנדומליים לכל המניות)

 



 


private static Timer _timer;


 



static void _timer_Elapsed(object sender, ElapsedEventArgs e)


{


    var list = GetStocks();


    var res = SetPrice(list);


 


    foreach (var client in _list)


    {


        var x = from n in res


                where client.Value.Contains(n.Name)


                select n;


 


        client.Key.GetResult(x);


    }


}


 


המתודה מחלצת החוצה את כל המניות מגדירה מחיר רנדומלי.

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

 

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

 

 

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

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

5 תגובות

  1. aralele26 בדצמבר 2011 ב 9:37

    שלום שלמה
    תודה רבה על הפוסט למדתי ממנו המון
    אני מנסה ליישם שירות DUPLEX אבל נתקל בבעיה הבאה:
    http://stackoverflow.com/questions/8630593/auto-generate-callback-class-at-duplex-wcf

    האם יש לך אולי רעיון ?

    בברכה

    הגב
  2. Shlomo26 בדצמבר 2011 ב 21:14

    ניסית להוריד את דוגמת הקוד המצורפת – ולהשוות אותה לקוד שלך ?

    הגב
  3. A10 בינואר 2012 ב 12:10

    עם איזה עוד BINDING עובד DUPLEX

    הגב
  4. יצחק19 במרץ 2013 ב 7:23

    ב"ה

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

    עוד אני יחזור בתשובה בגללך 🙂

    הגב