Meta Moments

Sunday, 6 December 2009

The usual methods of creating a client for this WCF service result in the creation of an AddClient class that derives from ClientBase<IAdd> and implements IAdd. This client has various problems:

Every time the IAdd interface changes, the client has to be regenerated. For services where you don't have access to the assembly that defines IAdd, this is not a problem because you'll have to regenerate the local definition IAdd anyway. However, if you have access to this assembly (usually in the case of a service that you yourself publish), then it is a bit of a pain to have to regenerate AddClient every time.

Disposing the AddClient is not as simple as one would think. There have been many posts on this (like here, here, and here), so I will not go into this in more detail.

I saved the worst for last: A good client should be transparent. That is, the fact that one is using a service reference to IAdd rather than a local implementation should be completely hidden except when the client is constructed (and disposed), allowing for dependency injection at the top level of the client program. One couldn't be faulted for thinking that this is indeed the case for the generated AddClient. However, it is not so. The underlying channel can become unusable for many reasons including, but not limited to, expiration of security tokens and a fault occuring during a call. The usual pattern to get around this is to construct and dispose the client within a using block every time you need to make a call or two. This is not only cumbersome but also breaks dependency injection because and AddClient would have to be constructed every where that it's needed. Of course, we could hide the construction behind an IClientProvider<IAdd> interface and pass that around instead of IAdd (this is like passing around the dependency injector instead of passing around the dependency itself). But this is still cumbersome and inelegant because we'd still have to have a using block every time we want to make a call. Besides, IAdd is not disposable (but AddClient is) and you'd have to jump through some hoops to make the using block work. Also, as much as possible, I like to keep code unaware of any dependency injection except at the top level (for example, in the Main() method).

This is closely related to the previous point: With secure channels, the process of creating a client is very expensive and given the periodic expiration of security tokens, client pooling becomes necessary. This has to be done manually and the common solutions break the transparency requirement in the previous point.

After getting sufficiently bothered by this state of affairs, I decided to fix it. The result is available here as a small (20KB) runnable sample. The client framework is in WcfClientLibrary\ClientFramework.cs. AddClient\Program.cs shows how it is used. The special sauce here is the use of a System.Runtime.Remoting.Proxies.RealProxy-derived class that provides a transparent proxy object and allows the interception of proxy calls. There is some reflection-based method invoking involved here.

Using the WCF client framework in this sample, here is all the code you would have to write to create a client to an IAdd service:

IClientProvider<IAdd> addClientProvider = new ClientProvider<IAdd>(new ServiceChannelProvider<IAdd>(new ChannelFactory<IAdd>( "AddServiceEndpoint" ) ) );IAdd addClient = addClientProvider.GetClient();// addClient can now be used as a plain and simple IAdd. It automatically// handles refreshing the underlying channel when it becomes unusable.// The addClient itself does not need to be disposed. But the addClientProvider// should be disposed on program shutdown (and this is a well-behaved// Dispose(), unlike that of ClientBase). Please look in the sample// for more details (in AddClient\Program.cs)

There is no other hidden generated code. Of course, if you don't have access to AddServiceLibrary from your client app, you'll need to use SvcUtil.exe to generate the IAdd interface.

Tuesday, 1 December 2009

You use Microsoft's DSL Tools to model some part of your system, and you use T4 to code-generate everything imaginable from your model. Your templates are probably getting quite large and unwieldy. You've probably factored your templates into some include files. And you probably have a lot of methods and nested classes in "class features" blocks ( blocks) to factor things better. But the lack of true C#/VB editor features when editing these blocks frustrates you and leads you to write shabbier code. And only being able to create nested classes and not top-level classes in these blocks is also sometimes limiting. So you create a separate C#/VB library to hold most of the code for the templates. Now you get all the editor features that ease your frustrations and help you write clean elegant code, and you can use top-level classes.

All is good until you realize the following: You cannot recompile the library after a T4 template that uses it has been run in Visual Studio until you close that instance of VS. What a bummer - that destroys the tight edit->run template->edit cycle.

The solution: A directive processor that creates a temporary copy of the referenced assembly and loads that copy for the template generation. Each time the assembly changes, a new copy is created and the new copy is loaded for the next template generation (the templating AppDomain has to be force-unloaded to make this work). The copies are lazily cleaned up when they become unused.

I've added such a directive processor to the T4Toolbox project at codeplex. You can use it as follows:

You should then add a reference to CodeGenerationLibrary from the project that contains the T4 template and set CopyLocal=true. If you cannot set a project reference, you can of course provide the relative path to the CodeGeneration.dll and ensure that it is built before code-gen happens.

If you don't want to get the entire T4Toolbox project, the code (with some names different from the T4Toolbox version) is pasted in the DSL Tools forum here.

Monday, 30 November 2009

Wow, it's been a while since I last blogged. Almost 4 years to be precise. My last blog was at http://blogs.msdn.com/georgem/. Since then, I've left Microsoft and started working for CareEvolution Inc, where we're in the business of improving healthcare through great software. I've been itching for a while to blog again to capture those things worth noting that I come across during my work or play with software. So here goes... enjoy.