Ok, so this post is definitely going down in the books as “why would you do that” for a lot of people, but it actually has its benefits in some cases. What I want to show, is how we can work with WCF services and service interfaces without having to add a service reference to Visual Studio and instead auto generate the required code using T4 templates…
So why would I want to do that? Well, in some cases it is kind of tedious and even complicated to spin up the service just to be able to update the service reference and in some cases it isn’t even possible to get access to the WSDL that is required to create it. And in those cases, this will help you… In my case, the services and service contracts are built by one dev, and the Silverlight stuff by me. To us, this way of working makes it a lot easier to handle changes to the service contracts as we go along… I can get changes by just checking out the changed interfaces without having to try and get my solution into a state where I can update my service reference.
Ok…so I start by creating a new Silverlight application project in VS and add a ASP.NET web application to host it. Once that is created, I add another project to the solution. This time it is a Windows class library project, which will host the WCF contracts, so I make sure to add a reference to System.ServiceModel. In this case, I define a very simple service contract that looks like this
[ServiceContract]
public interface IMyService
{
[OperationContract]
string Echo(string message);
}
As soon as I have the contract, I can build myself a simple service. To do this, I add a reference from the web application project to the interface project. I then add a new Silverlight-enabled WCF Service that I name MyService. This will generate a WCF service that looks like this
namespace DarksideCookie.Sample.WCFClientGeneration.Client.Web
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService
{
[OperationContract]
public void DoWork()
{
// Add your operation implementation here
return;
}
// Add more operations here and mark them with [OperationContract]
}
}
I personally do not like the way that the created code handles the service contract. It should be a separate interface…perhaps like the one I have just created…so I remove the contract attributes and set it to implement my interface instead
namespace DarksideCookie.Sample.WCFClientGeneration.Client.Web
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService : IMyService
{
public string Echo(string message)
{
return message;
}
}
}
As part of adding the service to the project, VS added a configuration for it in the web.config file. This needs to be changed to reflect my new service contract (as well as cleaned up a bit). The resulting config looks like this
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="DarksideCookie.Sample.WCFClientGeneration.Client.Web.MyService">
<endpoint address="" binding="basicHttpBinding" contract="DarksideCookie.Sample.WCFClientGeneration.Interfaces.IMyService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
After this is done, I can verify that the service works by browsing to it… And then I can look at the Silverlight end of the story.
I start by adding another project to the solution. This time it is a Silverlight class library project, which I immediately empty out by deleting the Class1.cs file. I then add a link to the IMyservice.cs file from the Windows class library project.
Adding a linked file is really simple. Just right-click and choose “Add” > “Existing Item..”. Then locate the cs file you want to link, but instead of clicking the “Add” button, you click the arrow at the right-hand side of it. In the drop-down, you select “Add As Link”. This will link the file into the project instead of adding it, so updating it in on place, will update it in both. This is a great way to get files compiled both for the full .NET Framework and for Silverlight. Just remember that all codes have to work in both runtimes, and any extra reference added to the original project will have to be added to the project that links the file as well.
Building the solution now, will result in 2 assemblies with interfaces, one for .NET and one for Silverlight. But just having an interface project won’t help us that much. Especially not since Silverlight can’t work with service contracts that aren’t asynchronous. But I don’t want to add asynchronous methods to the interface as that will mess up my .NET interface as well and thereby cause problems with the service implementation. The interface should only be asynchronous on the Silverlight side…
So to solve this, a friend of mine found this great T4 template that will take the interface and generate an asynchronous interface. The T4 template is available at http://weblogs.thinktecture.com/cweyer/2009/06/generating-async-wcf-operationcontract-signatures-with-a-t4-template.html.
To add a T4 template to the project, I just add a new text file and name it IMyServiceAsync.tt. The naming is important as the template uses the naming to find the interface to work with. So the name should be <interface name>Async.tt. As soon as you save it, VS will run it and auto generate a new class for you. The new class will be available as a child to the template in the Solution Explorer. If you modify the interface, all you have to do is right-click the template and choose “Run Custom Tool” to re-generate the file.
Unfortunately, it doesn’t work straight out of the box in this case. It needs a little tweaking to make sure it outputs the correct names and attributes. (In the download at the end, this template is called IMyServiceAsync.tt).
Now that we have the async interface, it is time to start using it. I simply add a button to the MainPage.xaml file and add a handler for the Click event in the code behind. I know I should be using MVVM and stuff, but in this case I am just testing something so it seems like overkill…
In the click handler, I start off by creating a new Binding. In this case the service uses a BasicHttpBinding, so that is what I create. Next, I create an EndPointAddress that points to the location of the svc file. Once I have this two pieces of the puzzle, I create a new ChannelFactory<>. I pass in the async interface as the type parameter.
As soon as I have configured a ChannelFactory, I can create a channel and start calling my service.
private void Button_Click(object sender, RoutedEventArgs e)
{
var b = new BasicHttpBinding();
var endpoint = new EndpointAddress("http://localhost:12642/MyService.svc");
var factory = new ChannelFactory<IMyServiceAsync>(b, endpoint);
var channel = factory.CreateChannel();
channel.BeginEcho("Hello World", (iar) =>
{
var msg = channel.EndEcho(iar);
Dispatcher.BeginInvoke(() => MessageBox.Show(msg));
}, null);
}
That’s all there is to it! That’s all you have to do if you don’t want to add service references to your projects.
But the service reference has one added bonus that this lacks. It creates a client for us, and that client implements a different async pattern. Instead of Begin<methodname> and End<methodname> it adds <methodname>Async. This pattern is easier to use as it uses a callback with a result instead of having you “end” the call manually. So to handle this, James (my friend) modified the T4 template even further. This time, he modified it to auto generate the client class for us.
In the download, I have broken out that part of the template into its own template (IMyServiceClient.tt). It can definitely be combined into one template, but for this demo it seemed better to keep them separate…
The template is fairly easy and basically copies the implementation that VS generates for us. In the callback, it passes an argument of type ResultAsyncCompletedEventArgs or ResultAsyncCompletedEventArgs<T>. This argument makes it very easy to verify whether a call failed or succeeded, as well as get access to the response.
Using this client instead of the ChannelFactory is simple.
private void Button_Click(object sender, RoutedEventArgs e)
{
var b = new BasicHttpBinding();
var endpoint = new EndpointAddress("http://localhost:12642/MyService.svc");
var svc = new MyServiceClient(b, endpoint);
svc.EchoAsync("Hello World", null, (s, result) =>
{
if (result.Error != null)
{
Dispatcher.BeginInvoke(() => MessageBox.Show("Error"));
return;
}
Dispatcher.BeginInvoke(() => MessageBox.Show(result.Result));
});
}
As you can see, we still create a Binding and an EndpointAddress, but to be honest the client inherits from ClientBase<T> and as such supports a couple of different constructors. So if you prefer putting your connection configuration in the ServiceReferences.ClientConfig instead, that is fully supported.
So…that’s it… The talked about in this post might not be useful in all scenarios, but it will definitely be in some.
Cheers!
And the download of course: DarksideCookie.Sample.WCFClientGeneration.zip (71.53 kb)