Silverlight 3 and RIA Service – Cross Domain Proxy Enhanced

Some time ago I wrote a very small RIA Service that could be used to overcome Cross Domain issues. It’s a proxy that sits between your Silverlight application and any server where you want to get content over http.

A lot people know that not even half of the public API’s have cross domain configurations in place. The best solution that’s left is a proxy that can forward the calls to the public API and return the response back to the original caller. This article will expand the Silverlight part of the Cross Domain Proxy. Nothing needs to be changed to the RIA Service part of this Cross Domain Proxy.

What is the problem we have with the solution from my earlier article about Cross Domain Proxy?

One thing: The Cross Domain Proxy RIA Service is a generic service that can be used for all cross domain accesses. So basically we could write an application that’s performing multiple calls through the cross domain proxy at the same time. But how do we use the responses? We basically have one event “ProcessCompleted”, should we then react to this event by doing different things? Yes that could be, but we could do it differently as well.

I’m thinking about a ProxyService that’s running on Silverlight which has a little bit more knowledge compared to the RIA Services generated stub. This service would enable to do a cross domain request but provide a function to be called on completion at the same time.

The code using this service would look like this (firing multiple cross domain requests at the same time):

var domainProxyService = new DomainProxyService();
domainProxyService.Process(
    new ProxyRequest
        {
            Url = "http://www.silverlight.net/"
        },
    response => Debug.WriteLine(string.Format("1:", response.Content)));
domainProxyService.Process(
    new ProxyRequest
        {
            Url = "http://www.asp.net/"
        },
    response => Debug.WriteLine(string.Format("2:", response.Content)));

domainProxyService.Process(
    new ProxyRequest
        {
            Url = "http://www.azure.net/"
        },
    response => Debug.WriteLine(string.Format("3:", response.Content)));

The DomainProxyService needs to be intelligent enough to be able to route the reponses to the correct function pointers.

So there needs to be a key that can help associate the request with the response. In my last article I introduced the property Id in the ProxyRequest and the RequestId in the ProxyResponse. But for all purpose I don’t want to put a value in the Id, as a caller I don’t use this field, it’s just for internal usage so that association between Request and Response is possible.

My idea is to put a dictionary in the DomainProxyService to associate the request id with a function pointer. I want the function point to be of a type void that accepts one parameter of type ProxyResponse. That would make a dictionary like this:

private readonly Dictionary<Guid, Action<ProxyResponse>> m_dictionaryOfListeners =
    new Dictionary<Guid, Action<ProxyResponse>>();

The method to process a request isn’t that difficult. It will generate a new Id if it’s still empty. Besides that it will add an item to the dictionary, the key with the associated function to call on completion. And after all the most important part, we will call the original RIA Service.

public void Process(ProxyRequest proxyRequest, Action<ProxyResponse> callBack)
{
    //Generate a unique id if it doesn't contain an id yet.
    if (Guid.Empty == proxyRequest.Id)
        proxyRequest.Id = Guid.NewGuid();
    //Add the callback to list of listeners for use on completion.
    m_dictionaryOfListeners.Add(proxyRequest.Id, callBack);

    m_domainProxy.Process(proxyRequest);
}

We will still need to have one generic listener for the ProcessCompleted event. This listener will look up the appropriate function to call in the dictionary. Will invoke the function and remove the item in the dictionary.

private void DomainProxyProcessCompleted(object sender, InvokeEventArgs e)
{
    var proxyResponse = e.ReturnValue as ProxyResponse;
    if (proxyResponse != null)
    {
        //If the dictionary contains listeners for this request
        if (m_dictionaryOfListeners.ContainsKey(proxyResponse.RequestId))
        {
            m_dictionaryOfListeners[proxyResponse.RequestId].Invoke(proxyResponse);
            m_dictionaryOfListeners.Remove(proxyResponse.RequestId);
        }
    }
}

To be complete here the full source of the DomainProxyService class. Please remember this article continues on my previous article about Domain Proxy.

public class DomainProxyService
{
    private readonly Dictionary<Guid, Action<ProxyResponse>> m_dictionaryOfListeners =
        new Dictionary<Guid, Action<ProxyResponse>>();

    private readonly DomainProxy m_domainProxy = new DomainProxy();

    public DomainProxyService()
    {
        m_domainProxy.ProcessCompleted += DomainProxyProcessCompleted;
    }

    private void DomainProxyProcessCompleted(object sender, InvokeEventArgs e)
    {
        var proxyResponse = e.ReturnValue as ProxyResponse;
        if (proxyResponse != null)
        {
            //If the dictionary contains listeners for this request
            if (m_dictionaryOfListeners.ContainsKey(proxyResponse.RequestId))
            {
                m_dictionaryOfListeners[proxyResponse.RequestId].Invoke(proxyResponse);
                m_dictionaryOfListeners.Remove(proxyResponse.RequestId);
            }
        }
    }

    public void Process(ProxyRequest proxyRequest, Action<ProxyResponse> callBack)
    {
        //Generate a unique id if it doesn't contain an id yet.
        if (Guid.Empty == proxyRequest.Id)
            proxyRequest.Id = Guid.NewGuid();
        //Add the callback to list of listeners for use on completion.
        m_dictionaryOfListeners.Add(proxyRequest.Id, callBack);

        m_domainProxy.Process(proxyRequest);
    }
}


Ps. This article is cross posted on:
Mark Monster’s blog and Silverlight Help.

Gravatar