Ok, so in my last post, I gave a brief introduction to WCF for the complete n00b. As a follow up to this, I would like to have a look at some different ways to consume these services.
The “normal” way to do it is obviously by adding a service reference to the project in VS, but there are alternatives that can make sense.
The samples I am going to use are specifically for Silverlight as they focus on simple bindings and features that Silverlight supports, but it can still be used for any .NET client.
The setup I am using for this demo is fairly common. The service is implemented and hosted in an ASP.NET Web Application project, the contract is defined in a separate class library and the client in this case is a Silverlight application. You could of course also place the service implementation in its own project, but the important thing in this case is that the contract is in a separate project.
The service configuration comes out of the web.config file and looks like this
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Which as you can see is very simple…
The service I will be looking at uses the following contract
[ServiceContract]
public interface IMyService
{
[OperationContract]
string Echo(string message);
}
And the actual implementation is about as simple
public class MyService : IMyService
{
public string Echo(string message)
{
return "..:: " + message + " ::..";
}
}
And once again, adding a service reference to the Silverlight client is the normal way to do it. This will generate a new interface in the Silverlight side, as well as a proxy to use to call it. It will however make some small modifications to the interface as it generates it.
Silverlight only supports asynchronous service calls, which is great, but means that the synchronous IMyService interface won’t work. So when VS adds the service reference and generates the new interface, it generates one that looks like this
public interface IMyService {
[OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IMyService/Echo", ReplyAction="http://tempuri.org/IMyService/EchoResponse")]
System.IAsyncResult BeginEcho(string message, System.AsyncCallback callback, object asyncState);
string EndEcho(System.IAsyncResult result);
}
As you can see, it has turned the Echo() method into a BeginEcho() and an EndEcho() method to support asynchronous usage. It has also added an OpertionContractAttribute with some extra settings. The main setting being the AsyncPattern=true. This tells WCF that this call is an async call that corresponds to the action called “http://tempuri.org/Echo”, basically my Echo() method. Having this attribute set lets WCF treat a synch call as async.
The next part that is generated is a class called MyServiceClient and that inherits from ClientBase<IMyService> and implements IMyService (remember, this is not the IMyService defined in the external class library, but a version generated by VS). This base class includes everything needed to communicate with the service, so all that needs to be done is to implement the IMyService interface.
The implementation isn’t really that complicated, but since VS does it automatically, I won’t bother looking at it. If you want to, you just need to make sure that the “Show All Files” is set and then expand the service reference as well as the Reference.svcmap to find the Reference.cs, which contains the implementation. Having that said, it is very boring and you really don’t need to know what is there in a most cases.
The generated class offers 2 ways to call the service, the obvious and the not so obvious.
The normal way to use it is by using the event driven response.
var svc = new MyService.MyServiceClient();
svc.EchoAsync(MessageText.Text);
svc.EchoCompleted += (s, args) =>
{
if (args.Error != null)
{
// todo: Handle
return;
}
MessageBox.Show(args.Result);
};
This is a good solution as it handles any exceptions that are thrown and returns it using a property on the event arguments. So it is easy to just check the Error property before reading the value instead of having to wrap it all in a try/catch statement. It also has a nice feature that isn’t obvious straight away, but becomes very obvious in the next sample. That feature is the fact that it raises the completed event on the UI thread (or potentially on the thread that the call was initiated on…).
The other solution is to cast the client to the IMyService interface. This interface is implemented, but it is implemented explicitly and requires you to cast the client to the interface to get access to it. This interface implements a callback pattern instead and looks like this
var svc = new MyService.MyServiceClient() as MyService.IMyService;
svc.BeginEcho(MessageText.Text, iar =>
{
try
{
var message = svc.EndEcho(iar);
Dispatcher.BeginInvoke(() => MessageBox.Show(message));
}
catch
{
// todo: Handle
}
}, null);
As you can see, this works in a completely different way. The second parameter to the method call is a callback to be used when it completes. Inside this callback, this pattern requires you to “end” the call to get the response. The dangerous part here is that any exception thrown during the execution will be thrown when this is done. So this needs to be wrapped in a try/catch. It also calls the callback on the “wrong” thread, forcing the user to marshal it to the UI thread using the dispatcher.
So, these are the normal ways that you do it, and also the way that Microsoft suggest you do it. And to be honest, in most cases it works fine, but in some cases it just isn’t quite what we need or want.
Another way to do it is by using a ChannelFactory<T>. A ChannelFactory<T> creates “channels” for you to communicate with a WCF service, all you have to do is create a new service, set the T to the interface that is used for the service and then tell it to create a new channel. At least sort of, you also need to tell it how to configure the channel for the communication, so it needs a Binding and an EndpointAddress. Or, if you prefer to put your client configuration in a config file (ServiceReferences.ClientConfig for Silverlight), you can just pass the name of the configuration to use.
The main difference is that you need to have access to the interface that the service implements in the client application as well. And in a normal .NET application this is not a problem. In Silverlight it is more of a problem as the project with the interface in is a .NET Class Library and not a Silverlight Class Library. This can however be solved easily by “linking” the interface source file into either the Silverlight application project, or another Silverlight Class Library project that it references. This way, Silverlight will get the interface compiled using the Silverlight compiler, but it will still be the same interface.
Unfortunately, it will be the same interface. As I mentioned before, Silverlight doesn’t support synchronous service calls, which is why VS changes the interface as it generates it on the client. Luckily it is fairly easy to use a T4 template to generate an async version from the sync interface. I have written about this before. The code for this blog post uses a tweaked version of that T4 template as I have combined the two T4 templates from that post into a single template.
Using a T4 template I can easily create a new async interface that looks like this
[ServiceContract(Name="IMyService")]
public interface IMyServiceAsync
{
[OperationContract(
AsyncPattern = true)]
IAsyncResult BeginEcho(string message, AsyncCallback callback, object asyncState);
string EndEcho(IAsyncResult result);
}
The nice thing about this is that I get it in the namespace that I want, and not one VS decides to use, and I have full control over the interface. It also means that any changes to it can be handled by just running the template again, instead of having to spin up the site that hosts the service and update my service reference. Not to mention the fact that sometimes it is more or less impossible to spin up the service host like this and sometimes the service doesn’t even expose a metadata endpoint to make it possible to use the automated VS way…
And once this async version of the interface has been created, it is really easy to use the ChannelFactory<T>. At least if you know the binding and endpoint information, which I in this case just assume bluntly that you do. And in that case, using the ChannelFactory<T> looks like this
var channel = new ChannelFactory<IMyServiceAsync>(new BasicHttpBinding(), new EndpointAddress("http://localhost:4339/MyService.svc")).CreateChannel();
channel.BeginEcho(MessageText.Text, iar => Dispatcher.BeginInvoke(() => MessageBox.Show(channel.EndEcho(iar))), null);
channel = new ChannelFactory<IMyServiceAsync>("IMyServiceAsync").CreateChannel();
channel.BeginEcho(MessageText.Text, iar => Dispatcher.BeginInvoke(() => MessageBox.Show(channel.EndEcho(iar))), null);
And yes…I did cut some corners in the code above. Ignoring whether or not the call succeeded is definitely not a good idea, but it is a demo…
As you can see in the code above, you can either create the Binding and EndpointAddress on your own and pass it to the factory, or just pass it the name of the configuration that you can then place in a config file.
The final way that I would like to show is basically the same that I have blogged about once before. It uses a T4 template to generate a client much like VS would. But it does it in a way that gives you full control, and in a way that does not require the WSDL from the service to work.
The T4 template that I use is a modified version of the one from my last blog post about this solution. This time, the template does it all in one. It takes the synchronous interface, creates a new asynchronous interface and then creates a proxy client that uses the asynchronous interface. However, the generated method looks a bit different from the one VS generates even if they use the same base class, ClientBase<T>. The one created by the T4 template takes the specified arguments, a state object and a callback method of type EventHandler<ResultAsyncCompletedEventArgs<T>>.
This change actually makes it a merge between using the auto generated proxy’s Begin/End implementation and the event based version. And in my mind, it gives me the best of both. It means that I can use a callback to handle the completion, but I don’t need to use a try/catch statement as the exception or result is returned in a nice clean object for me.
In the case with the IMyInterface, the code to use the client looks like this
var client = new MyServiceClient(new BasicHttpBinding(), new EndpointAddress("http://localhost:4339/MyService.svc"));
client.EchoAsync(MessageText.Text, null, (s, ret) =>
{
if (ret.Error != null)
{
// todo: Handle
return;
}
MessageBox.Show(ret.Result));
}
And yes, as it inherits from the same base class, and the template that generates it will generate 3 different constructors, you can use a configuration file with this client as well.
That was all I had to say about that. I think it covers most of what you need to know about generating a proxy for a WCF client. unless you want to go into advanced mode and start adding behaviors to the client. But that is a much more advanced topic, and I will have to leave that to another time…
Source code for this simple sample is available here: DarksideCookie.WCF.Intro.zip (43.75 kb)