DCSIMG
January 2011 - Posts - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

January 2011 - Posts

WCF 4 (Windows Communication Foundation) for Beginner - part 10

ללמוד WCF 4 פרק 10 - יצירת RSS Feed בעזרת WCF
 
 
נושאים בפרק 10:
מה זה RSS Feed ומה זה ATOM.
הכרת המחלקות הרלוונטיות.
יצירת RSS Feed לדוגמא.
בפרק הבא.
 
 
מה זה RSS Feed ומה זה ATOM.
RSS ו - ATOM קיימים כבר הרבה שנים ותפקידם לאפשר האזנה ועדכון אוטמטי למידע מתחדש, למשל אפשר להגדיר קבלת מייל אוטומטי בכל פעם שאני מפרסם פוסט חדש, כל מה שצריך לעשות זה להעתיק את כתובת ה - RSS של הבלוג http://blogs.microsoft.co.il/blogs/shlomo/rss.aspx ל - RSS Reader שלכם (שזה יכול להיות outlook או כל תוכנה אחרת).
 
חשוב לזכור שגם RSS וגם ATOM הם בסופו של דבר XML בפורמט מסויים.
 
יש כל מיני עטיפות ב - net שעוזרות לייצר RSS או ATOM בצורה קלה וכמו כן יש גם מחלקות שעוזרות לקרוא את התוכן (במידה ואתם כותבים Reader בעצמכם), אבל מעולם לא היה פשוט וקל לייצר תוכן בפורמט RSS כמו ב - WCF, הקלות והפשטות שניתן לעשות זאת ב - WCF הם פשוט מדהימים, בדוגמא הנוכחית אני אדגים כיצד לייצר תוכן בפורמט RSS, כמובן שאפשר גם לייצר תוכן בפורמט ATOM ובנוסף ניתן גם לקרוא את התוכן מקוד את שני הנושאים האלו אני לא אדגים, למידע נוסף ומפורט ב - MSDN.
 
 
הכרת המחלקות הרלוונטיות.
לפני שנראה דוגמא ספציפית נכיר כמה מן המחלקות שאנחנו צריכים כדי לייצר תוכן RSS בסיסי.
 
Rss20FeedFormatter - תפקידו לקבל את האובייקטים שיצרנו בקוד ולהעביר אותם סרליזציה לפורמט RSS 2.0.
 
SyndicationFeed - האובייקט הראשי שמכיל את כל המידע שנרצה לשפוך.
 
SyndicationItem - אובייקט המכיל את המידע (feed מכיל מספר items).
 
SyndicationPerson - אובייקט המכיל מידע אודות מפרסם ה - fees או ה - item.
 
SyndicationLink - אובייקט העוטף url.
 
TextSyndicationContent - אובייקט המכיל תוכן, יכול להיות text, html, xml.
 
 
יצירת RSS Feed לדוגמא.:
 
בערוץ שידורי סלע רצינו לחשוף RSS, כתבתי אותו ב - WCF, והנה הוא לפניכם (עם קצת שינויים)
 
 
 

[ServiceContract]

public interface ISCCSyndication

{

    [OperationContract]

    [WebGet]

    Rss20FeedFormatter GetSCCSyndication();

}

 

 

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

public class SCCSyndication : ISCCSyndication

{

    public Rss20FeedFormatter GetSCCSyndication()

    {

        SyndicationFeed feed = GetFeed();

        var items = GetItems();

        feed.Items = items;

 

        return new Rss20FeedFormatter(feed);

    }

 

    private List<SyndicationItem> GetItems()

    {

        var itemList = new List<SyndicationItem>();

        var list = Get10ItemsFromDB();

 

        foreach (var item in list)

        {

            SyndicationItem sItem = new SyndicationItem();

            SyndicationPerson person = GetPerson(item);

            sItem.Authors.Add(person);

            sItem.Links.Add(SyndicationLink.CreateAlternateLink(new Uri("url to lecture")));

            sItem.Content = new TextSyndicationContent(item.Description + "<br/><img src='link to image'/>", TextSyndicationContentKind.Html);

            sItem.Title = new TextSyndicationContent(item.Title);

            sItem.LastUpdatedTime = item.Created;

            itemList.Add(sItem);

        }

 

        return itemList;

    }

 

    private static SyndicationPerson GetPerson(Custom item)

    {

        SyndicationPerson person = new SyndicationPerson();

        person.Email = item.Email;

        person.Name = item.Name;

        person.Uri = item.Url;

 

        return person;

    }

 

    private static SyndicationFeed GetFeed()

    {

        SyndicationFeed feed = new SyndicationFeed();

 

        feed.Authors.Add(new SyndicationPerson("scc@sela.co.il", "Shlomo Goldberg", "http://blogs.microsoft.co.il/blogs/shlomo/"));

        feed.Links.Add(SyndicationLink.CreateAlternateLink(new Uri("http://scc.sela.co.il/scc")));

        feed.Title = new TextSyndicationContent("Sela College Channel Lectures");

        feed.Copyright = new TextSyndicationContent("Copyright Sela.co.il. All rights reserved.");

        feed.Description = new TextSyndicationContent("The latest news over the Sela College Channel.");

        feed.ImageUrl = new Uri("link to scc image");

 

        return feed;

    }

}

 
 
ובקונפיג:
 
 

<system.serviceModel>

  <behaviors>

    <endpointBehaviors>

      <behavior name="web">

        <webHttp />

      </behavior>

    </endpointBehaviors>

  </behaviors>

  <services>

    <service name="SCCSyndication">

      <endpoint binding="webHttpBinding" contract="ISCCSyndication" behaviorConfiguration="web"></endpoint>

    </service>

  </services>

  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

</system.serviceModel>

 
 
 
 
 
 
בפרק הבא.
בפרק הבא נדבר על Messaging Pattern וכיצד זה משפיע על צורת הכתיבה שלנו.
Posted: Jan 31 2011, 11:47 AM by Shlomo | with 2 comment(s)
תגים:,

קבלת נתיב מלא מתוך נתיב חלקי - ResolveClientUrl vs ResolveUrl

 

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

string imgUrl = ResolveUrl("~/images/mying.png");

 
או:

string imgUrl = ResolveClientUrl("~/images/mying.png");

 
 
לדוגמא אם מבנה התיקיות שלנו נראה כך:
 
 
Pages
    Folder1
        MyPage.aspx
Images
    myimg.png
 
ההבדל בין המתודות הוא שהראשונה מחזירה נתיב אבסולוטי מתחילת הפרוייקט, כלומר:
/WebApplication51/images/mying.png
 
ואילו המתודה השנייה מחזירה מיקום יחסי, כלומר:
../../images/mying.png
 
 
כמובן שברוב המקרים נעדיף להשתמש במתודה הראשונה כדי שנעבוד בצורה אבסולוטית ולא נהיה תלויים במבנה התיקיות.

Active sessions in asp.net

 

אחד מהיועצים של סלע שאל אותנו כיצד אפשר לדעת את מספר ה - sessions הפתוחים לאפליקציית asp.net.
 
עידו ענה במהירות שאפשר להשתמש ב - Performance Counters for ASP.NET יש שם כמה counters מעניינים, כמו כמה sessions כרגע נמצאים כמה היו ועוד, שווה להעיף על זה מבט.
Posted: Jan 30 2011, 04:36 PM by Shlomo | with 2 comment(s)
תגים:,

עדכון טבלה ב - SQL Server 2008

 

ב - SQL Server 2008 כברירת מחדל לאחר יצירת הטבלה כמעט בכל שינוי שנרצה לעשות נקבל את ההודעה הבאה:
 
Saving changes is not permitted. The changes you have mase require the following tables to be droping and re-created. You have either made changes to a table that can't be re-created or enable the option Prevent saving changes that require the table to be re-created
 
כדי לעדכן בכל זאת ניתן לכתוב סקריפט שיעדכן את הטבלה,
או ללכת ל Tools -> Options ולבחור ב - Designers ולהוריד את הסימון מ - Prevent saving changes that require the table to be re-created.
 
 
 
Sql Server

WCF 4 (Windows Communication Foundation) for Beginner - part 9

 

ללמוד WCF פרק 9 - כתיבה של web style services חלק 2
 
 
נושאים בפרק 9:
סיכום הפרק הקודם.
שימוש במנגנון ה - caching של asp.net
הפעלה של השירות בעזרת ScriptManager גם כשהשירות יושב בשרת אחר מהדף המשתמש בו.
בפרק הבא.
 
 
סיכום הפרק הקודם.
בפרק 8 ראינו כיצד ניתן לכתוב web style services בעזרת הגדרה של webHttp behavior והגדרת המאפיינים שלו, כמו כן הכרנו את System.ServiceModel.Web.dll שמגדיר את WebGet ו - WebInvoke שבעזרתם ניתן לקבוע כיצד יהיה ניתן להפעיל את השירות ובאיזה פורמטים (JSON, XML), בפרק זה נראה עוד כמה דברים מעניינים בנושא.
 
 
שימוש במנגנון ה - caching של asp.net
אחד הדברים הבנויים באפליקצייות web זה מנגנון ה - caching, מסתבר שכתובים web style service ניתן להשתמש במנגון המובנה של asp.net כדי לייעל את ביצועי השרת.
 
ראשית יש להוסיף ל - service את AspNetCompatibilityRequirements כך:
 

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

public class Calc : ICalc

 

 
למתודות שאנחנו רוצים להשתמש ב - cache יש להוסיף את AspNetCacheProfile כך:
 

[AspNetCacheProfile("CalcCache")]

public int Add(int a, int b)

{

    return (a) + (b);

}

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

<system.web>

  <caching>

    <outputCacheSettings>

      <outputCacheProfiles>

        <add name="CalcCache" duration="30" varyByParam="*"/>

      </outputCacheProfiles>

    </outputCacheSettings>

  </caching>

</system.web

 
המאפיין duration משפיע כמה שניות המידע ישמר במטמון, והמאפיין varyByParam משפיע כמה עותקים של הפניות ישמרו בזיכרון (כגרע הגדרתי * מכיוון שאני רוצה שכל פנייה לשירות אם פרמטרים אחרים ב - query string יחזירו תוצאות שונות, למידע נוסף)
 
דבר נוסף שצריך להוסיף בקונפיג (בתוך system.ServiceModel) הוא את המקטע הבא:
 

<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

 
 
לאחר השינויים הללו גלישה לשירות עם אותם פרמטרים בטווח של 30 שניות תחזיר את התוצאה של החישוב מתוך המטמון ולא תפעיל את השירות לחינם.
 
 
הפעלה של השירות בעזרת ScriptManager גם כשהשירות יושב בשרת אחר מהדף המשתמש בו.
בפרק 7 ראינו כיצד אפשר להפעיל את השירות מ - javascript בעזרת ScriptManager, כידוע ל - AJAX יש חיסרון אחד - והוא - שלא ניתן להפעיל WebService ב - AJAX כשה - WebService ממוקם בשרת אחר מאשר הדף שמנסה להפעיל אותו, (פיתרון חלקי לבעייה)
ב - WCF אפשר לפנות לשירות מרוחק בעזרת ההגדרה הבאה בקונפיג:
 

<webHttpBinding>

  <binding crossDomainScriptAccessEnabled="true">

  </binding>

</webHttpBinding>

 

<endpointBehaviors>

  <behavior>

    <webHttp />

    <enableWebScript  />

  </behavior>

</endpointBehaviors>

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

WCF 4 (Windows Communication Foundation) for Beginner - part 8

ללמוד WCF 4 פרק 8 - כתיבה של שירותים ב - web style והפעלה שלהם בעזרת קריאות GET ו - POST
 
 
(תודה לעידו על העזרה עם פוסט זה)
 
נושאים בפרק 8:
מה המשמעות של כתיבת שירותים web style.
להכיר את webHttp behavior וחלק מהמאפיינים שלו.
להכיר את WebGetAttribute, WebInvokeAttribute וחלק מהמאפיינים שלהם.
צריכה של שירות המוגדר כ - webHttp.
להכיר את WebServiceHost.
 
 
מה המשמעות של כתיבת שירותים web style.
לפעמים אנחנו רוצים לכתוב שירותים שיחשפו את המידע לא ב - SOAP אלא בפרוטוקולים אחרים, למשל כדי לאפשר יצירת RSS או כמו שראינו בדוגמא בפרק הקודם (להפעיל את השירות בעזרת AJAX) ואפילו לאפשר להפעיל את המתודות בצורה ישירה בעזרת גלישה בדפדפן (כמו ב - REST) ולאפשר להחזיר מידע בפורמטים שונים כמו JSON (שהוא יותר חסכוני בתעבורה, למעשה גם XML יותר חסכוני מ - SOAP), כדי לעשות את זה כבר ראינו בפרק הקודם שה - binding צריך להיות webHttpBinding, בפרק היום נראה עוד מידע הקשור ל - webHttpBindgin וה - behavior שמשמש אותו.
 
 
להכיר את webHttp behavior וחלק מהמאפיינים שלו.
כדי להתחיל לעבוד עם web style services מעבר לעובדה שצריך להגדיר את ה - binding כ - webHttpBinding צריך גם להגדיר behavior מיוחד שנקרא webHttp, כך:
 

<endpointBehaviors>

  <behavior>

    <webHttp />

  </behavior>

</endpointBehaviors>

 
יש לו מספר מאפיינים שנרצה להכיר חלק מהם.
 
automaticFormatSelectionEnabled:
שירות יכול להחזיר את התשובה באחד משני הפורמטים XML או JSON (אפשר גם פורמטים אחרים אבל זה דורש קצת יותר עבודה) המאפיין automatic מגדיר האם לנסות להחליט את הפורמט של התשובה (JSON או XML) בצורה אוטומטית לפי הסדר הבא:
הגדרה של media type ב - header של ה - request
הגדרה של ה - content-type של ה - request.
מה שהוגדר ב - OperationContract כברירת מחדל (נראה עוד מעט)
מה שהוגדר כברירת מחדל ב - webHttp behavior (נראה עוד מעט).
לעוד מידע בנושא WCF REST Formatting
 
helpEnabled:
מגדיר האם לחשוף החוצה דף עזרה שמספר על המתודות ובאיזה פרוטוקלים הם נחשפים.
הגישה לדף העזרה יהיה http://localhost/service.svc/help
 
לעוד מידע בנושא WCF REST Service Help Page.
 
faultExceptionEnabled:
במידה והערך אינו true כל שגיאה שתתרחש בשירות תחזיר קוד 500 (קוד שגיאה כללי) במידה והוא יסומן כ - true בזמן שגיאה נקבל הודעת fault (אנחנו נדבר על המושג fault באחד הפרקים הבאים).
 
המאפיין הזה מגיע בנוסף למאפיין includeExceptionDetailInFaults שמוגדר על serviceBehavior (כמו שראינו בפרק 4) שמגדיר האם להחזיר את פרטי השגיאה האמיתית, לעומתו המאפיין faultExceptionEnabled נותן את האפשרות כשכותבים web style services להחזיר שגיאה אמיתית ולא error code 500.
 
defaultOutgoingResponseFormat:
מגדיר מה ברירת המחדל של תשובות (יכול לקבל XML, JSON).
המאפיין הזה תלוי בערך ובהתנהגות של המאפיין automaticFormatSelectionEnabled.
 
defaultBodyStyle:
כשהמתודות שלנו מקבלות יותר מפרמטר אחד והגדרנו שהמתודה עובדת ב - WebInvoke וגם לא השתמשנו ב - UriTemplate (נראה עוד מעט מה זה אומר) ה - host יזרוק את השגיאה הבאה:
 
Operation 'Sub' of contract 'ICalc' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped
 
הסיבה לשגיאה היא פשוטה - כדי שהוא יצליח לעבור סרלזיצייה (כדי להעביר את הערכים) הוא אמור לכתוב קוד כזה:
 

<a>10</a>

<b>20</b>

שזה כמובן xml לא חוקי, לעומת זאת הקוד הבא:

<request>

  <a>10</a>

  <b>20</b>

</request>

 
שהוא xml חוקי מכיוון שיש root אחד.
כדי למנוע את השגיאה ניתן לקבוע את המאפיין defaultBodyStyle כ - Wrapped או כ - WrappedRequest (אפשר גם לקבוע את זה בעזרת  - attributes - נראה בהמשך)
 
 
להכיר את WebGetAttribute, WebInvokeAttribute וחלק מהמאפיינים שלהם.
ברירת המחדל של endpoint שנחשף עם webHttp Behavior מוגדר כ - POST, במידה ונרצה לחשוף ב - GET או להגדיר מספר הגדרות נוספות נצטרך להשתמש ב - attributes המתאימים.
 
כדי להגדיר שהמתודה תחשף החוצה ב - GET נצטרך לכתוב כך:
 

[WebGet]

[OperationContract]

int Add(int a, int b);

 
כעת ניתן יהיה לפנות לשירות בעזרת גלישה ישירה בדפדפן: http://localhost/service.svc/Add?a=1&b=9
 
במידה ונרצה פרוטוקול אחר (POST, PUT, DELETE) נכתוב:
 

[WebInvoke(Method = "POST")]

[OperationContract]

int Sub(int a, int b);

 
ה - attribues מקבלים מספר מאפיינים שכדאי להכיר:
bodyStyle:
זהה למאפיין defaultBodyStyle שהכרנו ב - webHttpBehavior.
RequestFormat ו - ResponseFormat
מגדירים את ברירת המחדל של הפורמטים (XML, JSON) והם תלויים בהגדרה של  automaticFormatSelectionEnabled
 
UriTemplate :
יש לו חשיבות רבה, הוא מאפשר לנו להחליט על הפורמט של ה - url, למשל נוכל לכתוב כזה דבר:
 

[WebGet(UriTemplate = "person/{id}")]

[OperationContract]

Person GetPerson(string id);

 
ואז ניתן יהיה לקבל את המידע בצורה הבאה: http://localhost/service.svc/person/2
 
חשוב לשים לב - שבמקרה שמשתמשים ב"שומרי מקום" ב - path, המשתנים יכולים להיות רק string, לעומת זאת אם ה"שומרי מקום" נמצאים ב - query string למשל:
 

[WebInvoke(Method = "POST", UriTemplate = "sub?a={a}&b={b}")]

[OperationContract]

int Sub(int a, int b);

 
סוג המשתנים יכול להיות כל דבר  (בדוגמא הזאת חסכנו את הצורך להשתמש ב - WrappedRequest).
 
שימוש ב - UriTemplate יכול לתת עוד הרבה ערך מוסף לשירות שלכם (לקריאה נוספת, כאן וכאן).
 
 
 
צריכה של שירות המוגדר כ - webHttp.
במידה והלקוח הוא לא WCF Client (למשל גלישה ישירה בדפדפן או שימוש ב - WebClient) הם יצרכו את השירות ככל שירות web רגיל,
במידה והלקוח הוא WFC (שימוש ב - Add Service Reference או ב - ChannelFactory, כמו שראינו בפרק 3) יש עוד כמה דברים קטנים שצריך לעשות.
האופצייה הפשוטה יותר היא שימוש ב - ChannelFactory, במקרה הזה כל מה שצריך לעשות זה להגדיר את הקונפיג עם ה - behavior כמו שצריך.
במידה ויש מתודה שצריכה הגדרה של Wrapped, אם ההגדרה הייתה על המתודה, למשל:
 

[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest)]

[OperationContract]

int Sub(int a, int b);

 
הלקוח לא צריך להגדיר שום דבר, לעומת זאת עם ההגדרה של ה - Wrapped היא בקונפיג בעזרת המאפיין defultBodyStyle, צריך את אותו הגדרה גם בצד הלקוח.
 
במידה והמאפיין automaticFormatSelectionEnabled הוגדר כ - true בצד השרת צריך גם להגדיר אותו כך בצד הלקוח ואז ניתן יהיה לכתוב קוד כזה:
 

try

{

    ChannelFactory<ICalc> channel = new ChannelFactory<ICalc>("calcEndpoint");

    ICalc proxy = channel.CreateChannel();

 

    int res = proxy.Sub(2, 4);

 

    channel.Close();

}

catch (FaultException ex)

{

 

}

catch (Exception ex)

{

 

}

 
 
לעומת זאת במידה והלקוח הוא בעזרת Add Service Reference, העניינים קצת יותר מסובכים. משום מה הוא לא מצליח לייצר את קובץ הקונפיג וצריך לכתוב אותו לבד (אמנם גם ב - ChnnelFactory אנחנו כותבים לבד,  אבל בשימוש עם ServiceReference אנחנו רגילים שהוא עושה את כל העבודה, בנוסף ויותר גרוע מכך הוא לא יודע להגדיר את המתודות שהוגדרו כ - WebGet כמו שצריך, ואנחנו צריכים להכנס לבד ל - מקום המתאים (תחת Service Refernece אחרי שלחצנו את ShowAllFiles נמצא את המחלקה שהוא יצר עבורנו) ולהוסיף על המתודה את ה - attribute של WebGet לבד.(כל פעם שנעשה Update Service Reference צריך לזכור להחזיר את ה - WebGetAttribute מכיון שמדובר בקוד שמחולל אוטומטית הוא ימחק את כל מה שאנחנו נעשה)
 
 
 
להכיר את WebServiceHost.
בפרק 6 דברנו על סוגי ה - host-ים השונים שקיימים, בנוסף על מה שהזכרתי שם יש גם host מיוחד שנקרא WebServiceHpst שבמידה ונשתמש בו במקום ServiceHpst הרגיל הוא מגדיר את כל ה - webHttp behavior בצורה אוטומטית.
 
Posted: Jan 18 2011, 09:58 AM by Shlomo | with 5 comment(s)
תגים:,

WCF 4 (Windows Communication Foundation) for Beginner - part 7

ללמוד WCF פרק 7 - הפעלה של Service ב - javascript בעזרת Script Manager.
 
 
רשימת נושאים בפרק 7:
דוגמא להגדרה של הפעלה השירות מתוך javascript.
הכרת המאפיין namespace של ServiceContract.
בעבודה עם ajax למה עדיף WCF מאשר Web Service.
בפרק הבא.
 
 
דוגמא להגדרה של הפעלה השירות מתוך javascript.
 
הרבה פעמים אנחנו עובדים עם Script Manager כדי להפעיל מתודות של web service, כדי לאפשר להפעיל מתודות בעזרת Script Manager ב - WCF צריך רק להגדיר behavior מתאים. ולעבוד עם endpoint שתומך בזה (כמו webHttpBinding).
 
בדוגמא שלנו גם השרת וגם הלקוח יושבים באותה אפליקציה (asp.net).
 
בשרת הקונפיג יראה כך:
 

 

<system.serviceModel>

  <services>

    <service name="Service.Calc">

      <endpoint binding="webHttpBinding"

                contract="Contarcts.ICalc">

      </endpoint>

    </service>

  </services>

  <behaviors>

    <endpointBehaviors>

      <behavior>

        <enableWebScript />

      </behavior>

    </endpointBehaviors>

  </behaviors>

</system.serviceModel>

 
 
בדף ה - aspx נכתוב את הקוד הבא:
 

<form id="form1" runat="server">

<div>

    <asp:ScriptManager ID="sm1" runat="server">

        <Services>

            <asp:ServiceReference Path="~/Service1.svc" />

        </Services>

    </asp:ScriptManager>

</div>

<input type="button" value="Click" onclick="Invoke()" />

</form>

 
והנה הסקריפט:
 

<script type="text/javascript">

    function Invoke() {

        tempuri.org.ICalc.Add(20, 30, function (res) {

            alert(res);

        });

    }

</script>

 
 
אפשר לראות שאנחנו ניגשים ל - contract ומפעילים את מתודת Add שמקבלת שני פרמטרים, כמובן כמו כל מתודה אסינכרונית היא מקבלת גם מתודה להפעלה בזמן קבלת התשובה (והיא יכולה גם לקבל מתודה להפעלה במידה והקריאה תכשל).
 
 
 
הכרת המאפיין namespace של ServiceContract.
בדוגמא הקודמת ראינו שהגישה ל - ICalc הייתה באמצעות tempuri.org, כדי לכתוב קוד קצת יותר יפה וכדי למנוע התנגשיות בין שני Contracts מומלץ לשנות את ההגדרה של ה - namespae, כך:
 

[ServiceContract(Namespace = "CalcService")]

public interface ICalc

 
ואז נוכל לכתוב:
 

function Invoke() {

    CalcService.ICalc.Add(20, 30, function (res) {

        alert(res);

    });

}

 
 
 
בעבודה עם ajax למה עדיף WCF מאשר Web Service.
ב - WebService כאשר אנחנו מחזירים אובייקט, צד הלקוח יקבל אך ורק את האובייקט שעבר סרלזיצייה, כלומר אם יש מאפיינים שהערך שלהם null צד הלקוח לא מכיר את ה - type, לעומת זאת WCF יוצר קובץ js מיוחד שמכיל את כל המידע שיש בצד השרת, למשל נניח שהוספנו את המתודה הבאה:
 
 

[OperationContract]

Person GetPerson();

 

 

[DataContract]

public class Person

{

    [DataMember]

    public int Age { get; set; }

 

    [DataMember]

    public Address[] Addresses { get; set; }

}

 

[DataContract]

public class Address

{

    [DataMember]

    public string Number { get; set; }

 

    [DataMember]

    public Cities City { get; set; }

}

 

[DataContract]

public enum Cities

{

    [EnumMember]

    RamtGan,

    [EnumMember]

    TelAviv,

    [EnumMember]

    BeniBrak

}

 
 
ב - WebSerivce במידה והמערך של Address יהיה ריק האובייקט לא יוכר בצד הלקוח ואם יהיה לנו מתודה המקבלת Person כפרמטר כדי לעדכן אותו, נצטרך להגדיר אובייקט מסוג Address ב - jacascript כדי שנוכל לשלוח אותו לשרת, לעומת זאת ב - WCF כל ה - type יורדים לצד הלקוח, אפילו ה - enums יורדים, למשל נוכל לכתוב קוד כזה:
 
 

function InvokeGetPerson() {

    CalcService.ICalc.GetPerson(function (res) {

        if (res.Addresses) {

            for (var i = 0; i < res.Addresses.length; i++) {

                if (res.Addresses[i].City == Contarcts.Cities.RamtGan) {

                    // do some code

                }

            }

        }

    });

}

 
השורה המעניינת היא השורה בה אנו מנסים לעשות השוואה בין העיר של הכתובת לבין ערך של enum שלא אנחנו הגדרנו אותו, אבל הוא נוצר אוטומטית וזה מקל המון על החיים.
 
 
למעשה ברגע שהגדרנו enableWebScript במידה ונגלוש לשירות ונוסיף בסוף את המילה js או jsdebug נקבל את קובץ ה - javascript שנוצר עובר השירות, למשל
 
 
 
הוא קובץ די ארוך - אני אציג חלק ממנו
 

Type.registerNamespace('CalcService');

CalcService.ICalc = function () {

    CalcService.ICalc.initializeBase(this);

    this._timeout = 0;

    this._userContext = null;

    this._succeeded = null;

    this._failed = null;

}

CalcService.ICalc.prototype = {

    _get_path: function () {

        var p = this.get_path();

        if (p) return p;

        else return CalcService.ICalc._staticInstance.get_path();

    },

    Add: function (a, b, succeededCallback, failedCallback, userContext) {

        /// <param name="a" type="Number">System.Int32</param>

        /// <param name="b" type="Number">System.Int32</param>

        /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>

        /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>

        /// <param name="userContext" optional="true" mayBeNull="true"></param>

        return this._invoke(this._get_path(), 'Add', false, { a: a, b: b }, succeededCallback, failedCallback, userContext);

    },

    Sub: function (a, b, succeededCallback, failedCallback, userContext) {

        /// <param name="a" type="Number">System.Int32</param>

        /// <param name="b" type="Number">System.Int32</param>

        /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>

        /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>

        /// <param name="userContext" optional="true" mayBeNull="true"></param>

        return this._invoke(this._get_path(), 'Sub', false, { a: a, b: b }, succeededCallback, failedCallback, userContext);

    },

    GetPerson: function (succeededCallback, failedCallback, userContext) {

        /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>

        /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>

        /// <param name="userContext" optional="true" mayBeNull="true"></param>

        return this._invoke(this._get_path(), 'GetPerson', false, {}, succeededCallback, failedCallback, userContext);

    }

}

 

 

Type.registerNamespace('Contarcts');

if (typeof (Contarcts.Person) === 'undefined') {

    Contarcts.Person = gtc("Person:http://schemas.datacontract.org/2004/07/Contarcts");

    Contarcts.Person.registerClass('Contarcts.Person');

}

if (typeof (Contarcts.Address) === 'undefined') {

    Contarcts.Address = gtc("Address:http://schemas.datacontract.org/2004/07/Contarcts");

    Contarcts.Address.registerClass('Contarcts.Address');

}

if (typeof (Contarcts.Cities) === 'undefined') {

    Contarcts.Cities = function () { throw Error.invalidOperation(); }

    Contarcts.Cities.prototype = { RamtGan: 0, TelAviv: 1, BeniBrak: 2 }

    Contarcts.Cities.registerEnum('Contarcts.Cities', true);

}

 
 
כמו שאפשר לראות הקובץ הזה שנוצר אוטומטית חוסך לנו הרבה עבודה (הקובץ המקורי הרבה יותר גדול הצגתי רק חלק ממנו).
 
 
בפרק הבא.
כיצד אפשר להגדיר שיהיה ניתן להפעיל את השירות בעזרת GET (כלומר גלישה פשוטה בדפדפן)
 
 
Posted: Jan 13 2011, 02:21 PM by Shlomo | with 2 comment(s)
תגים:,

How to get the return value of main

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

static int Main(string[] args)

{

    int num1 = int.Parse(args[0]);

    int num2 = int.Parse(args[1]);

    int res = 0;

 

    switch (args[2])

    {

        case "+": res = num1 + num2;

            break;

        case "-": res = num1 - num2;

            break;

        case "*": res = num1 * num2;

            break;

        case "/": res = num1 / num2;

            break;

    }

 

    return res;

}

 
כעת יש לנו פרוייקט אחר שרוצה להפעיל את ה - Console Application ולקבל בחזרה את הערך (לא כהפעלת מתודה אלא להריץ את ה - prosess).
 
נכתוב את הקוד הבא:
 

static void Main(string[] args)

{

    string path  = @"file.exe";

    Process process = Process.Start(path, "2 3 +");

    process.WaitForExit();

    int res = process.ExitCode;

    Console.WriteLine(res);

}

 
 
כמובן ש - path.exe צריך להיות שם הקובץ האמיתי, נפעיל את ה - process ונשלח את הפרמטרים המתאימים.
 
נעצור את האפליקציה שלנו עד שה - process יסתיים.
 
המאפיין ExitCode יכיל את הערך שחזר מה - main
 
Posted: Jan 12 2011, 01:35 PM by Shlomo | with no comments
תגים:,

Back Up and Restore database (sql server)

 

אחת הסטודנטיות במכללה בקשה שאכתוב פוסט כיצד עושים backup ו - restore לבסיס נתונים (SQL Server).
 
לבקשתה ולתועלת האחרים, אלו השלבים שצריך לעשות:
 
 
Back Up:
קליק ימין על שם בסיס הנתונים ובחירה ב - Tasks ואז ב - Back Up
 
 
Back Up 1
 
יפתח החלון הבא:
 
 
Back Up 2
 
 
יש לשים לב לחלק התחתון (Destination) שם אפשר לבחור לאיזה קובץ לשמור את בסיס הנתונים - במידה וזה פעם שנייה שאתם עושים את התהליך ותשמרו את בסיס הנתונים לאותו קובץ כמו בפעם הקודמת - יהיה לכם למעשה קובץ אחד המכיל כמה עותקים של בסיס הנתונים.
 
יש לשים לב לנתיב בו הוא מייצר את קבצי הגיבוי (סיומת של bak) כדי שיהיה ניתן לקחת אותם משם.
 
 
לחיצה על OK והתהליך הושלם.
 
 
Restore
כדי לשחזר את בסיס הנתונים אתם יכולים לעמוד על ה - Root ולבחור ב - Restore Database
 
Restore 1
 
 
יפתח החלון הבא:
 
Restore 2
 
 
כשבחלק העליון נצטרך לבחור (או לכתוב) את שם בסיס הנתונים - במידה ונכתוב שם שלא קיים, הוא יצור אחד חדש.
ובחלק התחתון אנחנו צריכים לייבא את קובץ הגיבוי.
 
נבחר באופצייה From device ונלחץ על הלחצן עם שלושת הנקודות
 
 
Restore 3
 
 
נקבל את החלון הבא:
 
 
Restore 4
 
 
נלחץ על Add ונבחר את קובץ הגיבוי שיצרנו (או הבאנו ממקום אחר), לאחר מכן נלחץ על OK
 
נקבל את החלון הבא:
 
Restore 5
 
 
כמו שאפשר לראות ייתכן ונקבל יותר מאופצייה אחת לשיחזור (במידה והפעלנו את תהליך הגיבוי על אותו קובץ יותר מכמה פעמים) - נבחר את הגיבוי הרצוי לנו נלחץ על OK ובסיס הנתונים ישתחזר

איחוד תוצאות של שתי שאילתות שונות לשאילתא אחת בעמודות

 

עלה לי הצורך לאחד תוצאות של שתי שאילתות לאחת, מה הכוונה ?
 
שאילתה אחת החזירה את התוצאות הבאות:
a
b
c
d
 
שאילתה נוספת החזירה את התוצאות הבאות:
1
2
3
4
 
 
כעת הייתי צריך לאחד את התוצאות, במידה והצורך היה לחבר אותם ולהחזיר כמה שורות השאילתא הייתה פשוטה - בעזרת union
 
 

SELECT Column1 FROM Table1

UNION

SELECT Column2 FROM Table2

 
ואז הייתי מקבל
a
b
c
d
1
2
3
4
 
אבל כפי שציינתי הצורך היה לחבר אותם בעמודות. בצורה הבאה:
 
a    1
b    2
c    3
d    4
 
למי שלא מכיר עדיין את הפורום המצויין לתכנות בתפוז - הגיע הזמן להכנס ולהכיר אותו, הפניתי את השאלה בתפוז ותוך כמה דקות ענה לי tenen שאני יכול להשתמש ב  - Row_Number כדי לבצע JOIN, ואכן מה השאילתא נכתבת כך:
 
 

SELECT table1.column1, column2.column2 FROM

(SELECT column1, ROW_NUMBER() OVER (ORDER BY column1) AS rn1 FROM table1) table1,

(SELECT column2, ROW_NUMBER() OVER (ORDER BY column2) AS rn2 FROM table2) table2

WHERE table1.rn1 = table2.rn2

 

 
 

Entity Framework include sub path in query

כיצד להביא בבת אחת בעזרת Entity Framework את כל האובייקטים הקשורים. (גם את תתי האובייקטים)

 
 
כידוע EF מביא את האובייקטים הקשורים לאובייקט שלנו בזמן שנצטרך אותם, וכדי לשכנע אותו בכל זאת להביא את כל האובייקטים בזמן השאילתא נצטרך להשתמש בפונקצייה Include
 
 

context.Owners.Include("Subjects").First(x => x.OwnerKey == ownerKey);

 
 
במידה ולאובייקט Subject יש גם אובייקטים שנרצה להביא אותם בזמן השאילתא הראשית - נכתוב קוד כזה
 
 

context.Owners.Include("Subjects").Include("Subjects.SubSubjects").First(x => x.OwnerKey == ownerKey);

WCF 4 (Windows Communication Foundation) for Beginner - part 6

ללמוד WCF 4 פרק 6 - אירוח השירות בפלטפורמות שונות.
 
 
רשימת נושאים בפרק 6:
אירוח שירות ב - Console Application, Windows Form Application, Windows Presentation Foundation.
אירוח שירות ב - Windows Service
אירוח שירות ב - Internet Information Services - IIS
אירוח שירות ב - Windows Process Activation Service - WAS
אירוח שירות ב - AppFabric
 
 
אירוח שירות ב - Console Application, Windows Form Application, Windows Presentation Foundation.
 
למעשה אירוח של שירות הוא בסך הכול Process כלשהו של מערכת ההפעלה שמארחת את השירות בתוכה, ולכן גם Console Application וגם WinForm וגם WPF הם למעשה אותו דבר, מכיון שמדובר באפיליקציה שאנחנו מריצים ובזמן שנפעיל אותה אנחנו נרים את השירות לאוויר, וראינו דוגמא לכך בפרק השני.
 
נראה שוב את הדוגמא לקוד, (כמובן שצריך לכתוב בקובץ הקונפיג את כל ההגדרות לשירות - כמו שלמדנו בפרקים הקודמים)
 

ServiceHost calcHost = new ServiceHost(typeof(Calc));

calcHost.Open();

 

Console.ReadLine();

 
השורה של Console.ReadLine חשובה רק במקרה של Console Application שיש לו נטייה להיסגר לאחר ביצוע השורה האחרונה של ה - Main ואם לא נעצור אותו ה - host לא יעבוד.
לעומת זאת ב - WinForm ו - WPF מספיק לפתוח את host והוא ישאר פתוח כל עוד שלא נסגור את החלון.
 
נקודה נוספת שכדאי לדעת שהאובייקט ServiceHost יכול לקבל גם אובייקט ולא רק Type, ואפשר לכתוב קוד כזה
 

Calc calc = new Calc();

ServiceHost calcHost = new ServiceHost(calc);

 
נשתמש בצורה הזאת בדרך כלל כשהשירות צריך לקבל פרמטרים בבנאי שלו, ובמקרה הזה אי אפשר להעביר את ה - Type אלא צריך לייצר אובייקט ולתת לו את הפמרטרים המתאימים ולשלוח אותו ל - ServiceHpst. (במקרה הזה המוד שהשירות רץ בו צריך להיות Single - נדבר על זה באחד הפרקים הבאים)
 
החסרונות של אירוח השירות באחת מהאפליקציות הללו, שאנחנו אחראים על פתיחת ה - host ואנחנו אחראים על טיפול בשגיאות וסגירת ה - host כשלא צריך אותו יותר, ואין שום מנגנון אוטומטי שיעשה לנו את העבודה.
 
 
 
אירוח שירות ב - Windows Service
 
 
שיטה נוספת לארח שירות של WCF היא לארח אותו ב - Windows Service, היתרון שלו לעומת השיטה הקודמת שה - host יכול להתחיל לעבוד מיד כשמערכת ההפעלה עולה ואנחנו לא צריכים לעשות שום דבר בשביל שזה יקרה (כמובן שצריך להגדיר את זה).
 
אמנם הפוסטים האלו מתעסקים ב - WCF - אבל נראה כאן דוגמה קטנה ליצירת Windows Service שמארח WCF.
 
ראשית, הוסיפו פרייקט מסוג Windows Service, יווצר קובץ בשם Service1 - נעבור לקובץ ה - cs שלו ונכתוב את הקוד הבא
 

namespace WindowsServiceHost

{

    public partial class Service1 : ServiceBase

    {

        private ServiceHost _calcHost;

 

        public Service1()

        {

            InitializeComponent();

        }

 

        protected override void OnStart(string[] args)

        {

            _calcHost = new ServiceHost(typeof(Calc));

            _calcHost.Open();

        }

 

        protected override void OnStop()

        {

            _calcHost.Close();

        }

    }

}

 
נוסיף קובץ קונפיג ונכתוב את ההגדרות המתאימות - בדוגמא אצלי כתבתי:
 

<system.serviceModel>

  <services>

    <service name="Service.Calc">

      <host>

        <baseAddresses>

          <add baseAddress="http://localhost:8412/CalcService"/>

        </baseAddresses>

      </host>

    </service>

  </services>

</system.serviceModel>

 
אפשר לראות שאני משתמש בתכונות של WCF 4 (מה שראינו בפרק הקודם) שאני מגדיר רק baseAddress והשאר מוגדר בצורה אוטומטית.
 
 
כעת מה שנשאר זה להתקין אותו ולהריץ אותו (כאן כתבתי פוסט כיצד מתקינים Windows Service) - חבל שאי אפשר פשוט ללחוץ על F5 אלא זה חייב לעבור תהליך התקנה (יש דרך להריץ אותו בזמן פיתוח כ - Console, עוד פרטים כאן).
 
אני אתאר בקצרה מה צריך לעשות כדי להתקין אותו:
לאחר כתיבת כל הקוד (והקונפיג) המתאים - חזרו לקובץ Service1 (ל - designer  - לא לקוד).
קליק ימין על השטח האפור ובחירה ב - Add Installer.
יווצר קובץ בשם ProjectIntaller1 ובו שני אלמנטים האחראים להתקין את השירות, תוכלו שם גם לבחור האם השירות יעלה בצורה אוטומטית ועוד הגדרות הקשורות ל - Windows Service.
כעת תפתחו Command Promt של הגרסה המתאימה (VS2008 / VS2010) ותריצו את הפקודה intallutil -i fileName (כש - fileName) מתחלף בנתיב המלא לקובץ ה - exe שלכם.
בפעם הראשונה אחרי ההתקנה אתם צריכים להפעיל את השירות בעצמכם דרך חלון ה - Services של Windows - בהפעלות הבאות של מערכת ההפלעה השירות יעלה לבד (אם הגדרתם כך).
וכעת לקוחות יכולים לפנות לשירות מבלי שצריך לדאוג ל - host.
 
 
כמובן ששימוש ב - Windows Service הוא הרבה יותר טוב מאשר Console Application מכיוון שאנחנו לא צריכים לדאוג להפעלה ולכיבוי אלא זה מחובר למערכת ההפעלה, מצד שני עדיין יש מגוון נושאים שאנחנו צריכים לדאוג להם, החל מהתאוששות משגיאות (למשל השירות קרס משגיאה פתאומית - מישהו צריך להרים אותו בחזרה לאוויר) כיבוי אוטומטי של השירות אם אף אחד לא ניגש אליו כעבור זמן מסויים והחזרתו לאוויר בזמן שמישהו פונה לשרת.
 
 
אירוח שירות ב - Internet Information Services - IIS
 
 
אופציה נוספת שתעשה עבורנו הרבה יותר דברים היא לארח את השירות ב - IIS, נוכל לארח שירות WCF כמו שאנחנו מארחים שם אפלקציות web רגילות.
 
גרסאות 5.1 (XP) ו - 6.0 (Windows Server 2003) תומכים רק ב - endpoints מסוג http, גרסאות מתקדמות יותר תומכים גם באחרים בעזרת WAS (נדבר עליו בהמשך).
 
כדי לארח ב - IIS נוסיף קובץ מסוג svc (נקרא WCF Service) לפרוייקט מסוג wab application, הוא יוסיף איתו גם Interface (עבור Contract ומימוש כלשהו בקובץ ה - cs של השירות).
כמו שאנחנו כבר יודעים אפשר לכתוב את הכול (Contract, Service, Host) בפרוייקט אחד - וזה מה שקרה כשהוספנו קובץ מסוג svc לפרוייקט, היות שבדוגמא שלנו המימוש של ה - Contract וה - Service הם בפרוייקטים שונים (כמו שמומלץ לעשות) נמחק את הקבצים IService1.cs ו - Service1.svc.cs ונשאיר רק את הקובץ Service1.svc.
 
כשנפתח את הקובץ נגלה שכתוב שם:
 

<%@ ServiceHost

        Language="C#"

        Debug="true"

        Service="IISHost.Service1"

        CodeBehind="Service1.svc.cs"

%>

 

 
שני הפרמטרים הראושנים הם מובנים,
הפרמטר Service אמור להכיל את השם המלא של המחלקה שמממשת את השירות - ולכן נשנה את זה ל - Service.Calc,
הפרמטר האחרון רלוונטי רק במידה וקובץ המימוש נמצא בפרוייקט של ה - host - ולכן במקרה שלנו נמחק את הפרמטר.
בסופו של תהליך הוא יראה כך:
 

<%@ ServiceHost

        Language="C#"

        Debug="true"

        Service="Service.Calc"

%>

 

 
כמובן שצריך להוסיף את הקוד הרלוונטי לקובץ הקונפיג:
 

<system.serviceModel>

  <services>

    <service name="Service.Calc">

      <endpoint binding="basicHttpBinding"

                contract="Contarcts.ICalc">

      </endpoint>

    </service>

  </services>

</system.serviceModel>

 
 
חשוב לשים לב שה - endpoint לא קבל פרמטר עבור ה - address, מכיוון שמדובר באפליקציית web הכתובת היא למעשה הכתובת של קובץ ה - svc (כמו כתובת של דף אינטרנט).
 
כעת לקוחות יכולים לתקשר עם השירות בצורה מאוד פשוטה - למשל נוכל לכתוב אפליקציית Console Application שבקונפיג כתוב:
 

<system.serviceModel>

  <client>

    <endpoint name="calcEndpoint"

              address="http://localhost:62431/Service1.svc"

              binding="basicHttpBinding"

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

  </client>

</system.serviceModel>

 
ובקוד כתוב:
 

static void Main(string[] args)

{

    ChannelFactory<ICalc> channel = new ChannelFactory<ICalc>("calcEndpoint");

    ICalc proxy = channel.CreateChannel();

 

    int res = proxy.Add(2, 4);

 

    channel.Close();

}

 
 
הכוח של אירוח אפליקציות ב - IIS הוא שלמעשה אנחנו מקבלים את כל היתרונות של אפליקציות web, מעבר לכך שאנחנו לא צריכים להרים אותם לאוויר, אלא ברגע שתגיע בקשה לשירות הוא יעלה בצורה אוטומטית וכמובן במידה ולא תגיע שום בקשה לשרת אחרי X זמן (X תלוי בקונפיגורצייה) השירות יסגר כדי לחסוך במשאבים, כמו כן IIS יודע להתאושש לבד משגיאות ויש לו עוד הרבה יתרונות כמו שאנחנו מכירים אותם מעולם ה - web.
 
 
במידה ורוצים קצת יותר שליטה על יצירת השירות (למשל במידה ויש לו בנאי שמקבל פרמטרים) נוכל לעשות זאת בעזרת פרמטר שנקרא Factory, נכתוב בקובץ ה - svc את הקוד הבא:
 

<%@ ServiceHost

        Language="C#"

        Debug="true"

        Service="Service.Calc"

        Factory="IISHost.CalcFactory"

%>

 

 
נוסיף את המחלקה הבאה: (יש להוסיף reference ל - System.ServiceModel.Activation)
 

namespace IISHost

{

    public class CalcFactory : ServiceHostFactory

    {

        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)

        {

              // Cutsom Code   

        }

    }

}

 
 
כך נוכל לכתוב קוד ליצירת ה - host
 
 
אירוח שירות ב - Windows Process Activation Service - WAS
החיסרון המרכזי של IIS (בגרסאות הישנות) הוא שאפשר לעבוד רק עם פרוטוקול http מה שפחות יעיל כמובן, במידה וזה אפשרי לעבוד עם tcp (אפליקציה פנימית וכו') עדיף לעשות זאת - במידה ועדיין נרצה לארח אותה ב - IIS, נוכל רק במידה ויש לנו גרסה 7 ומעלה (ויסטה).
 
למעשה כל מה שצריך לעשות זה להגדיר לאפליקציה שאנחנו מוכנים להאזין גם לתקשורת בפרוטוקלים אחרים.
נפתח את ה - IIS.
נחפש את האפליקציה שלנו - קליק ימין ונבחר ב - Manage Application -> Adavanced Settings.
במאפיין Enabled Protocols נוסיף איזה פרוטוקול שנרצה.
 
WAS
 
 
כמובן שנצטרך לשנות בקונפיג את ההגדרות המתאימות:
בשרת:
 

<system.serviceModel>

  <services>

    <service name="Service.Calc">

      <endpoint binding="netTcpBinding"

                contract="Contarcts.ICalc">

      </endpoint>

    </service>

  </services>

</system.serviceModel>

 
בלקוח:
 

<system.serviceModel>

  <client>

    <endpoint name="calcEndpoint"

              address="net.tcp://localhost/IISHost/Service1.svc"

              binding="netTcpBinding"

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

  </client>

</system.serviceModel>

 
וכעת גם נקבל את כל הניהול של IIS וגם נקבל את התכונות של עבודה בפרוטוקול tcp.
 
 
 
אירוח שירות ב - AppFabric
החיסרון (היחסי) של אירוח ב - IIS הוא - ש - IIS הוא מארח מאוד כללי, כלומר הוא לא מכיר WCF, מבחינתו שירות של WCF היא עוד אפליקציה שהוא מעביר לה בקשות ומחזיר תשובות, ולכן הוא יודע לעקוב אחרי השירות ברמה מאוד כללית, למשל נוכל לדעת שהתרחשה שגיאה אבל לא נוכל לדעת באיזה שירות בדיוק השגיאה התרחשה, כמו כן לא נוכל לקבל מידע על התעבורה שיש בכל שירות שמתארח באפליקצייה (אפליקצייה אחת == כמה שירותים).
 
כדי לקבל את כל המידע הזה נוכל להשתמש ב - AppFabric, כשנדבר על מעקב וטפול בשגיאות בשירותים של WCF אני ארחיב גם בנושא זה.
Posted: Jan 09 2011, 08:37 AM by Shlomo | with 2 comment(s)
תגים:,

WCF 4 (Windows Communication Foundation) for Beginner - part 5

ללמוד WCF 4 פרק 5 - המשך התעמקות בקובץ הקונפיג.
 
 
רשימת נושאים בפרק 5:
סיכום הפרקים הקודמים.
יצירת Binding מותאם אישית.
האופצייה להגדיר יותר מ - endpoint אחד לשירות.
הכרת האלמנט host ו - baseAddress.
הכירות עם protocolMapping.
בפרק הבא.
 
 
סיכום הפרקים הקודמים
בפרק הראשון השני והשלישי דברנו על ההבנה של מה זה שירות כיצד מגדירים אותו וכיצד הלקוח יכול לתקשר עם השירות.
בפרק הרביעי התחלנו להבין את החלקים השונים של קובץ הקונפיג ומה המשמעות של Binding ו - Behavior.
 
כעת נכיר עוד כמה אלמנטים בקובץ הקונפיג.
 
 
יצירת Binding מותאם אישית.
כזכור Binding מגדיר רשימה של פרוטוקלים עבור השירות, האם זה יעבור ב - http האם המידע יעבור בצורה מאובטחת ועוד, לפעמים (נדירות) רוצים להגדיר סט של פרוטוקולים שלא מוגדרים מראש יחד, למשל אנחנו רוצים לדבר ב - http אבל שהמידע יעבור בצורה בינארית (שזה יותר יעיל - כמובן שאפשר לעשות את זה רק עם גם השרת וגם הלקוח הם אפליקציות .net אחרת הלקוח לא יבין את המידע הבינארי וחייבים להעביר xml).
 
במקרה כזה נוכל להגדיר הגדרה כזאת (בתוך המקטע של bindings)
 

<customBinding>

  <binding name="HTTPbinary">

    <binaryMessageEncoding />

    <httpTransport transferMode="Streamed" />

  </binding>

</customBinding>

 
אפשר לראות שהגדרנו שהמידע יעבור בבינארי ובפרוטוקול http כשהמידע יעבור ב - stream (למשל אם נרצה לבנות נגן שמתקשר עם השרת כדי לקבל את הסרט שצריך לנגן)
 
כעת נצטרך להגדיר את ה - binding באלמנט service
 

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

          binding="customBinding"

          bindingConfiguration="HTTPbinary"

          contract="Contarcts.ICalc">

 

</endpoint>

 
 
 
האופצייה להגדיר יותר מ - endpoint אחד לשירות.
חשוב לזכור שזה שהגדרנו endpoint אחד לא מונע מאיתנו להגדיר על אותו שירות עוד endpoints, למשל:
 

<services>

  <service name="Service.Calc"

            behaviorConfiguration="calcServiceBehavior">

    <endpoint bindingConfiguration="webHttpBindingCalcBinding"

              behaviorConfiguration="calcEpBehavior"

              address="http://localhost:8412/MyCalcService"

              binding="webHttpBinding"

              contract="Contarcts.ICalc">

 

    </endpoint>

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

              binding="customBinding"

              bindingConfiguration="HTTPbinary"

              contract="Contarcts.ICalc">

 

    </endpoint>

    <endpoint address="http://localhost:2222/MyBasicCalcService"

              binding="basicHttpBinding"

              contract="Contarcts.ICalc">

 

    </endpoint>

  </service>

</services>


כעת יש לנו שלוש יציאות לאותו שירות,
אחת ב - webHttpBinding עבור AJAX
אחת בבינארי
ואחת ב - basicHttp לעבודה בפרוטוקול SOAP (כמו Web Service)
 
 
הכרת האלמנט host ו - baseAddress.
כמו שאפשר לראות בדוגמא הקודמת יש לנו כמה endpoints שעובדים ב - http, במקרה כזה אפשר להגדיר baseAddress וכך לא נצטרך כל פעם לכתוב את הכתובת המלאה, אלא מה שנכתוב יתייחס למה שכתבנו ב - baseAddress.
 
 

<services>

  <service name="Service.Calc"

            behaviorConfiguration="calcServiceBehavior">

    <host>

      <baseAddresses>

        <add baseAddress="http://localhost:8412"/>

      </baseAddresses>

    </host>

    <endpoint bindingConfiguration="webHttpBindingCalcBinding"

              behaviorConfiguration="calcEpBehavior"

              address="MyCalcService"

              binding="webHttpBinding"

              contract="Contarcts.ICalc">

 

    </endpoint>

    <endpoint address="MyBinaryCalcService"

              binding="customBinding"

              bindingConfiguration="HTTPbinary"

              contract="Contarcts.ICalc">

 

    </endpoint>

    <endpoint address="MyBasicCalcService"

              binding="basicHttpBinding"

              contract="Contarcts.ICalc">

 

    </endpoint>

  </service>

</services>

 
כאמור כל baseAddress מתייחס לפרוטוקול ספציפי (אחד עבור http ואם נרצה נוכל להוסיף אחד עבור tcp וכו')
 
 
 
הכירות עם protocolMapping
למעשה ב - WCF 4 לא באמת צריך להגדיר endpoint, במידה ואנחנו רוצים להחצין endpoint בסיסי בלי הגדרות מיוחודות כל מה שצריך זה להגדיר baseAddress, למשל:
 

<services>

  <service name="Service.Calc"

            behaviorConfiguration="calcServiceBehavior">

    <host>

      <baseAddresses>

        <add baseAddress="http://localhost:8412/CalcService"/>

      </baseAddresses>

    </host>

  </service>

</services>

 
יחשוף בצורה אוטומטית endpoint של basicHttpBinding עבור השירות, מכיוון שהוא מתבסס על הקידומת של הכתובת ב - baseAddress.
במידה ונרצה לשנות את ההגדרות ברירות המחדל של ה - endpoints האוטומטיים, נצטרך להוסיף אלמנט בשם protocolMapping
 

<protocolMapping>

  <add scheme="http" binding="customBinding" bindingConfiguration="HTTPbinary"/>

</protocolMapping>

 
כעת הגדרנו שכל הכתובות שיחשפו כ - http מבלי שיהיה להם binding מפורש ייתיחסו לסוג ה - binding של HTTPbinary (מה שהגדרנו מקודם)
 
 
 
בפרק הבא.
בפרק הבא נעזוב לבנתיים את הקונפיגורצייה ונראה כיצד מארחים את השירות במגווון המארחים של windows החל מ - winform עד ל - windows service ואפילו ב - IIS.
Posted: Jan 06 2011, 01:54 PM by Shlomo | with 2 comment(s)
תגים:,

The remote name could not be resolved

 

גישה לשרת מרוחק דרך Proxy מקוד.

 
באחד המקרים ניסיתי לייבא תוכן xml משרת מרוחק ובמחשב הפיתוח הכול עבד בצורה חלקה, לעומת זאת בשרת קבלנו הודעת שגיאה The remote name could not be resolved.
 
 
אחד מבוגרי יהלו"מ של סלע מני מרסיאנו גילה שבשרת כל התקשורת עוברת דרך proxy, ולכן הוא כתב את הקוד הבא:
 

string returnValue;

 

HttpWebRequest wrequest = (HttpWebRequest)WebRequest.Create("url");

string sHttpProxyUrl = "proxy address";

wrequest.Proxy = new WebProxy(sHttpProxyUrl);

wrequest.Timeout = 500000;

 

using (WebResponse wresponse = wrequest.GetResponse())

{

    Stream wstream = wresponse.GetResponseStream();

    StreamReader streamReader = new StreamReader(wstream, Encoding.UTF8);

    returnValue = streamReader.ReadToEnd();

}

 
וכך זה עבד.
Posted: Jan 06 2011, 09:35 AM by Shlomo | with no comments
תגים:

הוספה של פונט מקוד - חלק שני

לא מזמן פרסמתי פוסט - כיצד להוסיף פונט מקוד.
 
מסתבר שהחברה מפרוייקט אתגר לא נחים - וכרגע הם רוצים להוסיף את הפונט כ - Embedded Resource (כדי שלא יצטרכו להעתיק את הפונט כל פעם יחד עם קובץ ה - exe)
 
 
השלב הראשון הוא די פשוט, יש להוסיף את קובץ הפונט ל - Resources.
 
השאלה הנשאלת כעת: כיצד להוסיף את הפונט, מסתבר שיש מתודה בשם AddMemoryFont, אבל למרבה הצער זה מקבל IntPtr ולכן צריך לכתוב את הקוד הבא:

 

IntPtr pointer = Marshal.AllocHGlobal(Resources.ahronbd.Length);

Marshal.Copy(Resources.ahronbd, 0, pointer, Resources.ahronbd.Length);

myFonts.AddMemoryFont(pointer, Resources.ahronbd.Length);

Marshal.FreeHGlobal(pointer);

 
בהנחה ששם הפונט הינו ahronbd.
 
בהתחלה נגדhר משתנה מסוג IntPtr ונקצה מקום בזיכרון בגודל של המערך.
 
לאחר מכן נעתיק את הפונט לזיכרון ששמרנו בצעד בקודם.
 
נוסיף את הפונט ל - Collection.
 
נשחרר את המקום בזיכרון, (מכיוון שמדובר בזיכרון שאינו מנוהל אי אפשר להסתמך על ה - GC).
Posted: Jan 04 2011, 02:19 PM by Shlomo | with 1 comment(s)
תגים:,
More Posts Next page »