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

Revisit UI Validation

Central validation is not practical, distributed validation is OK as long as they are centrally controlled, e.g., through CI, even simpler, a linked file among projects.

Attribute validation on DTO is perfect for UI/service input validation. But most validation attribute are not serializable, if shared assembly can not be used, the linked file contains core validation rules can be a work around, especially for Silverlight app.


// This can be duplicated in both service and UI
[RegularExpression(ValidationRules.PostCriteriaRules.KeywordRegex)]
public string Keyword { get; set; }

// The core rule is defined in shared/linked file
    public static class ValidationRules
    {
        public static class PostCriteriaRules
        {
            public const string KeywordRegex = @"^[a-zA-Z0-9]{3,}$";
        }
    }

For WPF/Silverlight, an extension method to IDataError can be used to simply wire up code:

public static class DataAnnotationValidator
    {
        public static string ValidateProperty(this IDataErrorInfo viewModel, string columnName)
        {
            PropertyInfo prop = viewModel.GetType().GetProperty(columnName);

            IEnumerable validationMap = prop
                .GetCustomAttributes(typeof(ValidationAttribute), true)
                .Cast()
                ;

            foreach (ValidationAttribute v in validationMap)
            {
                try
                {
                    v.Validate(prop.GetValue(viewModel, null), new ValidationContext(viewModel, null, null) { MemberName = columnName });
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
            }

            return null;
        }
    }

 public class ShellViewModel : Conductor.Collection.OneActive, IShellViewModel,  IDataErrorInfo
    {....

 public string this[string columnName]
        {
            get {
                return this.ValidateProperty(columnName);
            }
        }

        public string Error
        {
            get { throw new NotImplementedException(); }
        }
    }

It would be nice if the Silverlight version of DataAnnotation can support MetadataType, then we can share the whole validation file.

MetadataType can be a solution if you want to re-do the same input validation in domain model, I don’t know why people want to do this. considering that most the property in domain model should not have setter.

 [MetadataType(typeof(PostCriteriaValidation))]
    public class ShellViewModel : Conductor.Collection.OneActive, IShellViewModel,  IDataErrorInfo
    {...

To use DataAnnotation in Silverlight is very simple:

<TextBox Height="23" HorizontalAlignment="Left" Margin="12,33,0,0"   x:Name="Keywords" VerticalAlignment="Top" Width="120"  >
 <TextBox.Text>
 <Binding Path="Keywords"  Mode="TwoWay" ValidatesOnExceptions="true"  ValidatesOnDataErrors="true" NotifyOnValidationError="true" />
 </TextBox.Text>
 </TextBox>


On the other hand, DataAnnotation has very limited feature, we couldn’t make a simply start-date/end-date validation working.

NHibernate.Validator seems more powerful, but I’m not sure is it good for Silverlight. It also has dependency to log4net and NHibernate.

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
        }

Unit test in Silverlight

SilverLight NUnit project and NUnit SilverLight project are two different projects.

The first one can only be used inside IDE, launched by testRunner.

NUnit Silverlight is intend to replace the default MSTest with NUint syntax in default Silverlight unit-test project ship with Silverlight tool-kit. By default, SilverLight UNnit Test project from Toolkit comes with MSTest, it can be replaced by NUnit, by referencing NUnit SilverLight project.

To make it continuous-build friendly, I  had to create a local lib folder right under silverlight unit-test project then create those linked reference, otherwise build server kept complaining missing reference error.

RhinoMock has a SilverLight edtion in case you need to mock something.

This walk through explained how to use MsBuild to implement a command line runner, note, must navigate to test project path to launch MSbuild command, not the web one.

Unfortunately, I got the same broswer-not-closing problem. I haven’t got time to try this workaround yet.

A bettor solution is to use StatLight. Need to find out the xsl file for CC.net. A simple xsl file to cc.net can be found at here.

SilverUnit is only used by TypeMock, a commercial product.

I hate SilverLight

How to Start

SilverLight sdk or toolkit or whatever bootstrap installation package doesn’t come with VS2008, all kinds of confusions when looking for “How to start”. The default IIS setting doesn’t support xap either.

Cache

Somewhere the magic cache (Server or Browser?) stop me from seeing my code change. I had to delete the clientBin folder, then clean my IE temp files, then to see my changes. Is there a easy way?

clientaccesspolicy.xml never works on my machine! Even silverlight app and WCF service are on the same IIS server. I had to go through the non-localhost host name to visit my localhost.

WCF FaultExcetption

SilverLight can’t consume regular / default WCF service, I had to add the basic http binding to WCF service just like what I did to expose WCF service to PowerBuidler, but, SilverLight doesn’t know WCF FaultException! I had to use fiddler to see the exception/stack info in the traffic to figure out what’s going on.

Add FaultContract  into ServiceContract will make WCF serice unavialable to silverlight.   There is a workaround to put faultexception into out parameter, but we can’t go this way because our service is shared, not only for silverlight. what I figured out is, it’s safe to add fautdetail as customized fault, do NOT  inherited from Exception type.

The easiest way for me is just changing httpstatus code from 500 to 200, the silverlight client side still keeps same:

        private void Application_Startup(object sender, StartupEventArgs e)
        {

                var client = new AglcSecureWebServiceClient();

                client.ServcieCallCompleted += client_ServcieCallCompleted;
                client.ServcieCallAsync(view);

        }

        private void client_ServcieCallCompleted(object sender, ServcieCallCompletedEventArgs e)
        {
            try
            {

                // good call , continue ...

            }
            catch (Exception err)
            {
                Exception innerException = err.InnerException;

                if (innerException is FaultException<MyFault>)
                {
                    Debug.WriteLine("FaultException occoured: " + innerException.Message);
                    // business code ...
                }else if(innerException is CommunicationException)
                {
                    MessageBox.Show("Communication exception occoured."+ innerException.Message);
//                    throw err; // depends app design, hide or throw.
                }else
                {
                    throw;
                }

            }
        }