views:

44

answers:

1

I am trying to reuse some .NET code that performs some calls to a data-access-layer type service. I have managed to package up both the input to the method and the output from the method, but unfortunately the service is called from inside code that I really don't want to rewrite in order to be asynchronous.

Unfortunately, the webservice code generated in Silverlight only produces asynchronous methods, so I was wondering if anyone had working code that managed to work around this?

Note: I don't need to execute the main code path here on the UI thread, but the code in question will expect that calls it makes to the data access layers are synchronous in nature, but the entire job can be mainly executing on a background thread.

I tried the recipe found here: The Easy Way To Synchronously Call WCF Services In Silverlight, but unfortunately it times out and never completes the call.

Or rather, what seems to happen is that the completed event handler is called, but only after the method returns. I am suspecting that the event handler is called from a dispatcher or similar, and since I'm blocking the main thread here, it never completes until the code is actually back into the GUI loop.

Or something like that.

Here's my own version that I wrote before I found the above recipe, but it suffers from the same problem:

public static object ExecuteRequestOnServer(Type dalInterfaceType, string methodName, object[] arguments)
{
    string securityToken = "DUMMYTOKEN";
    string input = "DUMMYINPUT";
    object result = null;
    Exception resultException = null;
    object evtLock = new object();

    var evt = new System.Threading.ManualResetEvent(false);
    try
    {
        var client = new MinGatServices.DataAccessLayerServiceSoapClient();
        client.ExecuteRequestCompleted += (s, e) =>
        {
            resultException = e.Error;
            result = e.Result;
            lock (evtLock)
            {
                if (evt != null)
                    evt.Set();
            }
        };
        client.ExecuteRequestAsync(securityToken, input);
        try
        {
            var didComplete = evt.WaitOne(10000);
            if (!didComplete)
                throw new TimeoutException("A data access layer web service request timed out (" + dalInterfaceType.Name + "." + methodName + ")");
        }
        finally
        {
            client.CloseAsync();
        }
    }
    finally
    {
        lock (evtLock)
        {
            evt.Close();
            evt = null;
        }
    }

    if (resultException != null)
        throw resultException;
    else
        return result;
}

Basically, both recipes does this:

  • Set up a ManualResetEvent
  • Hook into the Completed event
  • The event handler grabs the result from the service call, and signals the event
  • The main thread now starts the web service call asynchronously
  • It then waits for the event to become signalled

However, the event handler is not called until the method above has returned, hence my code that checks for evt != null and such, to avoid TargetInvocationException from killing my program after the method has timed out.

Does anyone know:

  • ... if it is possible at all in Silverlight 3
  • ... what I have done wrong above?
+1  A: 

I suspect that the MinGatServices thingy is trying to be helpful by ensuring the ExecuteRequestCompleted is dispatched on the main UI thread.

I also suspect that your code is already executing on the main UI thread which you have blocked. Never block the UI thread in Silverlight, if you need to block the UI use something like the BusyIndicator control.

The knee-jerk answer is "code asynchronously" but that doesn't satisfy your question's requirement.

One possible solution that may be less troublesome is to start the whole chunk of code from whatever user action invokes it on a different thread, using say the BackgroundWorker.

Of course the MinGatServices might be ensuring the callback occurs on the same thread that executed ExecuteRequestAsync in which case you'll need to get that to run on a different thread (jumping back to the UI thread would be acceptable):-

 Deployment.Current.Dispatcher.BeginInvoke(() => client.ExecuteRequestAsync(securityToken, input));
AnthonyWJones
I will try this, the MinGatServices name is just a namespace, and the class is basically just containing the method in the question so whatever traps I've laid for myself are all visible in the question, but yes, I do execute the main code that calls this on the UI thread so I'll try to split up the execution paths here. But should I understand your answer that "service.MethodNameAsync" runs on the UI thread through the Dispatcher class then? So since I invoke this from the UI thread, I now have the UI thread waiting for the UI thread?
Lasse V. Karlsen
@Lasse: Well, __if__ the async method is trying to be helpful then it will be using the Dispatcher for the thread to queue up code to be executed on the thread. So yes you do end up with this deadlock.
AnthonyWJones