Simplifying Asynchronous Calls in Silverlight using Action

This post explores a way to encapsulate web service calls from Silverlight in a way that is easy to use and understand.

As anyone who works with Silverlight knows, Silverlight forces web service calls to be asynchronous. There is a good reason for this, which I won’t get into with this post. What I would like to do is demonstrate an approach that advocates a clean separation of the internals of the service from the application that is consuming it.

First, I am assuming you understand web services and WCF. I assume you’ve called services from Silverlight, and are aware of the nuances of configuring the endpoint, etc. I am also going to assume you are familiar with some frameworks such as Prism, MEF, Caliburn, etc that advocate clean separation of concerns and modular design.

Let’s assume I have a service on my web page that is a calculator (oh, where did I get that idea?) It looks something like this:

[ServiceContract(Namespace = "https://training.atmosera.com/sample")]    
public interface ICalculatorService
{
    [OperationContract]
    long Multiply(int x, int y);
}

Simple, correct? The first thing I’m going to do is to create a similar interface on my Silverlight client. This interface will hide the details of how the service is connected and wired. I also want to make it easier to deal with the asynchronous nature of the calls. I’ve seen some pretty exotic implementations to try to make an asynchronous call look synchronous, and don’t quite understand why we’d want to do it. Instead, let’s just define a contract like this somewhere in the Silverlight application that is “seen” globally:

public interface ICalculator 
{
   void Multiply(int x, int y, Action<long> result); 
}

As you can see, it’s a simple void method … in fact, all methods we use to abstract the asynchronous calls will most likely be void. But the key is last parameter. This delegate allows me to specify how I want to deal with the result.

Now I create a project that is solely used to implement ICalculator and connect with the web service. First, if you haven’t read my post about abstracting WCF service calls, be sure to read it: Abstracting WCF Service Calls in Silverlight. It explains how I use a base class to avoid having to change the ServiceReferences.ClientConfig file everytime I deploy an application, and also will give you the concept for the BaseService class you see below.

public class CalculatorService : BaseService<CalculatorServiceClient,ICalculatorService>, ICalculator
{
    public CalculatorService()
    {
        _GetClientChannel().MultiplyCompleted += new EventHandler<MultiplyCompletedEventArgs>(CalculatorService_MultiplyCompleted);
    }        

    #region Event handlers

    void CalculatorService_MultiplyCompleted(object sender, MultiplyCompletedEventArgs e)
    {
        Action<long> result = e.UserState as Action<long>;
        if (result != null)
        {
            result(e.Result); 
        }
    }

    #endregion 

    #region ICalculator Members

    public void Multiply(int x, int y, Action<long> result)
    {
        _GetClientChannel().MultiplyAsync(x, y, result);
    }

    #endregion
}

The base class preps the channel and credentials, etc for me. In the constructor of the implementation, I simply register for the completion event for the call. When a consumer of the service makes a call, the consumer provides a delegate to call back to when the service is done. This is passed in the user state, then cast back to an action and called once the service returns. If you wanted to have error handling, you’d just extend the action delegate to have an error-related parameter and check the e.Error when the call completed.

Now we can put it into use. In my bootstrapper, I’ll bind the service to the contract like this:

Container.RegisterType<ICalculator,CalculatorService>(new ContainerControlledLifetimeManager());
            

Now I can throw a view model together that looks something like this:

public class CalculatorViewModel : INotifyPropertyChanged
{

    private bool _busy = false;

    public bool BusyState
    {
        get { return _busy; }
        set
        {
            if (!value.Equals(_busy))
            {
                _busy = value;
                OnPropertyChanged("BusyState");
                CalculateCommand.RaiseCanExecuteChanged();
            }
        }
    }

   public DelegateCommand<object> CalculateCommand { get; set; } 

    public CalculatorViewModel(ICalculator calculator)
    {
        Calculator = calculator;
        CalculateCommand = new DelegateCommand<object>(o => CommandCalculate(),
            o => !BusyState);
    }

    public ICalulator Calculator { get; set; }

    private int _x;

    public int X
    {
        get { return _x; }
        set
        {
            if (!value.Equals(_x))
            {
                _x = value;
                OnPropertyChanged("X"); 
            }
        }
    }

     private int _y;

    public int Y
    {
        get { return _y; }
        set
        {
            if (!value.Equals(_y))
            {
                _y = value;
                OnPropertyChanged("Y"); 
            }
        }
    }

    private long _answer;

    public long Answer
    {
        get { return _answer; }
        set
        {
            if (!value.Equals(_answer))
            {
                _answer = value;
                OnPropertyChanged("Answer"); 
            }
        }
    }

    public void CommandCalculate()
    {
        BusyState = true;
        Calculator.Multiply(_x, _y, answer => {
              BusyState = false;
              Answer = answer;
         }); 
    }

    private void OnPropertyChanged(string property)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(property));
        }
    }

    #endregion 

    #region INotifyPropertyChanged Members

   public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

Now I can simply data bind my XAML to the X, Y, and Answer properties, and bind a button to the Click command. The button will even disable itself while the client is waiting on the server to return the answer. As you can see, the implementation is very straightforward and I don’t have to worry about how the call happens. In fact, using this type of abstraction, I could easily wire in a mock class to satisfy the service for testing like this… or maybe decide that a web service for multiplication is overkill and just satisy the contract locally:

public class MockCalculator : ICalculator 
{
   public void Multiply(int x, int y, Action<long> result) 
   {
      result(x*y);
   }
}

While the Action command is really a different way of expressing a delegate, it also makes the intent of the code clear and allows for a clean separation of mechanism from result when dealing with asynchronous calls.

Jeremy Likness

Take advantage of our free assessment and Azure credits.

We understand that evolving your IT can be costly.
We are offering you a unique opportunity to save you money and get you started faster.

Get $2,500 Azure Credits and a Free Architecture Roadmap

Get up to 5 hours of architecture work and $2,500 worth of Azure credits, free of charge.

Assess and Migrate Your Existing Environment

We right-size your systems and move them into an optimized environment.

Improve Your Information Security and Compliance

We work closely with your team to develop the right roadmap.

CONTACT US TODAY

Atmosera cited by Gartner as a Representative Vendor in the Market Guide for Cloud Service Providers to Healthcare Delivery Organizations

We help healthcare solution providers make the most of the cloud.

We are a Gold certified Microsoft Azure service provider offering a comprehensive set of managed services.
We excel at working with care providers and their ecosystem of solution providers.

We Know Healthcare:

We are fortunate to count a number of leading care providers and healthcare vendors as our customers. We continue to grow this segment as companies turn to secure and compliant cloud solutions.

Collaborate with Experts

  • Plan out your cloud strategy without having to commit all your applications to the public cloud
  • Microsoft has the only viable hybrid strategy and expected to surpass AWS in market share by 2019.
  • We specialize in engineering, deploying and operating the right solution for your business by combining public and private Azure.
  • As one of the world’s five largest Microsoft cloud solution providers (CSP), we help you identify the optimal environment to run each application including your database and storage.

Migrate Environments

  • We have expertise which minimizes redevelopment to move applications and data without recoding.
  • We leverage Microsoft Azure Site Recovery (ASR) which provides a simple way to move applications and data without having to redevelop the underlying cloud infrastructure.
  • We implement your new environment on a public or private cloud depending on your preferences and business requirements.
  • We enable access to complete and accurate PHI for better medical care.
Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X