Handle WCF Fault in Silverlight

Run into this thread on stackoverflow about handling fault in asynchronous call.

  • In normal .NET code, the generated proxy class handles the exception properly by putting the exception in the Error property instead of throwing it.
  • In Silverlight, the generated proxy class sets the Error property, but does not handle the exception completely. The exception is picked up by the debugger, which pops up the exception box with the message “ProtocolException was unhandled by user code”. Despite this message, the exception does not seem to actually make it to the Application_UnhandledException function.

In my opinion, Silverlight is running within browser, in theory, it should rely on browser to handle all the exceptions, similar to javascript and flash. Asynchronous call makes this fault handling more complex, putting regular try catch block on the primary call doesn’t work,  Microsoft doesn’t add try catch block in OnComplete method in generated proxy.cs either.

Instead customize proxy generation wizard, we use a pipeline pattern to introduce a extra layer between silver light and wcf proxy.

This pipe line (wrapper) class is replacing the original WCF proxy inject into SL view model, in it’s ctor the new complete event handler will be appended after the one with the same name in proxy.

General Exception handling is happening in CheckError() method.

Here are some example code:

        public MySilverLightViewModel(IMySilverLightDataService dataService)
        {
            // Hookup DataAccess Completed Events
            _dataService = dataService;

            _dataService.DataServiceCallCompleted += (s, e) =>
                {
                    IsBusy = false; // used by UI control to display the spinner

                    if (e.Error.GetType() == typeof (FaultException<TooManyRecordsFault>)) // deal with FaultContract defined in OperationContract
                    {
                        DialogueService.ShowAlert(e.Error.Message);  // Our own message box service
                        return;
                    }

                    if (_dataService.ErrorCheck(e)) return;  // general unhandled exception happend in ErrorCheck, stop call if occurs.

                    // processing data ...
                };

Maybe we will re-visit this after we got chance to look into RIA service.

Advertisements

Passing credential over basicHttpBinding

Silverlight client only support basicHttpBinding, for internal usage it would be nice we can pass credential over basciHttpBinding. Server side config:


      <basicHttpBinding>
        <binding name="BasicBindingWithSecurity">
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows" />
          </security>
        </binding>
      </basicHttpBinding>

Server side can simply use either

System.Threading.Thread.CurrentPrincipal.IsInRole(role)

or

new PrincipalPermission(null, role).Demand()

to check permission.

Silverlight client config:

            <basicHttpBinding>
                <binding name="client">
                    <security mode="TransportCredentialOnly" />
                </binding>
            </basicHttpBinding>

WPF client config:

         <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows" proxyCredentialType="None"
              realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
          </security>

Config through code, note the way to setup clientCredentialType. Otherwise got an error:

must set message clientCredentialType to avoid this kind of error:

The HTTP request is unauthorized with client authentication scheme ‘Anonymous’. The authentication header received from the server was ‘Negotiate,NTLM,Basic realm=”Noneed”‘.
            var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
            binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
            binding.Security.Transport = new HttpTransportSecurity()
                                             {
                                                 ClientCredentialType = HttpClientCredentialType.Windows,
                                             };

             var factory = new ChannelFactory<IVineOnlineAdminService>(
                binding,
                new EndpointAddress("http://localhost/MyService.svc"));
             factory.CreateChannel().MyMethod();
            ...

The evolution of deploy process, from NAnt token to config transform in MSBuild

Deploy process used to be very simple, copy/xcopy + a few manual modifications for connection strings and other stuff.

But too many manual operation in deploy is always problematic, due to tired eyes, fingers and people, etc.

Using NAnt tokenized config file looks very elegant then, here is typical layout of our project.

—-+-cfg
—-+—app.config.template
—-+—local.properties.xml
—-+—acceptance.properties.xml
—-+—production.properties.xml

Tokens in app.config.template will be replaced by the values defined in each properties.xml file for different environment.

After looking at Soctt Hanselmn’s post about MSDeploy and config transform in deploy process, this built-in config xml transform really impressed me, time to give it a try.


Apply config transform,

For us, the transform happens in acceptance/production is just to change connect string and set includeExceptionDetailInFaults to false.


<?xml version="1.0"?>

<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
   <connectionStrings>
      <add name="MyDB"
        connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
        xdt:Transform="Replace"/>
    </connectionStrings>
  </connectionStrings>
  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
  </system.web>
  <system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="DefaultServicesBehavior">
        <serviceDebug includeExceptionDetailInFaults="false" xdt:Transform="Replace"/>
      </behavior>
    </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Do a publish for this wcf service project, choose publish method as File System, the web.config in the output folder is transformed.

Build package can be done either through  GUI or command line or NANT.


  <target name="release.server" depends="">
    <msbuild project="${dir.src}/MyWcfServices/WcfServices.csproj" target="package" >
      <property name="Configuration" value="Release" />
      <property name="OutDir" value="${dir.release}/Server/" />
    </msbuild>
  </target>

The generated package:

This package can be deployed on target server by cmd, as described in readme.txt, or by IIS admin console.

Note, those deploy options won’t appear until MSDeploy is installed on IIS server through http://www.iis.net/download/WebDeploy.

Import application wizard is very friendly in normal MS way.


Here is the most beautiful step, parameterss for this deploy are automatically read from WcfServices.SetParameters.xml file located beside the package, connection string, app name, blah blah:

More transform syntax can be found at http://msdn.microsoft.com/en-us/library/dd465326(VS.100).aspx

About MSDeploy package, http://msdn.microsoft.com/en-us/library/dd547591.aspx.

Our updated NAnt build script:


 <target name="release" depends="">
    <delete dir="${dir.release}" />
    <foreach item="String" in="Release Acceptance Production" delim=" " property="env">
      <echo message="releasing for env: ${env}" />
      <call target="release.client" />
      <call target="release.server" />
    </foreach>
  </target>

  <target name="release.client" depends="rebuild.assemblyinfo">
    <delete dir="${dir.release}/Client" />
    <msbuild project="${dir.src}/MyProject.UI.Wpf/MyProject.UI.Wpf.csproj"  >
      <property name="Configuration" value="${env}" />
      <property name="OutDir" value="${dir.release}/${env}/Client/" />
    </msbuild>
  </target>

  <target name="release.server" depends="rebuild.assemblyinfo">
    <msbuild project="${dir.src}/MyProject.WcfServices/WcfServices.csproj" target="Package">
      <property name="Configuration" value="${env}" />
      <property name="OutDir" value="${dir.release}/tmp/" />
    </msbuild>
    <copy todir="${dir.release}/${env}/Server/" flatten="true">
      <fileset>
        <include name="${dir.release}/tmp/_PublishedWebsites/WcfServices_Package/*.*" />
      </fileset>
    </copy>
    <delete dir="${dir.release}/tmp/" />
  </target>

I personally think this is really cool, unfortunately config transform is only available for Web project for now. I actually already did some tweet to WPF project to make it config transformable, I will put it in another post.

Automated Silverlight WCF asynchronous test

Using Silverlight NUnit + statlight really makes silverlight developing much safer, theoretically, at least for those really UNIT tests.

With the concern of integration tests, thing becomes a little bit difficult. At first I thought I could just write WCF test like this:


        [Test]
        public void Should_call_find_posts_by_keywords()
        {
            var client = new MyServiceClient();
            client.FindPostsByKeywordCompleted += (sender, args) => {};
        }

The first problem I encountered is, I couldn’t get the client config in Silverlight UnitTest project. Tried adding linked file and adding service reference, no good. My ugly workaround is to hardcode the endpoint address for now:


        [Test]
        public void Should_call_find_posts_by_keywords()
        {
            var client = new MyServiceClient(new System.ServiceModel.BasicHttpBinding(),
                                        new EndpointAddress(
                                            "http://localhost/WcfServices/PostService.svc"));
            client.FindPostsByKeywordCompleted += (sender, args) => {};
        }

OK, First problem solved. Now, another one, statlight couldn’t report the exception if service got something wrong. While in Visualstudio the exception alert can always pop-up.

Something needs be done for asynchronous test. After read the posts from Jeff Wilcox and Jonas Folleso, I ended up with my test looks like this:


 [TestFixture]
    public class ProxyTests : SilverlightTest
    {
        [Test]
        [Asynchronous]
        public void Should_call_find_posts_by_keywords()
        {
            var client = new VineOnlineServiceClient(new System.ServiceModel.BasicHttpBinding(),
                                        new EndpointAddress(
                                            "http://localhost2/WcfServices/PostService.svc"));

            bool isSearchDone = false;
            IEnumerable<PostedItemDto> searchResults = null;

            client.FindPostsByKeywordCompleted += (
                (sender, args) =>
                    {
                        searchResults = args.Result;
                        isSearchDone = true;
                    });

            EnqueueCallback(() => client.FindPostsByKeywordAsync("post"));
            EnqueueConditional( () => isSearchDone );

            EnqueueCallback( () => Assert.IsNotNull(searchResults));  // Optional if we don't care the return data

            EnqueueTestComplete(); // Cannot move to teardown coz asynchronous
        }

IOC in WPF/WCF

It makes sense to take advantage of IOC in WCF, because all WCF services are parallel, and usually WCF service suppose to behave like a wrapper to Domain service which might needs a repository which needs an NHibernate session… How to new up those dependencies? IOC is the easiest way. And it becomes the new evil global.

In WPF, we’ve already got an un-avoidable global main window, or shell view in MVVM. Adding IOC just introduces more overhead. Even further Caliburn.Micro framework forces developer to implement a bootstrapper acting as the wrapper to the actual IOC container, the only dependency in this bootstrapper is the shell view model. If your shell view model has dependencies, you need to set them up in your underlying container. To me, this is a chicken-egg question.

 public class StructureMapBootstrapper : Bootstrapper<IShellViewModel>
    {

        protected override void Configure()
        {
            ObjectFactory.Initialize(x => x.AddRegistry<WpfDependencyRegistry>());
        }

        protected override object GetInstance(System.Type service, string key)
        {
            return ObjectFactory.GetInstance(service);
        }

    }

// StructureMap is not available in Silverlight yet, use SimplerCotainer from Caliburn instead in SL
   public class SilverlightBootstrapper : Bootstrapper<IShellViewModel>
    {
        private SimpleContainer _container;

        protected override void Configure()
        {
            _container = new SimpleContainer();

            _container.RegisterSingleton(typeof(IMessageBox), null, typeof(SlMessageBox));
            _container.RegisterSingleton(typeof(IProductSearchTask), null, typeof(ProductSearchTask));
            _container.RegisterSingleton(typeof(IProductCrudTask), null, typeof(ProductCrudTask));

            _container.RegisterSingleton(typeof(IShellViewModel), null, typeof(ShellViewModel));

        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }
    }

A better solution might be to use poor man injection in shell view model to new up all the dependencies in it’s no-arg ctor. This will remove any IOC usuage from WPF/Silverlight project, so UI developer can relief from framework hassle, and concentrate on their spinning button work.

The power of WCF Behaviors 4 – Endpoint Behavior

The best usage of endpoint Behavior is still in exception shielding, to convert those Fault from server back to it’s original exception, by calling messageInspector in EndpointBehavior.

But using serviceClientFactory is much easier. We can just unwrap it.

ServiceClientFacotry

...
       public TResult CallService
(Func function)
        {
            Client client = CreateClient();
            TResult result = default(TResult);

            try
            {
                result = function(client.Service);
                if (client.State != CommunicationState.Faulted)
                {
                    client.Close();
                }
                else
                {
                    client.Abort();
                }
            }
            catch (Exception e)
            {
                client.Abort();
                HandleException(e);
            }

            return result;

        }

        public virtual void HandleException(Exception ex)
        {

            if (ex is FaultException && ex.GetType().IsGenericType)
            {
                var result = (Exception)ex.GetType().GetProperty("Detail").GetValue(ex, null);
                throw result;
            }

            throw ex;
        }

The power of WCF Behaviors 3 – Contract Behavior

For unhandledDatabaseException, we don’t really need to catch it at client side, but it would be nice to let the global exception handler at client app know about this actual database exception type. To implement that, unhandledDatabaseException must be added to Contract, yes, all the methods/operations in Contract.

No worry, we can use Contract Behavior to automatically add it. ContractBehavior


   public class MyStandardFaultContractAttribute : Attribute, IContractBehavior
    {
        // this is a list of our standard fault detail classes.
        private static readonly Type[] Faults = new[]
                                                   {
                                                        typeof (UnhandledDatabaseException),
                                                    };

        public void AddBindingParameters(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint,
            BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint,
            ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint,
            DispatchRuntime dispatchRuntime)
        {
        }

        public void Validate(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint)
        {
            foreach (OperationDescription op in contractDescription.Operations)
            {
                foreach (Type fault in Faults)
                {
                    if (op.Faults.Any(f => f.DetailType == fault))
                    {
                        Console.WriteLine( fault + " already defined in " + op.Name);
                        continue;
                    }
                    Console.WriteLine("adding fault " + fault + " to " + op.Name);
                    op.Faults.Add(MakeFault(fault));
                }
            }

        }

        private FaultDescription MakeFault(Type detailType)
        {
            string action = detailType.Name;
            DescriptionAttribute description = (DescriptionAttribute)
                                               GetCustomAttribute(detailType, typeof (DescriptionAttribute));
            if (description != null)
                action = description.Description;
            FaultDescription fd = new FaultDescription(action);
            fd.DetailType = detailType;
            fd.Name = detailType.Name;
            return fd;
        }
    }

Watch out, don’t add too many exceptions to contract behind the scene. This is violating DesingByContract principle.

One thing I noticed is, if the exception is thrown by behavior, this works, but if the operation method throw the exception directly, this doesn’t. Using fiddler look at the soap message, all the elements in the exception block has an empty xmlns! Switch to NetDataContractSerializer in errorhandler, I can only control the namespace at the root level, all those child elements still has empty namespace.

The code from http://typesafe.be/2009/02/17/flexible-wcf-exception-shielding/ has the same result.

Anyway, the exception message can reach down to client in this case, even I lost the type, will look back it later.