Pro Tip: Return the same object for WCF service methods

In a previous post I made reference to a consistent return type from my WCF services for Silverlight to consume. Here’s what it is and why I think it’s useful.

WebServiceVoidWrapper
{
    public string ExceptionMessage { get; set; }
    public DateTime LastUpdated { get; set; }
    public bool IsGroupActive { get; set; }
}

WebServiceWrapper<T> : WebServiceVoidWrapper
{
    public T Results { get; set; }
}

Each of our calls, regardless of what they’re returning, need to let the client know about certain conditions. At first we only cared to grab the exception message. Then we added the LastUpdated to be populated when working with data pieces so we could query the server to see if any changes had occurred since. Using the structure above, we could expect all our methods to at least have those members. (The VoidWrapper version is for those times we’re not returning an data from the service but we still want those fields.)

When this was our approach, we had methods scattered in various XAML pages to check for and react to these conditions and this was fine if not anemic since we were short on time and didn’t really implement nice UI interaction to deal with errors.

At some point the business requirements changed and a user could now be part of multiple groups and could be added and removed at any time. We needed a way to react to that. We easily added the field to the base response class and use exception catching on the server side to populate the field. This also meant we were going to need to handle this case all over our Silverlight application because any call could signal the IsGroupActive as false which means the user shouldn’t be able to work with local data they already have.

Rather than peppering checks for this field, popping a child window to prompt the user for a new Organization and then react to the new data we decided to handle this a little more generically. I had constructed a singleton class to act as the interface for the Silverlight application’s communication to the server. Since this singleton is active for the life of the user’s interaction with the application we used this as our starting point.

With this layer in place, we could easily wire up listeners to all our WCF service events and attach a delegate to check for the field and react to it. A quick glance shows 42 methods we’re interested in and these methods grow and shrink and change names constantly during our development cycle. Rather than putting in 42 calls to SomethingCompleted += … I wrote a bit of code to wire up the listener automatically. See previous post for info on that: Dynamically Attach Delegates to WCF Service in Silverlight.

Now with a generic listener in place to catch the user org becoming invalid, we can deal with the UI aspect. Continuing the theme of least-amount-of-code-change we targeted the MainPage object as the best place to handle the interaction with the user. MainPage is available as our Application.Current.RootVisual and can pop child windows and control other UI aspects. It also has access to the NavigationService which we’ll be using as well.

To let the MainPage know the user org has become invalid, I decided to fire a custom event from the service where we detected the error.

[In ConnectionService.cs]
public event EventHandler OnUserOrganizationNotValid;

And subscribe to that event in MainPage.

[In MainPage.xaml.cs]
public MainPage()
{
    InitializeComponent();
    ConnectionService.GetInstance().OnUserOrganizationNotValid +=
        new EventHandler(MainPage_OnUserOrganizationNotValid);
}

void MainPage_OnUserOrganizationNotValid(object sender, EventArgs e)
{
    PopUserOrganizationPicker();
}

From the event delegate we’re calling out to another function. This is because we also have the ability to allow the user to choose a new organization whenever they wish. Rather than have a duplication of code somewhere else, we made a public method that can be accessed from other views.

private OrganizationDialogActive = false;
public void PopUserOrganizationPicker()
{
    if (!OrganizationDialogActive)
    {
        OrganizationDialogActive = true;
        Dispatcher.BeginInvoke(() =>
        {
            OrganizationPicker.InitializeForm();
            OrganizationPicker.Show();
        });
    }
}

void OrganizationDialogClosed(object sender, EventArgs e)
{
    OrganizationDialogActive = false;
    ContentFrame.Refresh();
}

I’m using a property for the org dialog window which wires up the callback event on Closed event. This is nice since the window is late loaded on first use instead of at load time and since nothing really changes, we just keep it in memory.

private SelectOrganizationDialog _OrganizationPickerBackingField;
private SelectOrganizationDialog
{
    get
    {
        if (null == _OrganizationPickerBackingField)
        {
            _OrganizationPickerBackingField =
            new SelectOrganizationDialog(OrganizationDialogClosed);
        }
        return _OrganizationPickerBackingField;
    }
}

To get the user’s screen to update, we could place code to navigate in the MainPage but we’ve stuck it in the org picker child window instead. This is because our service method returns a new URL if the newly chosen org makes the user a power user which will need to send them to a new page. From the service callback in the org picker child window we call a helper method NavigateToPage.

//DefaultPage holds a relative url string for the destination page.
public static void NavigateToPage()
{
    MainPage mp = Application.Current.RootVisual as MainPage;
    Uri defaultUri = new Uri(DefaultPage, UriKind.Relative);

    if (mp.ContentFrame.Source.OriginalString.Equals(DefaultPage))
        mp.ContentFrame.Refresh();
    else
        if (!mp.ContentFrame.Navigate(defaultUri))
            mp.ContentFrame.Refresh();
}

Faleminderit!
-Erik

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s