Custom wsdl from WCF

I wanted my WCF service to export specific metadata.  Essentially I am building it contract-first and I only want the users to see the hand-crafted wsdl and xsd files that have been created.  If the web service was running in IIS that would be easy, I'd simply set the location of the metadata explicitlly in the web.config file, like this:

 

<serviceBehaviors>

  <behavior name="MyBehavior">

    <serviceMetadata httpGetEnabled="true"

    externalMetadataLocation="MyCustomWsdl.wsdl" />

    <serviceDebug includeExceptionDetailInFaults="false" />

  </behavior>

</serviceBehaviors>

 

But this time my WCF service is running inside a Windows Service, so I can't just let IIS serve the wsdl as a page, unless I install the wsdl file on some other web server, which makes for a deployment nightmare.  So I set about finding out how to override the mechanism that WCF uses for sending the auto-generated wsdl.  I was thinking about overriding that.  I found that I could easily add a class that implements the IWsdlExportExtension and IEndpointBehavior interfaces, and it did allow me to do some fiddling with the generated wsdl, but not at the level I wanted.  I wanted total control, essentially streaming the files that had been hand crafted so carefully.  For a minute, I even considered implementing an HttpListener to send out the files :-0

 

The solution that I have settled on is to add another WCF service to my Windows Service.  This is a simple WCF service that responds to http GET commands, the interface looks like this:

 

[ServiceContract]

public interface ICustomWsdl

{

   [OperationContract]

   [WebGet()]

   Stream MetaData(string name, string extension);

}

 

By returning a Stream, we are able to avoid sending the xml decoration around the response, which would happen if we returned a string, for example.  The basic implementation of this service looks like this:

 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

public class CustomWsdlService : ICustomWsdl

{

   private string renameXsd(string src)

   {

      XmlDocument xdoc = new XmlDocument();

      xdoc.LoadXml(src);

      XmlNodeList xmlnodes = xdoc.GetElementsByTagName("xsd:include");

      updateNodes(xmlnodes);

      xmlnodes = xdoc.GetElementsByTagName("xsd:import");

      updateNodes(xmlnodes);

      return xdoc.InnerXml;

   }

 

   private void updateNodes(XmlNodeList xmlnodes)

   {

      for (int i = 0; i < xmlnodes.Count; i++)

      {

         string xsdName = xmlnodes[i].Attributes["schemaLocation"].Value as string;

         if (!string.IsNullOrEmpty(xsdName))

         {

            string newName = "MetaData?name="

              + Path.GetFileNameWithoutExtension(xsdName) + "&extension=xsd";

            xmlnodes[i].Attributes["schemaLocation"].Value = newName;

         }

      }

   }

 

   public Stream MetaData(string name, string extension)

   {

      string path =

         Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

      string src =

         renameXsd(File.ReadAllText(path + "\\" + name + "." + extension));

      return new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(src));

   }

}

 

To make it work we need to add these settings in the config file:

 

<system.serviceModel>

  <behaviors>

    <endpointBehaviors>

      <behavior name="WebEndpointBehavior">

        <webHttp/>

      </behavior>

    </endpointBehaviors>

  </behaviors>

 

  <bindings>

    <webHttpBinding>

      <binding name="HttpBindingConfig"/>

    </webHttpBinding>

  </bindings>

 

  <services

    <service name="CustomWsdlService">

      <endpoint address="wsdl"

      behaviorConfiguration="WebEndpointBehavior" binding="webHttpBinding"

      contract="Aggregation.Broker.Core.ICustomWsdl"

      bindingConfiguration="HttpBindingConfig">

        <identity>

          <dns value="localhost" />

        </identity>

      </endpoint>

      <host>

        <baseAddresses>

           <add baseAddress="http://localhost:8731/Test/" />

        </baseAddresses>

      </host>

    </service>

  </services>

</system.serviceModel>

 

It will serve up xsd and wsdl files as long as they are in the same folder as the running assembly.  Since it can be called via http GET, you can test it in a browser, like this:

 

http://localhost:8731/Test/wsdl/MetaData?name=MyWsdlFileName&extension=Wsdl

 

...which would serve up a wsdl file named MyWsdlFileName.wsdl as long as it was in the same folder as the running binaries.  Any import or include statements in the metadata will be automatically remapped to the same serving method, so the wsdl can reference seperate xsd files (indeed, xsd files can also reference other xsd files).  All this means that another WCF service can use this in the config:

 

<serviceMetadata httpGetEnabled="true" externalMetadataLocation="URL_AS_ABOVE" />

 

Job done, I now have total control of my metadata, I just place the wsdl and xsd files in with the binaries of my Windows Service.  Finally, I just need to host this new WCF service in the existing Windows Service, which is easy.  Hopefully, that is not a bad solution.  I've tested it by pointing SoapUI at some metadata presented this way, and it seems fine, happy days.

A new lock statement for C#

I'm working on a lot of multithreaded code at the moment.  In the past I have made use of the lock keyword in C#, but I'm not doing that anymore because it is much better to have a timeout.  However, I don't like having lots of sections like this in my code:

 

if (System.Threading.Monitor.TryEnter(objLock, TIMEOUT))

{

    try

    {

        // do stuff here...

    }

    finally

    {

        System.Threading.Monitor.Exit(objLock);

    }

}

 

...which is the equivalent of using the lock statement, but with a timeout specified.  Still, what I'd like to see in my code is something like this:

 

lock (objLock,TIMEOUT)

{

    // do stuff here...

}

 

Which would be more pretty.  And since I like pretty code, I decided to see how close I could get to that.  This is my current solution:

 

public static class Lock

{

    public static T Try<T>(object objLock, int timeout, Func<T> f)

    {

        if (System.Threading.Monitor.TryEnter(objLock, timeout))

        {

            try

            {

                return f.Invoke();

            }

            finally

            {

                System.Threading.Monitor.Exit(objLock);

            }

        }

        return default(T);

    }

 

    public static void Try(object objLock, int timeout, Action f)

    {

        if (System.Threading.Monitor.TryEnter(objLock, timeout))

        {

            try

            {

                f.Invoke();

            }

            finally

            {

                System.Threading.Monitor.Exit(objLock);

            }

        }

    }

}

 

So far, that is as close as I can get, it means that I can do two things:

 

Lock.Try(objLock, TIMEOUT, delegate

{

    // stuff here...

});

 

or, if I need to return a value, then this:

 

return Lock.Try<int>(objLock,TIMEOUT,delegate

{

    // stuff here...

    return 0;

});

 

Because the second example uses generics, then I can return anything I like by specifying the type inside the angled brackets.  Now I feel that my code is a lot more prettier which is a nice way to finish off on a Friday.

Lenovo Windows 7 Upgrade, part 2

So, it's now been 21 days and I'm still waiting for Lenovo to validate my free Windows 7 upgrade.  I have chased them up again, and this is the e-mail reply that I received today: "Dear David, Please, be patient, our team needs some more time for checking all received POPs. Once your POP is validated you will receive a confirmation e-mail. Best regards Jana".

My question is: when should a customers patience end and be regarded as bad customer service?

Alternative WCF ServiceHost

I'm writing a test harness that will try out different WCF services and allow us to fire test data at them.  But I also want to be able to override some of the behaviour of the WCF services during the tests.  So I've built some code to look inside the assemblies in a particular folder and determine if they contain any classes that implement the WCF service interfaces.  Then I can create instances of those classes at runtime and host them as WCF services inside my test harness.  This means that I can switch between various new WCF classes from the UI of the test harness.  However, when swapping the service at runtime, I was getting this error: System.InvalidOperationException: Service 'TestHarness.ATestService' has zero application (non-infrastructure) endpoints.  That's because I was now using a differently named WCF service than the one named in the config file.  So, this was my solution:

 

public class NamedServiceHost : ServiceHost

{

    public static string configName { get; set; }

 

    public NamedServiceHost(object singletonInstance, params Uri[] baseAddresses)

        : base(singletonInstance, baseAddresses)

    { }

 

    protected override void ApplyConfiguration()

    {

        if (!string.IsNullOrEmpty(configName))

        {

            Description.ConfigurationName = configName;

        }

        base.ApplyConfiguration();

    }

}

 

I can use it instead of the normal ServiceHost class, like this:

 

NamedServiceHost.configName = "MyNamespace.MyService";

ServiceHost = new NamedServiceHost(singletonInstance);

ServiceHost.Open();

 

Now I can tell the service host what name I'd like to use from the config file, rather than letting it pick based on the name of the WCF class.  Unfortunately the ApplyConfiguration() method is called from the constructor of the base class, so the easiest way to tell the class what configuration name you'd like to use is by setting a static property.

Round robbin

I was doing some simple load balancing tests on an application I'm building.  I wanted to load multiple queues from different places in my code using a simple round-robin approach.  A very simple thing to do, and this is what I came up with:

 

int index = 0, max = 10;

...

index += index >= max ? (index * -1) : 1;

 

...it will increment the index counter until it reaches the limit (10), then start back at zero again. I don't know why I find that line so beautiful, but I just do.  I like it when you can do stuff in a single line of code.

Visual Studio 2008 Command From Here

I wanted to be able to easily get to a Visual Studio 2008 Command Prompt and run commands from different folders on my machine, essentially like the Command Prompt From Here PowerToy but for Visual Studio. What I ended up doing was taking this information and adapting it, so that you don't need to hold down the shift key. This is what I ended up with:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\VS2008 Command Prompt]
@="Open VS2008 Command Prompt Here"
"NoWorkingDirectory"=""

[HKEY_CLASSES_ROOT\Directory\Background\shell\VS2008 Command Prompt\command]
@="C:\\Windows\\System32\\cmd.exe /s /k \"cd \"%1\" && \"C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\vcvarsall.bat\"\" x86 "

[HKEY_CLASSES_ROOT\Directory\shell\VS2008 Command Prompt]
@="Open VS2008 Command Prompt Here"
"NoWorkingDirectory"=""

[HKEY_CLASSES_ROOT\Directory\shell\VS2008 Command Prompt\command]
@="C:\\Windows\\System32\\cmd.exe /s /k \"cd \"%1\" && \"C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\vcvarsall.bat\"\" x86 "

[HKEY_CLASSES_ROOT\Drive\shell\VS2008 Command Prompt]
@="Open VS2008 Command Prompt Here"
"NoWorkingDirectory"=""

[HKEY_CLASSES_ROOT\Drive\shell\VS2008 Command Prompt\command]
@="C:\\Windows\\System32\\cmd.exe /s /k \"cd \"%1\" && \"C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\vcvarsall.bat\"\" x86 "

Like the original version, it assumes that Visual Studio is installed in the default locations.  But it is proving very useful...

Opening WPF Windows on a new thread

Recently I needed to open a new WPF window on a new thread. I was helped a great deal by this article, however the window that I needed to open did not have parameterless constructor.  In the end, this is the code that I came up with:

private void NewWindowThread<T,P>(Func<P, T> constructor, P param) where T : Window

{

   Thread thread = new Thread(() =>

   {

      T w = constructor(param);

      w.Show();

      w.Closed += (sender, e) => w.Dispatcher.InvokeShutdown();

      System.Windows.Threading.Dispatcher.Run();

   });

   thread.SetApartmentState(ApartmentState.STA);

   thread.Start();

}

This method, uses generics to specify the Type of the window that you're creating as well as the Type of the parameter passed to its constructor, for example:

string t = "Hello World!";

NewWindowThread<TitleWindow, string>(c => new TitleWindow(c), t);

Would work when TitleWindow was declared like this:

public class TitleWindow : Window

{

    public TitleWindow(string title)

    {

        this.Title = title;

    }

}

Now I can open different types of window on a new thread and pass parameters to them.

Lenovo Windows 7 Upgrade

 

I bought myself a new PC.  I’m pleased with it, a Lenovo ThinkCentre with a Quad Core processor.  It’s nice and fast and has excellent build quality, the first thing I did when I took it out the box was peek inside the case.

 

One of the reasons I bought from Lenovo was because they’ll give me a free upgrade to Windows 7.  I tried to avoid Vista completely, but as it turns out I will need to use it for a few weeks.  I can put up with that.  So, when my new machine arrived I filled in Lenovo’s on-line form to request my upgrade.  Part of the process is to prove that I really did buy a Lenovo PC, so I have to send off my proof of purchase (an invoice or a receipt).  I did that, which was easy because it can be done via e-mail.  All good so far.

 

But that is where things have stalled.  I can check my order on-line and it still says: 'Pending for Proof of Purchase'. I want to know that I will actually get my upgrade! After chasing them up via an e-mail (and it took about 5 days to get a reply) I get this: “Dear David, Please be patient our team needs some more time to check all received POPs. Once your POP is validated you will receive a Confirmation mail. Best regards Jana”.  It’s now been 11 days since I submitted my proof of purchase.  How long will I need to be patient?  Come on Lenovo.

Not my first attempt at blogging, but

Welcome anyway

...to my world.  Yeah OK, I've decided that I should have a blog.  I have been here before, started a blog added about two posts and then given up.  But I also should take down that ancient website that describes what I was doing about 10 years ago.  The best thing that I can think of replacing it with is a blog.  So this time I will just have to knuckle down and do some posting.  Plus, I'm not feeling well today, it's a saturday and so I had just as well take my mind off the pain by installing some blogging software.  Let's see how it goes this time...