Asynchronous WCF Calls with .NET Framework 4.0

When we want the possibility to make asynchronous calls to web services with WCF, we can check “Generate asynchronous operations” when adding a service reference. But I usually hand-code my proxies. Here is a recipe for adding async to hand-coded proxies.

Say I have an interface like this:

[ServiceContract]
public interface IMyInterface
{
	[OperationContract(Name="Foo", Action="urn:MyAction", ReplyAction="")]
	Response Foo(Request request);
}

Create two more methods, BeginFoo and EndFoo. BeginFoo must have the same operation contract name, action and reply action as Foo:

        [OperationContract(Name = "Foo", Action = "MyAction", ReplyAction = "", AsyncPattern = true)]
        System.IAsyncResult BeginFoo(Request request, System.AsyncCallback callback, object asyncState);

        Response EndFoo(System.IAsyncResult result);

Then, create the proxy like this:

    public class MyProxy : ClientBase<IMyInterface>, IMyInterface
    {
        private BeginOperationDelegate onBeginFooDelegate;

        private EndOperationDelegate onEndFooDelegate;

        private System.Threading.SendOrPostCallback onFooCompletedDelegate;

        public event System.EventHandler<FooCompletedEventArgs> FooCompleted;

        public Response Foo(Request request)
        {
            return Channel.Foo(request);
        }

        public IAsyncResult BeginFoo(Request request, AsyncCallback callback, object asyncState)
        {
            return Channel.BeginFoo(request, callback, asyncState);
        }

        public Response EndFoo(IAsyncResult result)
        {
            return Channel.EndFoo(result);
        }

        private System.IAsyncResult OnBeginFoo(object[] inValues, System.AsyncCallback callback, object asyncState)
        {
            var request = (Request)inValues[0];
            return this.BeginFoo(request, callback, asyncState);
        }

        private object[] OnEndFoo(System.IAsyncResult result)
        {
            var retVal = this.EndFoo(result);
            return new object[] {retVal};
        }

        private void OnFooCompleted(object state)
        {
            if ((this.FooCompleted != null))
            {
                InvokeAsyncCompletedEventArgs e = ((InvokeAsyncCompletedEventArgs)(state));
                this.FooCompleted(this, new FooCompletedEventArgs(e.Results, e.Error, e.Cancelled, e.UserState));
            }
        }

        public void FooAsync(Request request)
        {
            this.FooAsync(request, null);
        }

        public void FooAsync(Request request, object userState)
        {
            if ((this.onBeginFooDelegate == null))
            {
                this.onBeginFooDelegate = new BeginOperationDelegate(this.OnBeginFoo);
            }
            if ((this.onEndFooDelegate == null))
            {
                this.onEndFooDelegate = new EndOperationDelegate(this.OnEndFoo);
            }
            if ((this.onFooCompletedDelegate == null))
            {
                this.onFooCompletedDelegate = new System.Threading.SendOrPostCallback(this.OnFooCompleted);
            }
            base.InvokeAsync(this.onBeginFooDelegate, new object[] {
                    request}, this.onEndFooDelegate, this.onFooCompletedDelegate, userState);
        }
    }

    public class FooCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        private object[] results;

        public FooCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :
            base(exception, cancelled, userState)
        {
            this.results = results;
        }

        public Response Result
        {
            get
            {
                base.RaiseExceptionIfNecessary();
                return ((Response)(this.results[0]));
            }
        }
    }

Here is how to use this proxy:

private void CallMyProxy()
{
    ...
    var proxy = new MyProxy();
    proxy.FooCompleted += new EventHandler<FooCompletedEventArgs>(FooCallback);
    proxy.FooAsync(request);
}

private void FooCallback(object sender, FooCompletedEventArgs e)
{
    try {
        var response = e.Result;
        ...
    }
    finally
    {
        var proxy = (MyProxy)sender;
        if (proxy.State == CommunicationState.Opened)
            proxy.Close();
    }
}
Advertisements

WCF Service accepting both username/password and certificate credentials

On a recent project, I had the requirement to support both username/password and certificate login simultaneously. With .NET Framework 4.5, WCF allows you to specify multiple authentication schemes on a single endpoint using the new clientCredentialType=”InheritedFromHost”. Unfortunately, we used .NET Framework 4.0 and could not upgrade. So I had to create two endpoints with different addresses. Unfortunately, I couldn’t get that to work with basicHttpBinding, which we were using. I got error messages similar to “Security settings for this service require ‘Anonymous’ Authentication but it is not enabled for the IIS application that hosts this service”. So a solution that worked in this scenario was to switch to wsHttpBinding and message credentials:

  <system.serviceModel>
    <services>
      <service name="MyService">
        <endpoint address="Password" binding="wsHttpBinding" bindingConfiguration="Password"
          name="GetListingResponder" contract="IMyContract" />
        <endpoint address="Certificate" binding="wsHttpBinding" bindingConfiguration="Certificate"
          name="GetListingResponder" contract="IMyContract" />
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="Password">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="Windows"/>
          </security>
        </binding>
        <binding name="Certificate">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="Certificate"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>   
  </system.ServiceModel>

Security mode TransportWithMessageCredential means that SSL is used for encryption and message credential for authentication.

The client is also configured with two endpoints:

    <client>
      <endpoint address="https://localhost/MyService/MyService.svc/Password" binding="wsHttpBinding"
        bindingConfiguration="Password" contract="IMyContract"
        name="MyServicePassword" />
      <endpoint address="https://localhost/MyService/MyService.svc/Certificate" binding="wsHttpBinding"
        bindingConfiguration="Certificate" contract="IMyContract"
        name="MyServiceCertificate" />
    </client>

Which is used can be specified in the parameter to the client proxy constructor.

Logitech SqueezeBox/Media Server and Spotify

I have used Spotify on my Logitech SqueezeBoxes for a long time now, but only recently got it working properly.

One problem I had was that I got a “not available in your region” error for many tracks. I had installed the Triode Spotify plugin rather than the official one from Logitech (which sucks by the way), but that didn’t help. I read somewhere that I had to select “always stream via helper” in plug-in settings. That prompted me to do a self-test (available on the settings page), which revealed a problem – the helper (on port 9005) was not available. It turned out the firewall on my server was blocking that port. I fixed that by creating a new inbound rule of type application and specified the full path of the helper (C:\ProgramData\Squeezebox\Cache\InstalledPlugins\Plugins\Spotify\Bin\MSWin32-x86-multi-thread\spotifyd.exe). After that, everything seems to work.

Windows Home Server 2011 and Backing up UEFI-based computers

After upgrading my computers from Windows 7 to 8 my backups, made using Windows Home Server 2011 (which I run as a virtual machine for the sole purpose of backups), did not work. I got this error on the client machines:

Volume Shadow Copy Service error: Unexpected error querying for the IVssWriterCallback interface.  hr = 0x80070005, Access is denied.
. This is often caused by incorrect security settings in either the writer or requestor process.

Operation:
   Gathering Writer Data

Context:
   Writer Class Id: {e8132975-6f93-4464-a53e-1050253ae220}
   Writer Name: System Writer
   Writer Instance ID: {bc66abce-f123-4f20-9121-8ebe21c3134f}

It turned out this error message is rather misleading. What was really the problem was that Windows Home Server 2011 does not support client backup on Unified Extensible Firmware Interface (UEFI)-based computers that contain a GUID partition table (GPT)-formatted disk. But luckily, there is a hotfix from Microsoft: http://support.microsoft.com/kb/2781272/en-us

After applying this hotfix, reinstalling WHS client software and extending the virtual disk for WHS (it was full), the backups started working.

SpecFlow and WatiN and HTML 5

I recently started testing with SpecFlow and WatiN. A problem that I stumbled on was that in my binding class, I had a method to type input into textfields:

        [When(@"jag angett följande data:")]
        public void NarJagAngettFoljandeData(Table table)
        {
            foreach (var tableRow in table.Rows)
            {
                string id = tableRow["Fält"];
                var field = WebBrowser.Current.TextField(id);
                field.TypeText(tableRow["Värde"]);
            }
        }

But for some fields, it failed with the following exception: “WatiN.Core.Exceptions.ElementNotFoundException: Could not find INPUT (hidden) or INPUT (password) or INPUT (text) or INPUT (textarea) or TEXTAREA element tag matching criteria…”

It turned out some of my fields didn’t have type=”text” but type=”number” and type=”datetime”. So thanks to StackOverflow I found a solution: Create an extended textfield like this:

    [ElementTag("input", InputType = "text", Index = 0)]
    [ElementTag("input", InputType = "password", Index = 1)]
    [ElementTag("input", InputType = "textarea", Index = 2)]
    [ElementTag("input", InputType = "hidden", Index = 3)]
    [ElementTag("textarea", Index = 4)]
    [ElementTag("input", InputType = "email", Index = 5)]
    [ElementTag("input", InputType = "url", Index = 6)]
    [ElementTag("input", InputType = "number", Index = 7)]
    [ElementTag("input", InputType = "range", Index = 8)]
    [ElementTag("input", InputType = "search", Index = 9)]
    [ElementTag("input", InputType = "color", Index = 10)]
    [ElementTag("input", InputType = "datetime", Index = 11)]
    public class TextFieldExtended : TextField
    {
        public TextFieldExtended(DomContainer domContainer, INativeElement element)
            : base(domContainer, element)
        {
        }

        public TextFieldExtended(DomContainer domContainer, ElementFinder finder)
            : base(domContainer, finder)
        {
        }

        public static void Register()
        {
            var typeToRegister = typeof(TextFieldExtended);
            ElementFactory.RegisterElementType(typeToRegister);
        }
    }

Then, change the binding method:

                //var field = WebBrowser.Current.TextField(id);
                var field = WebBrowser.Current.ElementOfType<TextFieldExtended>(id);