Monday, September 4, 2017

Microsoft 70-487: Consume WCF services

Exam Objectives

Generate proxies by using SvcUtil; generate proxies by creating a service reference; create and implement channel factories


Quick Overview of Training Materials


Exam Ref 70-487 - Chapter 3.5
[Book] Programming WCF Services
[MSDN] Accessing Services Using a WCF Client
[CSharpCorner] Channel Factory in WCF

PluralSight WCF Library
My github repo for WCF Examples

Calling a service from code is done with the use of a service proxy, which can be created in one of several ways:  on the command line with svcutil.exe, through the Visual Studio Solution Explorer with "Add Service Reference", and by hand coding with a Channel Factory.



Generate Proxies with SvcUtil


The ServiceModel Metadata Utility Tool (svcutil.exe) is a command line tool that can generate a proxy from a metadata file, such as a WSDL, or directly from a metadata exchange (mex) URL to a running service.  It is included with the Windows SDK, which I found under "Program Files (x86)" on Windows 7:



Running svcutil /? or /help (alternatively, -? or -help in bash) will bring up the help, which explains all the available options and how to use them, as well as a couple of examples.  It really isn't that hard to figure out.  Just point it at the metadata, and voila, instant service proxy:



The EchoService.cs file contains code defining several classes and interfaces to communicate with the service both synchronously and asynchronously.  Most of the communication is delegated to the base class of the service, ClientBase<T>, which uses a channel typed to the service interface to make the calls to service operations.

For a duplex service, like the Chat server example from my "Create a Service" post, the generated client will use a DuplexClientBase<T> as the base class.



Generate Proxies with Service Reference


In Solution Explorer, it is possible to right click on "Reference" and select "Add Service Reference". You point the tool at the metadata file or endpoint and *poof*, the service client is generated.




The generated code looks exactly like that generated by svcutil.exe (with one obvious exception: "Add Service Reference..." wraps everything in a namespace):





Create and Implement Channel Factories


Client communication can also be accomplished through the use of the ChannelFactory<T> class. Like the command line tool and the Visual Studio functionality for generating a proxy, the ChannelFactory code  takes care of all the plumbing so that the programmer can get right to work interacting with the service.

While my toy demo uses programmatic configuration, this is also possible using the config file to define static addresses and bindings to use with the target service, I just chose to reuse some of my code from the programmatic config post.

There are several overloads for the ChannelFactory constructor, one of which takes a binding and a service address.  My service has WS-Discovery enabled, so I could dynamically discover the address, and the binding I knew was basic HTTP.  The client code will wait until a service is available before it starts passing messages (though it really isn't resilient to the service going down later).

Once the binding and address are known and the factory constructed, getting an instance of what we would consider the "proxy" is just a mater of calling CreateChannel() on the factory.  Because the factory is typed to the service interface, the channel created will have methods corresponding to the methods on the service (direct link to code on GitHub).



    class Program
    {
        static void Main(string[] args)
        {
            //service uses Discovery... 
            var services = FindServices();
            var address = services.Endpoints[0].Address;
            var binding = new BasicHttpBinding();
            Console.WriteLine("Found service at: {0}", address);

            //create ChannelFactory Client to found echoservice
            ChannelFactory<IEchoService> factory = 
                new ChannelFactory<IEchoService>(binding, address);

            IEchoService instance = factory.CreateChannel();

            string message = null;
            do
            {
                Console.Write("Enter a message, or blank to exit: ");
                message = Console.ReadLine();
                Console.WriteLine(instance.Echo(message));

            } while (!string.IsNullOrEmpty(message));

            factory.Close();
        }

        static FindResponse FindServices()
        {
            // Create DiscoveryClient  
            FindResponse findResponse = null;
            while(true)
            {
                DiscoveryClient discoveryClient = 
                     new DiscoveryClient(new UdpDiscoveryEndpoint());
                Console.WriteLine("Scanning for endpoints.");
                findResponse = discoveryClient.Find(
                    new FindCriteria(typeof(IEchoService)) {
                        Duration = TimeSpan.FromSeconds(2)
                    });

                if(findResponse == null || findResponse.Endpoints == null || 
                   findResponse.Endpoints.Count == 0)
                {
                    Console.Write(".");
                    Thread.Sleep(5000);
                } else
                {
                    return findResponse;
                }
            }
        }
    }







No comments:

Post a Comment