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

3 בפברואר 2011

תגיות: , ,
אין תגובות




ללמוד WCF 4 פרק 11 – Messaging Patterns
 

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


מה זה Messaging Patterns.
המשמעות של Request-Respone Pattern.
המשמעות של One-Way Pattern, ודוגמת קוד.
המשמעות של Duplex Pattern.
המשמעות של Streaming Pattern, והרבה קוד.
בפרק הבא.


 
מה זה Messaging Patterns.


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

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

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

 

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

 

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

 

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

 

 

התשובות לשאלות הללו למעשה יכריעו באיזה Mssaging Pattern נשתמש.

 

 

המשמעות של Request-Respone Pattern.


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

 

במידה ולא נגדיר אחרת זוהי התנהגות ברירת המחדל לשליחת הודעות.

 

 

המשמעות של One-Way Pattern, ודוגמת קוד.



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

 

מתודות שמוגדרות כ – OneWay חייבות להחזיר void.

 

דוגמא להגדרה של מתודה כ – OneWay

 

 



[OperationContract(IsOneWay = true)]


void SaveChanges(Person person);


 

 

המשמעות של Duplex Pattern.


הלקוח פונה לשרת איך שהוא רוצה (כלומר ב – Request-Response או OneWay) השרת יבצע את הפעולות שלו ומתי שהוא השרת יזום פנייה ללקוח כדי להחזיר לו תשובה, במילים אחרות הלקוח נהפך במקרה הזה לשרת בעצמו ואפשר ליזום אליו פניות.

 

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

 

 

המשמעות של Streaming Pattern, והרבה קוד.



 

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


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


נצטרך לקנפג את ה – TranferMode ל – Stream, RequestStream, ResponseStream בהתאם למתודה.

בנוסף נצטרך להגדיר את המאפיין maxReceivedMessageSize שמגדיר מה המקסימום ב – bytes שניתן להעביר על גבי השירות, יש חשיבות רבה למאפיין הזה, אף על פי שלכאורה המידע לא נשלח בבת אחת עדיין נצטרך להגדיר מה המקסימום בבתים שמתודה יכולה לקבל או להחזיר ובמידה וה – stream יהיה יותר גדול, הלקוח יתרסק, המקסימום שאפשר להגדיר הוא: Int64.MaxValue


דוגמת קוד לנגן אודיו של קבצי wav.



הגדרת ה – Contract

 



namespace Contarcts


{


    [ServiceContract]


    public interface IPlay


    {


        [OperationContract]


        Stream GetSong(string name);


    }


}


 



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

 



namespace Service


{


    public class Play : IPlay


    {


        public Stream GetSong(string name)


        {


            FileStream stream = File.OpenRead(@"C:\tmp\" + name + ".wav");


            return stream;


        }


    }


}


 

 



ההגדרה בקונפיג של ה – host

 



<system.serviceModel>


  <services>


    <service name="Service.Play">


      <endpoint address="http://localhost:3333/play"


                binding="basicHttpBinding"


                bindingConfiguration="httpStream"


                contract="Contarcts.IPlay">


      </endpoint>


    </service>


  </services>


  <bindings>


    <basicHttpBinding>


      <binding name="httpStream"


                maxReceivedMessageSize="2147483647"


                transferMode="StreamedResponse">


      </binding>


    </basicHttpBinding>


  </bindings>


</system.serviceModel>


 



במידה ו – host הינו IIS ואתם שולחים stream כפרמטר ייתכן שתצטרכו גם להגדיר את זה:

 



<system.web>


  <httpRuntime maxRequestLength="2147483647"/>


</system.web>


 

 



צד הלקוח (אני משתמש במקרה הזה ב – ChannelFactory)

 



static void Main(string[] args)


{


    ChannelFactory<IPlay> channel = new ChannelFactory<IPlay>("playEndpoint");


    IPlay proxy = channel.CreateChannel();


 


    using (Stream stream = proxy.GetSong(Console.ReadLine()))


    {


        SoundPlayer mp = new SoundPlayer(stream);


        mp.Play();


 


        Console.ReadLine();


    }


}


 



וכמובן הקונפיג של הלקוח:

 



<system.serviceModel>


  <client>


    <endpoint name="playEndpoint"


              address=http://localhost:3333/play


              binding="basicHttpBinding"


              contract="Contarcts.IPlay"/>


  </client>


  <bindings>


    <basicHttpBinding>


      <binding transferMode="StreamedResponse"


                maxReceivedMessageSize="2147483647">


      </binding>


    </basicHttpBinding>


  </bindings>


</system.serviceModel>


 

 



כדאי גם לשים לב למאפיין receiveTimeout ולהגדיל אותו (גם בשרת וגם בלקוח) מכיוון שקריאה של הרבה מידע יכולה לזרוק TimeoutException

 



receiveTimeout="00:10:00"




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

 

 


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

 

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

 

המימוש:

 



public Stream GetFile(string name)


{


    return File.Open(name, FileMode.Open);


}


 


הלקוח:

 



using (Stream stream = proxy.GetFile(Console.ReadLine()))


{


    byte[] bytes = new byte[1024];


    int count = 0;


    while ((count = stream.Read(bytes, 0, 1024)) > 0)


    {


        Console.WriteLine(count);


    }


 


    Console.ReadLine();


}


 


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

 

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

 



<binding name="httpStream"


          maxReceivedMessageSize="2147483647"


          transferMode="StreamedResponse">


  <readerQuotas maxBytesPerRead="2048" />


</binding>


 


בדוגמא הזאת הגבלנו את מספר הבתים המקסימלי לקריאה ל – 2048 בתים (ברירת המחדל היא 4096)

 

 

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

 

הגדרת ה – Contract:

 



namespace Contarcts


{


    [ServiceContract]


    public interface IFileWorker


    {


        [OperationContract]


        [WebGet]


        Stream DownloadFile(string fileName);


 


        [OperationContract]


        [WebInvoke(Method = "POST", UriTemplate = "UploadFile?fileName={fileName}")]


        void UploadFile(string fileName, Stream fileContents);


    }


}


 


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

 

 

המימוש:

 



namespace Service


{


    public class FileWorker : IFileWorker


    {


        public Stream DownloadFile(string fileName)


        {


            WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] =


                    "attachment; filename=" + fileName;


 


            return File.Open(fileName, FileMode.Open);


        }


 


        public void UploadFile(string fileName, Stream fileContents)


        {


            using (FileStream fs = File.OpenWrite(fileName))


            {


                byte[] bytes = new byte[1024];


                int count = 0;


                while ((count = fileContents.Read(bytes, 0, 1024)) > 0)


                {


                    fs.Write(bytes, 0, count);


                }


            }


        }


    }


}


 


וכמובן קובץ הקונפיג ב – host.

 



<system.serviceModel>


  <services>


    <service name="Service.FileWorker" behaviorConfiguration="filemt">


      <endpoint binding="webHttpBinding" behaviorConfiguration="webHttp"


                address="http://localhost:3333/file"


                bindingConfiguration="httpAllStream"


                contract="Contarcts.IFileWorker"></endpoint>


    </service>


  </services>


  <bindings>


    <webHttpBinding>


      <binding name="httpAllStream"


                maxReceivedMessageSize="2147483647"


                receiveTimeout="00:10:00"


                transferMode="Streamed">


      </binding>


    </webHttpBinding>


  </bindings>


  <behaviors>


    <serviceBehaviors>


      <behavior name="filemt">


        <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:3333/filemt" />


        <serviceDebug includeExceptionDetailInFaults="true"/>


      </behavior>


    </serviceBehaviors>


    <endpointBehaviors>


      <behavior name="webHttp">


        <webHttp faultExceptionEnabled="true" />


      </behavior>


    </endpointBehaviors>


  </behaviors>


</system.serviceModel>


 

 


בהתחלה יש כמובן את ההגדרה של השירות וכמו שאמרנו מקודם זה חייב להיות REST ולכן ה – binding מוגדר כ – webHttpBinding.

יש כמובן את ההגדרות עבור ה – binding – מה שאנחנו מגדירים שזה יהיה stream.

כמובן שצריך לתת behavior מתאים.

 

אף על פי שזה יעבוד אם תגדירו serviceMetadata ותנסו לגלוש אליו הוא ייתן לכם הודעת שגיאה שאי אפשר יותר מפרמטר אחד (מוזר).

 

 

כעת למתודה DownloadFile ניתן פשוט לגלוש בצורה כזאת:

 


 


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

 

 

בפרק הבא:


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

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

כתיבת תגובה

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