Lab

Document Sample
Lab Powered By Docstoc
					Hands-On Labs
WCF Service Discovery
Lab version:   1.0.0

Last updated: 11/10/2011
Contents

OVERVIEW ................................................................................................................................................... 3

EXERCISE 1: AD HOC DISCOVERY .......................................................................................................... 5
       Task 1 – Configuring Service Discovery for the DiscoveryChat Application.......................................... 5
       Task 2 – Enabling ad hoc Discovery ...................................................................................................... 7
   Exercise 1: Verification                                                                                                                               13

EXERCISE 2: METADATA EXTENSIONS ................................................................................................ 16
       Task 1 – Adding an EndpointDiscoveryBehavior with Extensions ...................................................... 16
   Exercise 2: Verification                                                                                                                               19

EXERCISE 3: ANNOUNCEMENTS ........................................................................................................... 21
       Task 1 – Enabling Announcement Endpoint ....................................................................................... 21
   Exercise 3: Verification                                                                                                                               26

EXERCISE 4: DISCOVERY PROXY .......................................................................................................... 27
       Task 1 – Creating a DiscoveryProxy .................................................................................................... 28
       Task 2 – Modifying the DiscoveryChat Application to use Managed Discovery ................................. 45
   Exercise 4: Verification                                                                                                                               47

EXERCISE 5: LEGACY DISCOVERY ....................................................................................................... 50
       Task 1 – Implementing the Legacy Chat Proxy ................................................................................... 51
   Exercise 5: Verification                                                                                                                               59

SUMMARY .................................................................................................................................................. 62
Overview
Windows Communication Foundation 4 includes a new feature that enables service discovery. Service
discovery allows you to locate services on the same subnet using ad hoc discovery, or using a proxy to
establish connections with servers regardless of where they are. In this lab you will create a simple chat
application that will use both methods to learn about available services.

Objectives
In this Hands-On Lab, you will learn:

       How to make a service discoverable in ad hoc mode
       How to use DiscoveryClient to search for service
       How to implement and use a DiscoveryProxy
       How to bridge legacy discovery mechanisms


System Requirements
You must have the following items to complete this lab:
       Microsoft Visual Studio 2010 Beta 2

       .NET Framework 4


Setup
All the requisites for this lab are verified using the Configuration Wizard. To make sure that everything is
correctly configured, follow these steps.

 Note: To perform the setup steps you need to run the scripts in a command window with
 administrator privileges.



    1. Run the Configuration Wizard for the Training Kit if you have not done it previously. To do this,
       run the CheckDependencies.cmd script located under the
       %TrainingKitInstallationFolder%\Labs\WCFServiceDiscovery\Setup folder. Install any pre-
       requisites that are missing (rescanning if necessary) and complete the wizard.

         Note: For convenience, much of the code you will be managing along this lab is available as
         Visual Studio code snippets. The CheckDependencies.cmd file launches the Visual Studio
         installer file that installs the code snippets.
Exercises
This Hands-On Lab comprises the following exercises:
    1. Ad Hoc Discovery
    2. Metadata Extensions
    3. Announcements
    4. Discovery Proxy
    5. Legacy Proxy


Starting Materials
This Hands-On Lab includes the following starting materials.

       Visual Studio solutions. Depending on the exercise you will find Visual Studio solutions for C#
        and Visual Basic that you can use as starting point for the exercises.


        What if I get stuck?
 The source code that accompanies this hands-on lab includes an end folder where you can find a Visual
 Studio solution with the code that you would obtain if you complete the steps in each exercise. You
 can use this solution as a guide if you need additional help working through the exercises.




Next Step
Exercise 1: Ad hoc Discovery
Exercise 1: Ad hoc Discovery
In this exercise you will add an ad hoc discovery mechanism to the DiscoveryChat program using
System.ServiceModel.Discovery, an implementation of the WS-Discovery protocol. In order for a
service to be discoverable in an ad hoc manner it needs to respond to incoming probe messages. Ad hoc
discovery implies that these probe messages come in through a well known port over UDP multicast.
Task 1 – Configuring Service Discovery for the DiscoveryChat Application
DiscoveryChat is a chat application that automatically discovers users on the network using ad hoc or
managed discovery via a proxy.




Figure 1
Two simple chat windows with no discovery enabled


The first thing you need to do is to enable discovery in the simple chat application.
    1. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.


                 Watch Out
         Visual Studio 2010 must be run in elevated mode. To do this, right-click the Visual Studio 2010
         icon and select Run as Administrator.
2. Open the starting solution Begin.sln located under
   %TrainingKitInstallationfolder%\Labs\WCFServiceDiscovery\Source\Ex1-
   AdHocDiscovery\Begin folder, choosing the language of your preference (C# or VB).
3.   To enable discovery, you need to add a service behavior on the service that will be
     discoverable. Open the app.config configuration file from the DiscoveryChat project.
4. Add a new Service Behavior named DiscoveryBehavior inside the <serviceBehaviors> element.
     XML
     <behaviors>
       <serviceBehaviors>
         <behavior name="DiscoveryBehavior">
           <serviceDiscovery />
         </behavior>
       </serviceBehaviors>
     </behaviors>



5. Modify the service description by adding the behaviorConfiguration attribute referencing the
   DiscoveryBehavior just created.
     XML
     <services>
       <service name="Microsoft.Samples.Discovery.ChatService"
                 behaviorConfiguration="DiscoveryBehavior">
         <endpoint
           address=""
           binding="basicHttpBinding"
           contract="ISimpleChatService"/>
       </service>
     </services>



6. Next, you need to add a UDP discovery endpoint. This is where the discovery probe messages
   will be processed.


             Probe Messages
      A Probe message is a WS-Discovery message used by a client to search for services on the
      network by service type. For more information about Probe messages, see section 5.2 of the
      WS-Discovery Specification.



     XML
        <services>
          <service name="Microsoft.Samples.Discovery.ChatService "
                   behaviorConfiguration="DiscoveryBehavior">
            <endpoint
              address=""
              binding="basicHttpBinding"
              contract="ISimpleChatService"/>

            <endpoint
              name="udpDiscoveryEpt"
              kind="udpDiscoveryEndpoint"/>

          </service>
        </services>


                Making Services Discoverable
         The combination of the DiscoveryBehavior and UDP endpoint (specified by the attribute kind =
         “udpDiscoveryEndpoint”) makes your service discoverable. The discovery behavior will now
         respond to incoming UDP probes that match the contract type.


Task 2 – Enabling ad hoc Discovery
In this task you will add code to asynchronously search for other chat users on the same subnet using
service discovery.
    1. First, you will need a reference to the new discovery assembly. Right-click the DiscoveryChat
       project and select Add Reference.
               a. Using the .NET tab, add a reference to System.ServiceModel.Discovery.
           Figure 2
           System.ServiceModel.Discovery assembly reference


2. Open the SimpleChat.cs (C#) or SimpleChat.vb (Visual Basic) file from the DiscoveryChat
   project. You can open the code view by selecting the file and pressing F7.
3. To perform ad hoc discovery you will need to add a member field of type DiscoveryClient. To do
   that, perform the following steps:
          a. At the top of the class, add the following namespace directive for
             System.ServiceModel.Discovery
           C#
           using System.ServiceModel.Discovery;

           Visual Basic
           Imports System.ServiceModel.Discovery



          b. Add the following member variable to the SimpleChat class
           C#
           public partial class SimpleChat : Form
           {
               private DiscoveryClient discoveryClient;

           Visual Basic
           Public Partial Class SimpleChat
               Inherits Form
                  Private WithEvents discoveryClient As DiscoveryClient



4. When the DiscoveryClient locates a service, it will provide an EndpointDiscoveryMetadata
   instance containing metadata about the service. You will use this metadata to display the list of
   available chat users in a list box. To do this, add the PopulateUserList method to the
   SimpleChat class.

     Note: Because the DiscoveryChat application hosts a service, the DiscoveryClient will find this
     service as well as other services on the subnet.

   (Code Snippet – Service Discovery – PopulateUserList Method CSharp)
   C#
   private void PopulateUserList(EndpointDiscoveryMetadata
   endpointDiscoveryMetadata)
   {
       if (!this.EndpointIsSelf(endpointDiscoveryMetadata.Address.Uri))
       {
           this.AddUser(new PeerUser(endpointDiscoveryMetadata.Address));
           this.EnableUsersBox();
       }
   }



   (Code Snippet – Service Discovery – PopulateUserList Method VB)
   Visual Basic
   Private Sub PopulateUserList(ByVal endpointDiscoveryMetadata As
   EndpointDiscoveryMetadata)

       If Not Me.EndpointIsSelf(endpointDiscoveryMetadata.Address.Uri) Then
           Me.AddUser(New PeerUser(endpointDiscoveryMetadata.Address))
           Me.EnableUsersBox()
       End If
   End Sub

     Note: The PeerUser class is provided with the lab code. It is used to track chat conversations
     with other users. The AddUser method adds the user to the list box.



5. Locate the AdHocDiscovery method stub and add the following code. This code will initiate
   discovery looking for services that implement the ISimpleChatService contract and then, as
   services are found, it will add them to the list of available services.
   (Code Snippet – Service Discovery – AdHocDiscovery Method CSharp)
   C#
   private void AdHocDiscovery()
   {
       this.discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());

       this.discoveryClient.FindProgressChanged +=
           new
   EventHandler<FindProgressChangedEventArgs>(this.OnFindProgressChanged);
       this.discoveryClient.FindCompleted +=
           new EventHandler<FindCompletedEventArgs>(this.OnFindCompleted);

        // Setup the form for discovery
        this.ShowDiscoveryInProgress(true);

       // Do async discovery
       this.discoveryClient.FindAsync(new
   FindCriteria(typeof(ISimpleChatService)));
   }



   (Code Snippet – Service Discovery – AdHocDiscovery Method VB)
   Visual Basic
   Private Sub AdHocDiscovery()
       Me.discoveryClient = New DiscoveryClient(New UdpDiscoveryEndpoint())

        ‘ Setup the form for discovery
        Me.ShowDiscoveryInProgress(True)

       ‘ Do async discovery
       Me.discoveryClient.FindAsync(New
   FindCriteria(GetType(ISimpleChatService)))
   End Sub


           Using DiscoveryClient
    The discovery process can be started either synchronous or asynchronously by using the Find
    or FindAsync methods respectively. Most of the times you will use the FindAsync method (as
    in the preceding code).
    During discovery, you can register some events handlers. For example, FindProgressChanged
    is invoked each time one of service endpoints has been discovered. If you want to be notified
    when the discovery process is finished, then the handler FindCompleted should be used.



6. Implement the OnFindProgressChanged and OnFindCompleted handlers as shown in the
   following code by pasting it below the AdHocDiscovery() method implementation.
(Code Snippet – Service Discovery – DiscoveryClient Event Handlers CSharp)
C#
private void OnFindProgressChanged(object sender, FindProgressChangedEventArgs
e)
{
    this.PopulateUserList(e.EndpointDiscoveryMetadata);
}

private void OnFindCompleted(object sender, FindCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        this.ShowStatus("Discovery cancelled");
    }
    else if (e.Error != null)
    {
        this.discoveryClient.Close();
        MessageBox.Show(
            e.Error.Message,
            this.Text,
            MessageBoxButtons.OK,
            MessageBoxIcon.Information,
            MessageBoxDefaultButton.Button1,
            (MessageBoxOptions)0);
    }
    else
    {
        if (this.discoveryClient.InnerChannel.State ==
CommunicationState.Opened)
        {
            this.discoveryClient.Close();
        }
    }

     this.discoveryClient = null;
     this.ShowDiscoveryInProgress(false);
}



(Code Snippet – Service Discovery – DiscoveryClient Event Handlers VB)
Visual Basic
Private Sub OnFindProgressChanged(ByVal sender As Object,
                                      ByVal e As FindProgressChangedEventArgs)
Handles discoveryClient.FindProgressChanged

    Me.PopulateUserList(e.EndpointDiscoveryMetadata)
End Sub
   Private Sub OnFindCompleted(ByVal sender As Object,
                               ByVal e As FindCompletedEventArgs) Handles
   discoveryClient.FindCompleted
       If e.Cancelled Then
           Me.ShowStatus("Discovery cancelled")
       ElseIf e.Error IsNot Nothing Then
           Me.discoveryClient.Close()
           MsgBox(e.Error.Message, MsgBoxStyle.Information, Me.Text)
       Else
           If Me.discoveryClient.InnerChannel.State = CommunicationState.Opened
   Then Me.discoveryClient.Close()
       End If

       Me.discoveryClient = Nothing
       Me.ShowDiscoveryInProgress(False)
   End Sub



7. The other thing you need to do is to handle the case where the user signs out during discovery.
   In that case, you simply need to abort the discovery process and close down the service host.
   Implement the AbortDiscovery method as shown.
   (Code Snippet – Service Discovery – AbortDiscovery Method CSharp)
   C#
   private void AbortDiscovery()
   {
       if (this.discoveryClient != null)
       {
           this.discoveryClient.Close();
       }
   }



   (Code Snippet – Service Discovery – AbortDiscovery Method VB)
   Visual Basic
   Private Sub AbortDiscovery()
       If Me.discoveryClient IsNot Nothing Then Me.discoveryClient.Close()
   End Sub



8. Press Ctrl+Shift+B to build the solution.
Next Step
Exercise 1: Verification


Exercise 1: Verification
    1. Press Ctrl+F5 to start an instance of the DiscoveryChat.exe application without debugging.
    2. Switch back to Visual Studio and press Ctrl+F5 again to launch another instance of the
       DiscoveryChat.exe application.
    3. Switch to one of the instances of DiscoveryChat.exe and setup the chat as follows:
            a. User Name: Fred
            b. Click Sign In




                Figure 3
                Sign in as Fred
     Note: Windows Firewall may prompt you to allow the Chat client access to use the network. It
     is safe to allow this.



4. Switch to the other DiscoveryChat.exe instance and setup the chat as follows:
       a. Username: Wilma
       b. Click Sign In
5. When the Wilma instance signs in, it will do ad hoc discovery immediately and locate the Fred
   instance.
6. Double click on the URI in the available users list to start a chat with Fred.
       a. Send Message Text: Hi Fred
       b. Click Send
7. When you start the chat, you will see Wilma appear in the user list on the Fred Discovery chat
   window.
Figure 4
Two instances of DiscoveryChat application that discovered each other


    8. Close both instances of the DiscoveryChat application.



Next Step
Exercise 2: Metadata Extensions
Exercise 2: Metadata Extensions
The chat application works ok, but you may have noticed an issue. When Wilma discovered other chat
instances all you knew about them was the Uri of the service endpoint. So her chat window shows a
chat with the host machine name. Fred, on the other hand, knew that the chat message came from
Wilma because the chat message included her name. In this exercise you will learn how you can extend
the metadata used in WS-Discovery to supply additional information (such as the username used for the
chat session).
Task 1 – Adding an EndpointDiscoveryBehavior with Extensions
    1. Open the starting solution Begin.sln located under
       %TrainingKitInstallFolder%\Labs\WCFServiceDiscovery\Source\Ex2-
       MetadataExtensions\Begin, choosing the language of your preference (C# or VB). Alternatively,
       you may continue working with the solution obtained after completing the previous exercise.


                 Watch Out
          Visual Studio 2010 must be run in elevated mode. To do this, right-click the Visual Studio 2010
          icon and select Run as Administrator.



    2. Open the SimpleChat.cs (C#) or SimpleChat.vb (Visual Basic) file from the DiscoveryChat
       project. You can open the code view by selecting the file and pressing F7.
    3. You can add XML to the endpoint metadata when responding to a discovery probe. To do this,
       you will need to add namespaces directives, in particular the System.ServiceModel.Description
       namespace.
         (Code Snippet – Service Discovery – ServiceModel.Description CSharp)
         C#
         using System.Linq;
         using System.Xml.Linq;
         using System.ServiceModel.Description;



         (Code Snippet – Service Discovery – ServiceModel.Description VB)
         Visual Basic
         Imports System.ServiceModel.Description



    4.   Locate the OpenServices method and modify it as shown in the following code.
(Code Snippet – Service Discovery – OpenServices Method CSharp)
C#
private void OpenServices()
{
    // Create a singleton instance for the host
    ChatService chatService = new ChatService(this);
    chatServiceHost = new ServiceHost(chatService, _localAddress);

     // Create a discovery behavior
     var endpointDiscoveryBehavior = new EndpointDiscoveryBehavior();

     // Add an extension element with the username
     endpointDiscoveryBehavior.Extensions.Add(
         new XElement(
             "root",
             new XElement("Name", this.userName)));

     // Find the endpoint
     ServiceEndpoint simpleEndpoint =

this.chatServiceHost.Description.Endpoints.Find(typeof(ISimpleChatService));

     // Add our behavior to the endpoint before opening it
     simpleEndpoint.Behaviors.Add(endpointDiscoveryBehavior);

     ShowStatus("Opening chat service...");
     chatServiceHost.BeginOpen(
         (result) =>
         {
             chatServiceHost.EndOpen(result);
             ShowStatus("Chat service ready");
         },
         null);
}



(Code Snippet – Service Discovery – OpenServices Method VB)
Visual Basic
Private Sub OpenServices()
    ' Create a singleton instance for the host
    Dim chatService As New ChatService(Me)
    Me.chatServiceHost = New ServiceHost(chatService, Me._localAddress)

     ' Create a discovery behavior
     Dim endpointDiscoveryBehavior As New EndpointDiscoveryBehavior()

     ' Add an extension element with the username
       endpointDiscoveryBehavior.Extensions.Add(<root><Name><%= Me.UserName
   %></Name></root>)

       ' Find the endpoint
       Dim simpleEndpoint =
   Me.chatServiceHost.Description.Endpoints.Find(GetType(ISimpleChatService))

          ' Add our behavior to the endpoint before opening it
          simpleEndpoint.Behaviors.Add(endpointDiscoveryBehavior)

       Me.ShowStatus("Opening chat service...")
       Me.chatServiceHost.BeginOpen(Sub(result)
                                        chatServiceHost.EndOpen(result)
                                        Me.ShowStatus("Chat service ready")
                                    End Sub, Nothing)
   End Sub



       Note: (For Visual Basic users) The above code snippet uses implicit typing. You will need to set
       Option Infer On in the VB file or set Option Infer at the project level.



5. Next you need to add code to look for this metadata when doing discovery of other services.
   Add the GetPeerName method which returns the first node named “Name” from the extension
   metadata right after the OpenServices method.
   (Code Snippet – Service Discovery – GetPeerName Method CSharp)
   C#
   private static string GetPeerName(EndpointDiscoveryMetadata metadata)
   {
       XElement peerNameElement =
           metadata.Extensions.Elements("Name").FirstOrDefault();

          if (peerNameElement != null)
              return peerNameElement.Value;

          return null;
   }



   (Code Snippet – Service Discovery – GetPeerName Method VB)
   Visual Basic
   Private Shared Function GetPeerName(ByVal metadata As
   EndpointDiscoveryMetadata) As String
       Dim peerNameElement = metadata.Extensions.<Name>
             If peerNameElement IsNot Nothing Then Return peerNameElement.Value

            Return Nothing
        End Function



    6. Now that the name is discoverable as well as the service endpoint, you need to modify the
       PopulateUserList method to invoke the GetPeerName method. If the name node exists, the
       PeerUser class will use it as the display name in the list box. To do this, replace the current call
       to the AddUser method with the following one.
        C#
        private void PopulateUserList(
            EndpointDiscoveryMetadata endpointDiscoveryMetadata)
        {
            if (!EndpointIsSelf(endpointDiscoveryMetadata.Address.Uri))
            {
                this.AddUser(new PeerUser(GetPeerName(endpointDiscoveryMetadata),
        endpointDiscoveryMetadata.Address));
                this.EnableUsersBox();
            }
        }



        Visual Basic
        Private Sub PopulateUserList(
                        ByVal endpointDiscoveryMetadata As EndpointDiscoveryMetadata)
            If Not Me.EndpointIsSelf(endpointDiscoveryMetadata.Address.Uri) Then
                Me.AddUser(New PeerUser(GetPeerName(endpointDiscoveryMetadata),
        endpointDiscoveryMetadata.Address))
                Me.EnableUsersBox()
            End If
        End Sub



    7. Press Ctrl+Shift+B to build the solution.



Next Step
Exercise 2: Verification


Exercise 2: Verification
    1. Press Ctrl+F5 to start an instance of the DiscoveryChat application without debugging.
2. Switch back to Visual Studio and press Ctrl+F5 again to launch another instance of the
   application.
3. Switch to one of the instances and setup the chat as follows:
       a. User Name: Fred
       b. Click Sign In
4. Switch to the other DiscoveryChat instance and setup the chat as follows:
       a. User Name: Wilma
       b. Click Sign In
5. Wilma’s chat window will discover Fred’s after signing in. Switch to Fred’s chat window and
   click on the Discover Users button to find Wilma.
6. You should see the other instance’s username appear in the Available Users pane. You can
   double-click on it to start a chat.




   Figure 5
   Extensions allow us to send the username in discovery metadata


7. Close both instances of the DiscoveryChat application.
Next Step
Exercise 3: Announcements



Exercise 3: Announcements
Most chat applications notify you when other users sign in. This application can discover other users but
it would be better if you were notified when other users sign in. Discovery supports this feature with
announcements.
Task 1 – Enabling Announcement Endpoint
    1. Open the starting solution Begin.sln located under %TrainingKitInstallFolder%\
       Labs\WCFServiceDiscovery\Source\Ex3-Announcements\Begin and choosing the language of
       your preference (C# or VB). Alternatively, you may continue working with the solution obtained
       after completing the previous exercise.


                Watch Out
         Visual Studio 2010 must be run in elevated mode. To do this, right-click the Visual Studio 2010
         icon and select Run as Administrator.



    2. Open the App.config file in the DiscoveryChat project.
    3. To take advantage of announcements, you will need to add an announcement endpoint. To do
       this, locate the DiscoveryBehavior behavior you added earlier in this lab and modify it as shown
       in the following code.
        XML
        <behavior name="DiscoveryBehavior">
          <serviceDiscovery>
            <announcementEndpoints>
              <endpoint name="udpEndpointName"
                        kind="udpAnnouncementEndpoint"/>
            </announcementEndpoints>
          </serviceDiscovery>
        </behavior>


                Announcement Endpoint
     Adding an announcement endpoint to the discovery service behavior creates a default
     announcement client for the service. This guarantees that the service will send an online and
     offline announcement when the service is opened and closed respectively.



4. Now you need to add an announcement service to our code to receive announcement
   messages. Open SimpleChat.cs (C#) or SimpleChat.vb (Visual Basic) and declare the following
   member fields; you can do that after the discoveryClient member declaration.
   (Code Snippet – Service Discovery – AnnouncementService Member CSharp)
   C#
   private AnnouncementService announcementService;
   private ServiceHost announcementServiceHost;



   (Code Snippet – Service Discovery – AnnouncementService Member VB)
   Visual Basic
   Private WithEvents announcementService As AnnouncementService
   Private announcementServiceHost As ServiceHost



5. Create the OpenAnnouncementService method as shown in the following code.
   (Code Snippet – Service Discovery – OpenAnnouncementService Method CSharp)
   C#
   private void OpenAnnouncementService()
   {
       this.announcementService = new AnnouncementService();

        // Add event handlers
        this.announcementService.OnlineAnnouncementReceived +=
            new EventHandler<AnnouncementEventArgs>(this.OnOnlineAnnouncement);
        this.announcementService.OfflineAnnouncementReceived +=
            new EventHandler<AnnouncementEventArgs>(this.OnOfflineAnnouncement);

        // Create the service host with a singleton
        this.announcementServiceHost = new ServiceHost(this.announcementService);

       // Add the announcement endpoint
       this.announcementServiceHost.AddServiceEndpoint(new
   UdpAnnouncementEndpoint());

        // Open the host async
        this.announcementServiceHost.BeginOpen(
            (result) =>
              {
                  announcementServiceHost.EndOpen(result);
              },
              null);
   }



   (Code Snippet – Service Discovery – OpenAnnouncementService Method VB)
   Visual Basic
   Private Sub OpenAnnouncementService()
       Me.announcementService = New AnnouncementService()

          ' Create the service host with a singleton
          Me.announcementServiceHost = New ServiceHost(Me.announcementService)

       ' Add the announcement endpoint
       Me.announcementServiceHost.AddServiceEndpoint(New
   UdpAnnouncementEndpoint())

       ' Open the host async
       Me.announcementServiceHost.BeginOpen(Sub(result)
                                        announcementServiceHost.EndOpen(result)
                                            End Sub, Nothing)
   End Sub


             Announcement Service
       The self-hosted implementation of the announcement service exposes two different events
       you might be interested in when using Announcements: OnlineAnnouncementReceived and
       OfflineAnnouncementReceived. Those events are fired when Online (Hello) and Offline (Bye)
       announcement messages are received respectively.



6. Now you need to implement the handlers for these announcements. When new users come
   online you will add them to the list. Add the OnOnlineAnnouncement method as shown in the
   following code.
   (Code Snippet – Service Discovery – OnOnlineAnnouncement Method CSharp)
   C#
   private void OnOnlineAnnouncement(object sender, AnnouncementEventArgs e)
   {
       EndpointDiscoveryMetadata metadata =
           e.EndpointDiscoveryMetadata;

          // We are looking for services that
          // implement the ISimpleChatService contract
        FindCriteria criteria =
            new FindCriteria(typeof(ISimpleChatService));

        if (criteria.IsMatch(metadata))
        {
            if (this.GetUser(metadata.Address.Uri) == null)
            {
                this.PopulateUserList(metadata);
            }
        }
   }



   (Code Snippet – Service Discovery – OnOnlineAnnouncement Method VB)
   Visual Basic
   Private Sub OnOnlineAnnouncement(ByVal sender As Object,
                                    ByVal e As AnnouncementEventArgs) Handles
   announcementService.OnlineAnnouncementReceived

        Dim metadata = e.EndpointDiscoveryMetadata

        ' We are looking for services that
        ' implement the ISimpleChatService contract
        Dim criteria As New FindCriteria(GetType(ISimpleChatService))

       If criteria.IsMatch(metadata) And Me.GetUser(metadata.Address.Uri) Is
   Nothing Then Me.PopulateUserList(metadata)
   End Sub



7. When users go offline you will remove them and close any active chat windows. Add the
   OnOfflineAnnouncement method as shown in the following code.
   (Code Snippet – Service Discovery – OnOfflineAnnouncement Method CSharp)
   C#
   private void OnOfflineAnnouncement(object sender, AnnouncementEventArgs e)
   {
       EndpointDiscoveryMetadata metadata =
           e.EndpointDiscoveryMetadata;

        FindCriteria criteria =
            new FindCriteria(typeof(ISimpleChatService));

        if (criteria.IsMatch(metadata))
        {
            this.RemoveUser(metadata.Address.Uri);
        }
   }



   (Code Snippet – Service Discovery – OnOfflineAnnouncement Method VB)
   Visual Basic
   Private Sub OnOfflineAnnouncement(ByVal sender As Object,
                                     ByVal e As AnnouncementEventArgs) Handles
   announcementService.OfflineAnnouncementReceived

        Dim metadata = e.EndpointDiscoveryMetadata

        Dim criteria As New FindCriteria(GetType(ISimpleChatService))

        If criteria.IsMatch(metadata) Then Me.RemoveUser(metadata.Address.Uri)
   End Sub



8. Locate the OpenServices method and add a call to OpenAnnouncementService service at the
   end of the method implementation.
   C#
        this.ShowStatus("Opening chat service...");
        this.chatServiceHost.BeginOpen(
            (result) =>
            {
                chatServiceHost.EndOpen(result);
                this.ShowStatus("Chat service ready");
            },
            null);

        this.OpenAnnouncementService();
   }

   Visual Basic
        Me.ShowStatus("Opening chat service...")
        Me.chatServiceHost.BeginOpen(Sub(result)
                                         chatServiceHost.EndOpen(result)
                                         Me.ShowStatus("Chat service ready")
                                     End Sub, Nothing)

       Me.OpenAnnouncementService()
   End Sub



9. Press CTRL+SHIFT+B to build the solution.
Next Step
Exercise 3: Verification


Exercise 3: Verification
Now you will test the application to verify that the clients are able to detect the announcements.
    1. Press CTRL+F5 to start an instance of the DiscoveryChat application without debugging.
    2. Switch back to Visual Studio and press Ctrl+F5 again to launch another instance of the
       DiscoveryChat application.
    3. Switch to one of the instances of DiscoveryChat and setup the chat as follows:
            a. User Name: Fred
            b. Click Sign in
    4. Switch to the other DiscoveryChat instance and setup the chat as follows:
            a. User Name: Wilma
            b. Click Sign in
    5. When Wilma signed in, Fred’s chat window should detect the online announcement and
       automatically add her to the list of available users.
        Figure 6
        Users are automatically discovered


    6. Verify that the offline announcement is working by clicking the Sign Out button on the Wilma's
       chat window. This should cause Fred's window to remove Wilma from the list of available
       users.
    7. Try signing in and out from both applications. Each application is aware of the online / offline
       announcements of the other.
    8. Close both instances of the DiscoveryChat application.



Next Step
Exercise 4: Discovery Proxy



Exercise 4: Discovery Proxy
All the previous exercises have relied on a well known UDP multicast endpoint for discovery. The port
and multicast address are specified by the WS-Discovery protocol documentation. The utilization of this
multicast discovery is referred to as ad hoc discovery. Ad hoc discovery is limited to recognizing only
services on the same subnet. Managed discovery allows you to locate services no matter where they
are, as long as they are registered with a discovery proxy.
The following sequential diagram shows how a Discovery Proxy responds on behalf of the target
services:
Figure 7
Extensions allow us to send the username in discovery metadata




        Discovery Proxy
 For more information about Discovery Proxy and WS-Discovery message exchanges, see section 3 of
 the WS-Discovery Specification.



Task 1 – Creating a DiscoveryProxy
The System.ServiceModel.Discovery namespace includes a base class to help you in building your
Discovery Proxy. To implement your proxy you will have to override a number of methods provided by
the System.ServiceModel.Discovery.DiscoveryProxy class. These methods are all asynchronous to
guarantee maximum scalability for the server. For this Hands-on Lab, you will focus on supporting only
Announcements in the proxy. When a chat client goes online or offline the cache of metadata will be
updated. Then you will respond to find requests when a client wants to query the proxy for list of chat
clients.
    1. Open Discovery.sln from the begin folder located at
       %TrainingKitInstallFolder%\Labs\WCFServiceDiscovery\Source\Ex4-DiscoveryProxy folder,
       choosing the language of your preference (C# or VB). Alternatively, you may continue working
       with the solution obtained after completing the previous exercise.
            Watch Out
     Visual Studio 2010 must be run in elevated mode. To do this, right-click the Visual Studio 2010
     icon and select Run as Administrator.



2. Add a new project of type Console Application. To do this, in Solution Explorer right-click the
   Begin solution and point to Add and click New Project. Click Windows in either Visual C# or
   Visual Basic Installed Templates list. In the Templates pane, click Console Application, and
   make sure that .NET Framework 4.0 is selected as the target runtime. Finally, set the project's
   name to DiscoveryProxy, and then click OK.




   Figure 8
   Adding a new Console Application Project named “DiscoveryProxy” (C#)
   Figure 9
   Adding a new Console Application Project named “DiscoveryProxy” (Visual Basic)


3. Change the target framework of the DiscoveryProxy project to .NET Framework 4. To do this in
   Solution Explorer, right-click the DiscoveryProxy project and select Properties.
       a. For C# projects, click the Application tab and select the .NET Framework 4 option in the
          Target Framework dropdown list.
       Figure 10
       Changing the target framework in C# projects


   b. For Visual Basic projects, select the Compile tab and click the Advanced Compile
      Options button. In the Advanced Compiler Settings dialog box select the .NET
      Framework 4 option of the Target Framework dropdown list and click OK.




       Figure 11
       Changing the target framework in Visual Basic projects


Click Yes in the Target Framework Change message box to confirm that you want to change the
target framework.
   Figure 12
   Confirmation dialog



     Note: By default the Console Application project template shipped with Visual Studio 2010
     creates a project that targets the .NET Framework Client Profile 4. The solution will not
     compile if you have projects that target different .NET Framework profiles.



4. Right-click DiscoveryProxy project and click Add Reference. Using the Projects tab, add a
   project reference to the DiscoveryChat project. Repeat these steps, using the .NET tab to add a
   reference to the System.ServiceModel and System.ServiceModel.Discovery libraries.
5. Press Ctrl+Shift+B to build the solution.
6. (For Visual Basic users only) Add the following namespaces imports at project level to be used
   during the exercise. To do this, right-click DiscoveryProxy project and select Properties. Open
   the References page, and in the Imported namespaces section, select the following
   namespaces:
       a. System.Net
       b. System.ServiceModel
       c. System.ServiceModel.Discovery
       d. System.Collections.ObjectModel
       e. Microsoft.Samples.Discovery.Contracts

     Note: For C# users, specific using directives will be added to each file when needed.



7. Create the ChatDiscoveryProxy class. In Solution Explorer, right-click DiscoveryProxy project,
   point to Add, and then click Class. Type ChatDiscoveryProxy in the Name box.
Figure 13
Adding a new Class called “ChatDiscoveryProxy” (C#)
   Figure 14
   Adding a new Class called “ChatDiscoveryProxy” (Visual Basic)


8. (For C# users only) Add the following using directives for the new class.
   (Code Snippet – Service Discovery – ChatDiscoveryProxy Using Statements CSharp)
   C#
   using   System.ServiceModel;
   using   System.ServiceModel.Discovery;
   using   System.Collections.ObjectModel;
   using   Microsoft.Samples.Discovery.Contracts;



9. Make your class inherit from System.ServiceModel.Discovery.DiscoveryProxy base class.
   C#
   public class ChatDiscoveryProxy : System.ServiceModel.Discovery.DiscoveryProxy

   Visual Basic
   Public Class ChatDiscoveryProxy
       Inherits Discovery.DiscoveryProxy
10. In this lab, your class will maintain a thread-safe in-memory cache. For this reason, you need to
    make your WCF service instance to be a singleton. And because it is thread safe and you want
    to guarantee maximum scalability, you will also allow multiple concurrent calls. Modify the
    ChatDiscoveryProxy class signature, to add the following attributes.
   C#
   [ServiceBehavior(
       InstanceContextMode = InstanceContextMode.Single,
       ConcurrencyMode = ConcurrencyMode.Multiple)]
   public class ChatDiscoveryProxy : System.ServiceModel.Discovery.DiscoveryProxy

   Visual Basic
   <ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single,
   ConcurrencyMode:=ConcurrencyMode.Multiple)>
   Public Class ChatDiscoveryProxy
       Inherits Discovery.DiscoveryProxy



     Note: Using an in-memory cache means that if our discovery proxy host shutdowns, all
     information regarding services is lost. More robust implementations would use durable
     storage, such as a database; this will insure that service metadata is not lost even when the
     service is down.



11. You will need to create a thread safe collection class to hold instances of services you have
    discovered. Right-click DiscoveryProxy project, point to Add, and then click Class. Type
    ChatServiceCollection in the Name box.

     Note: For more information see, Collections and Synchronization (Thread Safety).



12. (For C# users only) Add the following using directives for the new class.
   C#
   using System.ServiceModel.Discovery;



13. Mark your class as internal (C#) or Friend (Visual Basic), and make it inherit from the
    SynchronizedKeyedCollection base class.
   C#
   internal class ChatServiceCollection : SynchronizedKeyedCollection<Uri,
   EndpointDiscoveryMetadata>

   Visual Basic
   Friend Class ChatServiceCollection
       Inherits SynchronizedKeyedCollection(Of Uri, EndpointDiscoveryMetadata)



14. Implement the GetKeyForItem method of the SynchronizedKeyedCollection class as shown in
    following code.
   (Code Snippet – Service Discovery – GetKeyForItem Method CSharp)
   C#
   internal class ChatServiceCollection :
       SynchronizedKeyedCollection<Uri, EndpointDiscoveryMetadata>
   {

        protected override Uri GetKeyForItem(EndpointDiscoveryMetadata item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

             return item.Address.Uri;
        }

   }



   (Code Snippet – Service Discovery – GetKeyForItem Method VB)
   Visual Basic
   Friend Class ChatServiceCollection
       Inherits SynchronizedKeyedCollection(Of Uri, EndpointDiscoveryMetadata)

       Protected Overrides Function GetKeyForItem(ByVal item As
   EndpointDiscoveryMetadata) As Uri
           If item Is Nothing Then Throw New ArgumentNullException("item")

            Return item.Address.Uri
        End Function

   End Class



15. Switch back to the ChatDiscoveryProxy class implementation, and add a static
    ChatServiceCollection property and its backing field as shown in following code.
   (Code Snippet – Service Discovery – ChatServiceCollection Member CSharp)
   C#
   public class ChatDiscoveryProxy : DiscoveryProxy
   {
      private static ChatServiceCollection cache = new ChatServiceCollection();

        internal static ChatServiceCollection Cache
        {
            get { return cache; }
        }
   }



   (Code Snippet – Service Discovery – ChatServiceCollection Member VB)
   Visual Basic
   Public Class ChatDiscoveryProxy
       Inherits Discovery.DiscoveryProxy

        Private Shared _cache As New ChatServiceCollection()

       Friend Shared ReadOnly Property Cache As ChatServiceCollection
           Get
               Return _cache
           End Get
       End Property
   End Class



16. There are several helper classes that you will need to complete the service. These classes are
    already developed for simplicity, and you will have to add them to your solution. To do this,
    right-click on the DiscoveryProxy project, point to Add and click Existing Item. Then, browse to
    the %TrainingKitInstallFolder%\Labs\WCFServiceDiscovery\Source\Assets\DiscoveryProxy
    folder, choosing the language of your preference (C# or VB) and add the following files:
        a. AsyncResult.cs (C#) or AsyncResult.vb (Visual Basic)
        b. CompletedAsyncResult.cs (C#) or CompletedAsyncResult.vb (Visual Basic)
        c. FindAsyncResult.cs (C#) or FindAsyncResult.vb (Visual Basic)
        d. EndpointDiscoveryMetadataExtensions.cs (C#) or
           EndpointDiscoveryMetadataExtensions.vb (Visual Basic)
17. When an online announcement is received, you need to determine if the service is one that you
    want to cache. If so, add it to our cache. Override the OnBeginOnlineAnnouncement method
    inside the ChatDiscoveryProxy class by adding the following code.
   (Code Snippet – Service Discovery – OnBeginOnlineAnnouncement Method CSharp)
   C#
protected override IAsyncResult
OnBeginOnlineAnnouncement(DiscoveryMessageSequence messageSequence,
                                                EndpointDiscoveryMetadata
endpointDiscoveryMetadata,
                                                AsyncCallback callback,
                                                object state)
{
    if (endpointDiscoveryMetadata == null)
    {
        throw new ArgumentNullException("endpointDiscoveryMetadata");
    }

    // We care only about ISimpleChatService services
    FindCriteria criteria = new FindCriteria(typeof(ISimpleChatService));

    if (criteria.IsMatch(endpointDiscoveryMetadata))
    {
        endpointDiscoveryMetadata.WriteLine("Adding");
        Cache.Add(endpointDiscoveryMetadata);
    }

    return new CompletedAsyncResult(callback, state);
}



(Code Snippet – Service Discovery – OnBeginOnlineAnnouncement Method VB)
Visual Basic
Protected Overrides Function OnBeginOnlineAnnouncement(ByVal messageSequence
As DiscoveryMessageSequence, ByVal endpointDiscoveryMetadata As
EndpointDiscoveryMetadata, ByVal callback As AsyncCallback, ByVal state As
Object) As IAsyncResult
    If endpointDiscoveryMetadata Is Nothing Then Throw New
ArgumentNullException("endpointDiscoveryMetadata")

    ' We care only about ISimpleChatService services
    Dim criteria As New FindCriteria(GetType(ISimpleChatService))

    If criteria.IsMatch(endpointDiscoveryMetadata) Then
        endpointDiscoveryMetadata.WriteLine("Adding")
        Cache.Add(endpointDiscoveryMetadata)
    End If

    Return New CompletedAsyncResult(callback, state)
End Function
18. When an offline announcement message is received, you want to remove the metadata from
    the cache if it is there. Override the OnBeginOfflineAnnouncement method by adding the
    following code.
   (Code Snippet – Service Discovery – OnBeginOfflineAnnouncement Method CSharp)
   C#
   protected override IAsyncResult
   OnBeginOfflineAnnouncement(DiscoveryMessageSequence messageSequence,
   EndpointDiscoveryMetadata endpointDiscoveryMetadata, AsyncCallback callback,
   object state)
   {
       try
       {
           if (endpointDiscoveryMetadata == null)
           {
               throw new ArgumentNullException("endpointDiscoveryMetadata");
           }

            // We care only about ISimpleChatService services
            FindCriteria criteria = new FindCriteria(typeof(ISimpleChatService));

            if (criteria.IsMatch(endpointDiscoveryMetadata))
            {
                endpointDiscoveryMetadata.WriteLine("Removing");
                Cache.Remove(endpointDiscoveryMetadata.Address.Uri);
            }
        }
        catch (KeyNotFoundException)
        {
            // No problem if it does not exist in the cache
        }

        return new CompletedAsyncResult(callback, state);
   }



   (Code Snippet – Service Discovery – OnBeginOfflineAnnouncement Method VB)
   Visual Basic
   Protected Overrides Function OnBeginOfflineAnnouncement(ByVal messageSequence
   As DiscoveryMessageSequence, ByVal endpointDiscoveryMetadata As
   EndpointDiscoveryMetadata, ByVal callback As AsyncCallback, ByVal state As
   Object) As IAsyncResult
       Try
           If endpointDiscoveryMetadata Is Nothing Then Throw New
   ArgumentNullException("endpointDiscoveryMetadata")

            ' We care only about ISimpleChatService services
             Dim criteria As New FindCriteria(GetType(ISimpleChatService))

            If criteria.IsMatch(endpointDiscoveryMetadata) Then
                endpointDiscoveryMetadata.WriteLine("Removing")
                Cache.Remove(endpointDiscoveryMetadata.Address.Uri)
            End If
        Catch e1 As KeyNotFoundException
            ' No problem if it does not exist in the cache
        End Try

        Return New CompletedAsyncResult(callback, state)
   End Function



19. Now you can override the OnBeginFind method, which is called when a client issues a Discovery
    Find request to the proxy. Here is where you can search the cache of known service endpoints
    and reply to the client Find request with any matching endpoints.
   (Code Snippet – Service Discovery – OnBeginFind Method CSharp)
   C#
   protected override IAsyncResult OnBeginFind(FindRequestContext
   findRequestContext, AsyncCallback callback, object state)
   {
       if (findRequestContext == null)
       {
           throw new ArgumentNullException("findRequestContext");
       }

        Console.WriteLine(
            "Find request for contract {0}",
            findRequestContext.Criteria.ContractTypeNames.FirstOrDefault());

        // Query to find the matching endpoints
        var query = from service in Cache
                    where findRequestContext.Criteria.IsMatch(service)
                    select service;

        // Collection to contain the results of the query
        var matchingEndpoints = new Collection<EndpointDiscoveryMetadata>();

        // Execute the query and add the matching endpoints
        foreach (EndpointDiscoveryMetadata metadata in query)
        {
            metadata.WriteLine("\tFound");
            matchingEndpoints.Add(metadata);
            findRequestContext.AddMatchingEndpoint(metadata);
        }
        return new FindAsyncResult(matchingEndpoints, callback, state);
   }



   (Code Snippet – Service Discovery – OnBeginFind Method VB)
   Visual Basic
   Protected Overrides Function OnBeginFind(ByVal findRequestContext As
   FindRequestContext, ByVal callback As AsyncCallback, ByVal state As Object) As
   IAsyncResult
       If findRequestContext Is Nothing Then Throw New
   ArgumentNullException("findRequestContext")

       Console.WriteLine("Find request for contract {0}",
   findRequestContext.Criteria.ContractTypeNames.FirstOrDefault())

        ' Query to find the matching endpoints
        Dim query = From service In Cache
                    Where findRequestContext.Criteria.IsMatch(service)
                    Select service

        ' Collection to contain the results of the query
        Dim matchingEndpoints = New Collection(Of EndpointDiscoveryMetadata)()

        ' Execute the query and add the matching endpoints
        For Each metadata As EndpointDiscoveryMetadata In query
            metadata.WriteLine(Constants.vbTab & "Found")
            matchingEndpoints.Add(metadata)
            findRequestContext.AddMatchingEndpoint(metadata)
        Next metadata

        Return New FindAsyncResult(matchingEndpoints, callback, state)
   End Function



20. Override the OnEndFind method to complete the find operation.
   (Code Snippet – Service Discovery – OnEndFind Method CSharp)
   C#
   protected override void OnEndFind(IAsyncResult result)
   {
       FindAsyncResult.End(result);
   }



   (Code Snippet – Service Discovery – OnEndFind Method VB)
   Visual Basic
   Protected Overrides Sub OnEndFind(ByVal result As IAsyncResult)
       FindAsyncResult.End(result)
   End Sub

     Note: In this lab solution, the Discovery Proxy is implemented as a Console Application.
     Production environments would use a more robust hosting solution such as a Windows
     Service.



21. Override the rest of the required methods declared abstract in
    System.ServiceModel.Discovery.DiscoveryProxy class.
   (Code Snippet – Service Discovery – DiscoveryProxy Abstract Methods CSharp)
   C#
   protected override IAsyncResult OnBeginResolve(ResolveCriteria
   resolveCriteria, AsyncCallback callback, object state)
   {
       return new CompletedAsyncResult(callback, state);
   }

   protected override EndpointDiscoveryMetadata OnEndResolve(IAsyncResult result)
   {
       return CompletedAsyncResult<EndpointDiscoveryMetadata>.End(result);
   }

   protected override void OnEndOfflineAnnouncement(IAsyncResult result)
   {
       CompletedAsyncResult.End(result);
   }

   protected override void OnEndOnlineAnnouncement(IAsyncResult result)
   {
       CompletedAsyncResult.End(result);
   }



   (Code Snippet – Service Discovery – DiscoveryProxy Abstract Methods VB)
   Visual Basic
   Protected Overrides Function OnBeginResolve(ByVal resolveCriteria As
   ResolveCriteria, ByVal callback As AsyncCallback, ByVal state As Object) As
   IAsyncResult
       Return New CompletedAsyncResult(callback, state)
   End Function

   Protected Overrides Function OnEndResolve(ByVal result As IAsyncResult) As
   EndpointDiscoveryMetadata
       Return CompletedAsyncResult(Of EndpointDiscoveryMetadata).End(result)
   End Function

   Protected Overrides Sub OnEndOfflineAnnouncement(ByVal result As IAsyncResult)
       CompletedAsyncResult.End(result)
   End Sub

   Protected Overrides Sub OnEndOnlineAnnouncement(ByVal result As IAsyncResult)
       CompletedAsyncResult.End(result)
   End Sub



22. Now you need to modify the Main method to create a ServiceHost for our ChatDiscoveryProxy
    service. Open Program.cs (C#) or Module1.vb (Visual Basic) in the DiscoveryProxy project.
23. (For C# users only) Add the following using directives to Program.cs.
   C#
   using System.Net;
   using System.ServiceModel;
   using System.ServiceModel.Discovery;



24. For hosting the ChatDiscoveryProxy service you will create the DiscoveryEndpoint endpoint for
    the service host. Add the following method inside the Program class (C#) or Module1 module
    (Visual Basic).


            Note
     In this lab solution, you will be using TCP port 8001 for our proxy service.



   (Code Snippet – Service Discovery – HostDiscoveryEndpoint Method CSharp)
   C#
   private static ServiceHost HostDiscoveryEndpoint(string hostName)
   {
       // Create a new ServiceHost with a singleton ChatDiscovery Proxy
       ServiceHost myProxyHost = new
           ServiceHost(new ChatDiscoveryProxy());

        string proxyAddress = "net.tcp://" +
            hostName + ":8001/discoveryproxy";

        // Create the discovery endpoint
        DiscoveryEndpoint discoveryEndpoint =
            new DiscoveryEndpoint(
                new NetTcpBinding(),
                new EndpointAddress(proxyAddress));
        discoveryEndpoint.IsSystemEndpoint = false;

        // Add UDP Annoucement endpoint
        myProxyHost.AddServiceEndpoint(new UdpAnnouncementEndpoint());

        // Add the discovery endpoint
        myProxyHost.AddServiceEndpoint(discoveryEndpoint);

        myProxyHost.Open();
        Console.WriteLine("Discovery Proxy {0}",
            proxyAddress);

        return myProxyHost;
   }



   (Code Snippet – Service Discovery – HostDiscoveryEndpoint Method VB)
   Visual Basic
   Private Function HostDiscoveryEndpoint(ByVal hostName As String) As
   ServiceHost
       ' Create a new ServiceHost with a singleton ChatDiscovery Proxy
       Dim myProxyHost As New ServiceHost(New ChatDiscoveryProxy())

        Dim proxyAddress = "net.tcp://" & hostName & ":8001/discoveryproxy"

       ' Create the discovery endpoint
       Dim discoveryEndpoint As New DiscoveryEndpoint(New NetTcpBinding(), New
   EndpointAddress(proxyAddress))

        discoveryEndpoint.IsSystemEndpoint = False

        ' Add UDP Annoucement endpoint
        myProxyHost.AddServiceEndpoint(New UdpAnnouncementEndpoint())

        ' Add the discovery endpoint
        myProxyHost.AddServiceEndpoint(discoveryEndpoint)

        myProxyHost.Open()
        Console.WriteLine("Discovery Proxy {0}", proxyAddress)

       Return myProxyHost
   End Function



25. Modify the Main method as shown in the following code, to host the ChatDiscoveryProxy
    service.
       (Code Snippet – Service Discovery – DiscoveryProxy Main Method CSharp)
       C#
       static void Main(string[] args)
       {
           Console.Title = "DiscoveryProxy Service";
           Console.WriteLine("DiscoveryProxy Console Host");
           string hostName = Dns.GetHostName();

            using (ServiceHost proxyHost = HostDiscoveryEndpoint(hostName))
            {
                Console.WriteLine("Press <Enter> to exit");
                Console.ReadLine();
                proxyHost.Close();
            }
       }



       (Code Snippet – Service Discovery – DiscoveryProxy Main Method VB)
       Visual Basic
       Sub Main()
           Console.Title = "DiscoveryProxy Service"
           Console.WriteLine("DiscoveryProxy Console Host")
           Dim hostName = Dns.GetHostName()

           Using proxyHost As ServiceHost = HostDiscoveryEndpoint(hostName)
               Console.WriteLine("Press <Enter> to exit")
               Console.ReadLine()
               proxyHost.Close()
           End Using
       End Sub



    26. Press Ctrl+Shift+B to build the solution

Task 2 – Modifying the DiscoveryChat Application to use Managed Discovery
Now that you have implemented a discovery proxy, you need to modify the chat application to make
use of it.
    1. Open the SimpleChat.cs file (C#) or SimpleChat.vb (Visual Basic) from the DiscoveryChat
       project. You can open the code view by selecting the file and pressing F7.
    2. Locate the ManagedDiscovery method. The UI is already calling this method if the Managed
       Discovery radio button is checked. As you have already implemented the handlers for ad hoc
       discovery, the only thing pending is to add code to implement this managed discovery as shown
       in the following code.
(Code Snippet – Service Discovery – ManagedDiscovery Method CSharp)
C#
private void ManagedDiscovery()
{
    try
    {
        // Create an endpoint for the proxy
        DiscoveryEndpoint proxyEndpoint =
            new DiscoveryEndpoint(
                new NetTcpBinding(),
                new EndpointAddress(proxyAddressText.Text));

         // Create the DiscoveryClient with a proxy endpoint
         // for managed discovery
         this.discoveryClient = new DiscoveryClient(proxyEndpoint);

        // Same handlers as ad hoc discovery
        this.discoveryClient.FindCompleted +=
            new EventHandler<FindCompletedEventArgs>(this.OnFindCompleted);
        this.discoveryClient.FindProgressChanged +=
            new
EventHandler<FindProgressChangedEventArgs>(this.OnFindProgressChanged);

         // Setup the form for discovery
         this.ShowDiscoveryInProgress(true);

        this.discoveryClient.FindAsync(new
FindCriteria(typeof(ISimpleChatService)));
    }
    catch (UriFormatException)
    {
        MessageBox.Show(
            Resources.InvalidUriMessage,
            this.Text,
            MessageBoxButtons.OK,
            MessageBoxIcon.Information,
            MessageBoxDefaultButton.Button1,
            (MessageBoxOptions)0);
    }
}



(Code Snippet – Service Discovery – ManagedDiscovery Method VB)
Visual Basic
Private Sub ManagedDiscovery()
    Try
        ' Create an endpoint for the proxy
                Dim proxyEndpoint As New DiscoveryEndpoint(New NetTcpBinding(), New
        EndpointAddress(proxyAddressText.Text))

                  ' Create the DiscoveryClient with a proxy endpoint
                  ' for managed discovery
                  Me.discoveryClient = New DiscoveryClient(proxyEndpoint)

                  ' Setup the form for discovery
                  Me.ShowDiscoveryInProgress(True)

                Me.discoveryClient.FindAsync(New
        FindCriteria(GetType(ISimpleChatService)))
            Catch e1 As UriFormatException
                MsgBox(My.Resources.InvalidUriMessage, MsgBoxStyle.Information,
        Me.Text)
            End Try
        End Sub



    3. Locate the InitializeManagedDiscovery method and set the managed radio button Checked
       property to True.
        C#
        private void InitializeManagedDiscovery()
        {
            this.managedRadioButton.Checked = true;
        }

        Visual Basic
        Private Sub InitializeManagedDiscovery()
            Me.managedRadioButton.Checked = True
        End Sub



    4. Press Ctrl+Shift+B to build the solution.



Next Step
Exercise 4: Verification


Exercise 4: Verification
Now you will test the chat application to see that the clients are able to detect the announcements.
    1. Right-click the DiscoveryProxy project and click Set as StartUp project.
    2. Press F5 to start DiscoveryProxy console in the debugger.
   Figure 15
   DiscoveryProxy Service running



     Note: Windows Firewall may prompt you to allow the Proxy and/or Chat client access to use
     the network. It is safe to allow this.



3. Switch back to Visual Studio Solution Explorer and start two instances of the DiscoveryChat
   application by right-clicking the DiscoveryChat project, pointing to Debug and clicking Start
   New Instance.
4. Switch to one of the instances of the DiscoveryChat application and setup the chat as follows:
       a. User Name: Fred
       b. Click Sign in
5. Switch to the other DiscoveryChat instance and setup the chat as follows:
       a. User Name: Wilma
       b. Click Sign in
6. When Wilma signed in, her window found Fred from the managed discovery proxy. Fred's chat
   window should detect the online announcement from Wilma's window and automatically add
   her.
   Figure 16
   Managed discovery in action


7. Verify that the offline announcement is working by clicking the Sign Out button on Wilma's chat
   window. This should cause Fred's window to remove Wilma from the list of available users.
8. Try signing in and out from both applications. Each application is aware of the online / offline
   announcements of the other.
        Figure 17
        DiscoveryProxy Service in action


    9. Press Shift+F5 to stop debugging



Next Step
Exercise 5: Legacy Discovery



Exercise 5: Legacy Discovery
In the previous exercise, it might appear that having managed discovery really did not offer anything
new. It is difficult to see the improvements, unless you have chat clients on different subnets. If you did,
then you would now be able to see the other chat clients. Up to this point, the only way the discovery
proxy would know about chat clients is if they used announcements to make their presence known.
Another powerful benefit of managed discovery is that the discovery proxy can get the list of server
instances by other means. It could read a list of available services from a database, a configuration file
or use a custom protocol to communicate with service instances.
In the lab solution there is a different chat client project called LegacyChat. This chat client uses the
same ISimpleChat interface that the DiscoveryChat application uses, however it does not support WS-
Discovery, instead it uses a custom protocol to communicate to the proxy server.
The problem with this is that a new chat client based on discovery will not discover the legacy chat client
and the legacy client will not discover new chat clients. In this exercise, you will bridge the two clients by
creating discovery metadata from the custom protocol that the legacy client uses. When you are
finished, both chat applications will be able to see each other using managed discovery with a proxy.
Task 1 – Implementing the Legacy Chat Proxy
    1. Open Begin.sln from the begin folder located at
       %TrainingKitInstallFolder%\Labs\WCFServiceDiscovery\Source\Ex5-LegacyDiscovery folder,
       choosing the language of your preference (C# or VB). Alternatively, you may continue working
       with the solution obtained after completing the previous exercise.


                Watch Out
         Visual Studio 2010 must be run in elevated mode. To do this, right-click the Visual Studio 2010
         icon and select Run as Administrator.



    2. The existing legacy contract uses a DataContract, therefore you need to add a reference to the
       System.Runtime.Serialization namespace. To do this, perform the following steps.
            a. Right-click the DiscoveryProxy project, and click Add Reference.
            b. From the .NET tab select the System.Runtime.Serialization namespace and click Ok.
    3. Add the LegacyChat project from the Assets folder. To do this, Right-click the Begin solution,
       point to Add, and then click Existing project. Then, browse to the
       %TrainingKitInstallFolder%\Labs\WCFServiceDiscovery\Source\Assets\LegacyChat folder,
       choosing the language of your preference (C# or VB). Finally, select the LegacyChat project and
       click Open.
    4. Right-click the LegacyChat project, and click Add Reference. From the Projects tab select the
       DiscoveryProxy project and click Ok.
    5. Add to the DiscoveryProxy project the ILegacyChatProxy.cs file (C#) or ILegacyChatProxy.vb
       (Visual Basic) from the Assets folder. To do this, right-click the DiscoveryProxy project, point to
       Add, and then click Existing Item. Browse to the
       %TrainingKitInstallFolder%\Labs\WCFServiceDiscovery\Source\Assets\LegacyChatProxy
       folder, choosing the language of your preference (C# or VB). Finally, select the file and then click
       Add.
Figure 18
Adding the provided ILegacyChatProxy.cs file (C#)




Figure 19
   Adding the provided ILegacyChatProxy.vb file (Visual Basic)


6. Press Ctrl+Shift+B to build the solution.
7. (For Visual Basic users only) Add the following namespaces imports at project level to be used
   during the exercise. To do this, right-click the DiscoveryProxy project and select Properties.
   Open the References page, and in the Imported namespaces section, select the following
   namespaces:
       a. System.Xml
       b. DiscoveryProxy.Contracts

     Note: For C# users, specific using directives will be added to each file when needed.



8. The legacy client uses the ILegacyChatProxy interface to register with a proxy server. The
   DiscoveryProxy server will have to implement this interface. Right-click the DiscoveryProxy
   project, point to Add, and then click Class. Type LegacyChatProxy in the Name box and click
   Add. Perform the following steps to set up this class.
       a. (For C# users only) Add the following using directives
           C#
           using   System.ServiceModel;
           using   System.ServiceModel.Discovery;
           using   System.Xml;
           using   System.Xml.Linq;
           using   Microsoft.Samples.Discovery;
           using   Microsoft.Samples.Discovery.Contracts;
           using   DiscoveryProxy.Contracts;



       b. Add a ServiceBehavior attribute to the class signature, as shown in the following code.
           C#
           [ServiceBehavior(
               InstanceContextMode = InstanceContextMode.Single,
               ConcurrencyMode = ConcurrencyMode.Multiple)]
           class LegacyChatProxy

           Visual Basic
           <ServiceBehavior(
               InstanceContextMode:=InstanceContextMode.Single,
               ConcurrencyMode:=ConcurrencyMode.Multiple)>
           Public Class LegacyChatProxy
        c. Make the LegacyChatProxy class inherit from the ILegacyChatProxy interface.
           C#
           class LegacyChatProxy : ILegacyChatProxy

           Visual Basic
           Public Class LegacyChatProxy
               Implements ILegacyChatProxy



             Note: The ILegacyChatProxy interface declares three operations, Register, Unregister
             and GetUsers, that will be implemented through the exercise.



9. The legacy chat client discovery protocol calls the Register method to let the server know that a
   new chat client is available. To make legacy chat clients visible, you have to create
   EndpointDiscoveryMetadata and add it to the cache. Implement the Register method as shown
   in the following code.
   (Code Snippet – Service Discovery – Register Method CSharp)
   C#
   public void Register(Uri chatUri, string name)
   {
       // Create endpoint metadata from the legacy API
       var metadata = new EndpointDiscoveryMetadata()
       {
           Address = new EndpointAddress(chatUri),
           ContractTypeNames =
           {
               new XmlQualifiedName("SimpleChat",
   "http://sample.microsoft.com/wcf4")
           },
           Extensions =
           {
               new XElement("root",new XElement("Name", name))
           },
           ListenUris =
           {
               chatUri
           }
       };

        metadata.WriteLine("Adding legacy");

        // Add it to the cache
        ChatDiscoveryProxy.Cache.Add(metadata);
   }
   (Code Snippet – Service Discovery – Register Method VB)
   Visual Basic
   Public Sub Register(ByVal chatUri As System.Uri, ByVal name As String)
   Implements ILegacyChatProxy.Register
       ' Create endpoint metadata from the legacy API
       Dim metadata = New EndpointDiscoveryMetadata() With {.Address = New
   EndpointAddress(chatUri)}
       metadata.ContractTypeNames.Add(New XmlQualifiedName("SimpleChat",
   "http://sample.microsoft.com/wcf4"))
       metadata.Extensions.Add(<root><Name><%= name %></Name></root>)
       metadata.ListenUris.Add(chatUri)

        metadata.WriteLine("Adding legacy")

       ' Add it to the cache
       ChatDiscoveryProxy.Cache.Add(metadata)
   End Sub



10. When the legacy chat client signs out, you must remove the metadata from the discovery
    cache. To do this, implement the Unregister method as shown in the following code.
   (Code Snippet – Service Discovery – Unregister Method CSharp)
   C#
   public void Unregister(Uri chatUri)
   {
       if (chatUri == null)
       {
           throw new ArgumentNullException("chatUri");
       }
       Console.WriteLine("Removing legacy {0}", chatUri.ToString());
       ChatDiscoveryProxy.Cache.Remove(chatUri);
   }



   (Code Snippet – Service Discovery – Unregister Method VB)
   Visual Basic
   Public Sub Unregister(ByVal chatUri As System.Uri) Implements
   ILegacyChatProxy.Unregister
       If chatUri Is Nothing Then Throw New ArgumentNullException("chatUri")
       Console.WriteLine("Removing legacy {0}", chatUri.ToString())
       ChatDiscoveryProxy.Cache.Remove(chatUri)
   End Sub
11. The legacy chat client also includes a GetUsers method that returns a list of ChatUser instances.
    You need to implement this interface to search through the endpoint metadata and return a list
    of chat users. Implement the GetUsers method as shown in the following code.
   (Code Snippet – Service Discovery – GetUsers Method CSharp)
   C#
   public List<ChatUser> GetUsers()
   {
       List<ChatUser> users = new List<ChatUser>();
       FindCriteria criteria = new FindCriteria(typeof(ISimpleChatService));

        // Query to find the matching endpoints
        var query = from service in ChatDiscoveryProxy.Cache
                    where criteria.IsMatch(service)
                    select service;

       Console.WriteLine("GetUsers returning list to legacy chat");
       foreach (var serviceMetadata in query)
       {
           ChatUser user = new ChatUser();
           XElement peerNameElement =
   serviceMetadata.Extensions.Elements("Name").FirstOrDefault();
           if (peerNameElement != null)
           {
                user.Username = peerNameElement.Value;
           }
           else
           {
                user.Username = serviceMetadata.Address.ToString();
           }

             user.Address = serviceMetadata.Address.Uri.ToString();

             users.Add(user);
        }

        return users;
   }



   (Code Snippet – Service Discovery – GetUsers Method VB)
   Visual Basic
   Public Function GetUsers() As List(Of ChatUser) Implements
   ILegacyChatProxy.GetUsers
       Dim users As New List(Of ChatUser)()
       Dim criteria As New FindCriteria(GetType(ISimpleChatService))
        ' Query to find the matching endpoints
        Dim query = From service In ChatDiscoveryProxy.Cache
                    Where criteria.IsMatch(service)
                    Select service

        Console.WriteLine("GetUsers returning list to legacy chat")
        For Each serviceMetadata In query
            Dim user As New ChatUser()
            Dim peerNameElement = serviceMetadata.Extensions.<Name>
            If peerNameElement IsNot Nothing Then
                 user.Username = peerNameElement.Value
            Else
                 user.Username = serviceMetadata.Address.ToString()
            End If

             user.Address = serviceMetadata.Address.Uri.ToString()

            users.Add(user)
        Next serviceMetadata

       Return users
   End Function



12. Now you need to modify the Program class (C#) or Module1 module (Visual Basic) to host the
    legacy discovery service. Open the Program.cs file (C#) or the Module1.vb file (Visual Basic)
    located in the DiscoveryProxy project.
13. (For C# users only) Add the following using directives.
   C#
   using Microsoft.Samples.Discovery.Contracts;
   using DiscoveryProxy.Contracts;



14. Add the HostLegacyEndpoint method as shown in the following code.
   (Code Snippet – Service Discovery – HostLegacyEndpoint Method CSharp)
   C#
   private static ServiceHost HostLegacyEndpoint(string hostName)
   {
       Uri proxyAddress = new Uri("net.tcp://" +
           hostName + ":8001/legacyproxy");

        ServiceHost legacyHost = new
            ServiceHost(new LegacyChatProxy());

        legacyHost.AddServiceEndpoint
                      (
                           typeof(ILegacyChatProxy),
                           new NetTcpBinding(),
                           proxyAddress
                      );

        legacyHost.Open();

        Console.WriteLine(
            "Legacy Proxy {0}",proxyAddress);

        return legacyHost;
   }



   (Code Snippet – Service Discovery – HostLegacyEndpoint Method VB)
   Visual Basic
   Private Function HostLegacyEndpoint(ByVal hostName As String) As ServiceHost
       Dim proxyAddress As New Uri("net.tcp://" & hostName & ":8001/legacyproxy")

        Dim legacyHost As New ServiceHost(New LegacyChatProxy())

       legacyHost.AddServiceEndpoint(GetType(ILegacyChatProxy), New
   NetTcpBinding(), proxyAddress)

        legacyHost.Open()

        Console.WriteLine("Legacy Proxy {0}", proxyAddress)

       Return legacyHost
   End Function



15. Modify the Main method to open the legacy host as well.
   C#
   static void Main(string[] args)
   {
       Console.Title = "DiscoveryProxy Service";
       Console.WriteLine("DiscoveryProxy Console Host");
       string hostName = Dns.GetHostName();

        using (ServiceHost proxyHost = HostDiscoveryEndpoint(hostName))
        using (ServiceHost legacyHost = HostLegacyEndpoint(hostName))
        {
            Console.WriteLine("Press <Enter> to exit");
            Console.ReadLine();
            proxyHost.Close();
                  legacyHost.Close();
             }
        }



        Visual Basic
        Sub Main()
            Console.Title = "DiscoveryProxy Service"
            Console.WriteLine("DiscoveryProxy Console Host")
            Dim hostName = Dns.GetHostName()

            Using proxyHost As ServiceHost = HostDiscoveryEndpoint(hostName)
                Using legacyHost As ServiceHost = HostLegacyEndpoint(hostName)
                    Console.WriteLine("Press <Enter> to exit")
                    Console.ReadLine()
                    proxyHost.Close()
                    legacyHost.Close()
                End Using
            End Using
        End Sub



    16. Press Ctrl+Shift+B to build the solution.



Next Step
Exercise 5: Verification


Exercise 5: Verification
    1. Right-click the DiscoveryProxy project and click Set as StartUp project.
    2. Press F5 to debug the solution. The DiscoveryProxy application will be started.
   Figure 20
   The DiscoveryProxy Console Host


3. Switch back to Visual Studio Solution Explorer and start one instance of the DiscoveryChat
   application and one instance of the LegacyChat application, by right-clicking the corresponding
   projects, pointing to Debug and clicking Start New Instance.
4. Sign in to the Discovery Chat window, by setting up the chat as follows.
       a. User Name: Fred
       b. Click Sign In
5. Sign in to the Legacy Chat window. To do this, perform the following steps.
       a. At the command prompt, type login and press ENTER.
       b. At the Username command prompt, type Wilma and press ENTER.
6. You should now see a list of users with Fred available. Say hello to Fred. To do this, perform the
   following step.
       a. At the chat Wilma command prompt, type Say Fred Hello and press ENTER.
7. A new chat window should open from the Discovery Chat application. Respond back with Hello
   by performing the following steps.
       a. Send Message Text: Hello
       b. Click Send
   Figure 21
   The legacy chat application chatting to the new Discovery Chat application


8. In the legacy chat client, sign out. To do this, perform the following steps.
       a. At the chat Wilma command prompt, type bye and press ENTER.
       b. You should see the message that you are signed out.




   Figure 22
   The DiscoveryProxy host receiving messages from the legacy chat client
    9. Switch to Visual Studio and press Shift+F5 to stop debugging.



Next Step
Summary



Summary
During this lab you have learnt how to discover services in an ad hoc manner and how to extend the
discovery messages to supply additional information. You have used announcements to be notified
when other users sign in. You have also explored the benefits of using Managed discovery to locate
services no matter if they are in the same network or not, as long as they are registered with a discovery
proxy. Finally, you have seen how to bridge the chat client with a legacy client by creating discovery
metadata from the custom protocol that the legacy client uses.




        Feedback
 What do you think of this lab? What do you think of the new Windows Communication Foundation 4?
 Your feedback is important; it will help us build the best product for you. Please take a moment to
 provide it. Send your comments to wfwcfhol@microsoft.com

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:8
posted:11/11/2011
language:English
pages:62