If you're reading this you probably know why Microsoft tries to force the use of asynchronous WCF service calls in Silverlight. If not and you cannot infer based on your knowledge of threading I suggest you read up on the matter.
While Microsoft may have a strong case for their decision, it can lead to bloated, messy code when several service calls are chained together. The following extension method, through heavy use of reflection, allows for synchronous service calls. Remember, do not block the UI thread.
ICommunicationObjectExtension.cs
using System;
using System.Net;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.ServiceModel;
namespace SyncWCFCalls
{
public static class ICommunicationObjectExtension
{
public static TEventArgs SynchronousCall<TEventArgs>(this ICommunicationObject Client,
string Method, params object[] Parameters)
where TEventArgs : EventArgs
{
Type clientType = Client.GetType();
string methodName = Method + "Async";
string completedName = Method + "Completed";
MethodInfo[] methods = null;
try
{
methods = clientType.GetMethods().Where(x =>
x.Name == methodName).ToArray();
}
catch { }
if (methods == null || methods.Length == 0)
{
throw new MissingMethodException(methodName + " not found");
}
MethodInfo method = null;
try
{
method = methods.Single(x =>
x.GetParameters().Length == Parameters.Length);
}
catch { }
if (method == null)
{
throw new MissingMethodException(methodName +
" parameter count mismatch");
}
EventInfo completedEvent = null;
try
{
completedEvent = clientType.GetEvent(completedName);
}
catch { }
if (completedEvent == null)
throw new MissingMemberException(completedName + " not found");
ManualResetEvent reset = new ManualResetEvent(false);
EventHandler<TEventArgs> completedHandler = null;
TEventArgs args = null;
completedHandler = (s, e) =>
{
completedEvent.RemoveEventHandler(Client, completedHandler);
args = e;
reset.Set();
};
completedEvent.AddEventHandler(Client, completedHandler);
method.Invoke(Client, Parameters);
reset.WaitOne();
return args;
}
}
}
Asynchronous WCF Service Call
void AsyncTest()
{
CalculatorServiceClient client = new CalculatorServiceClient();
Guid addState1 = Guid.NewGuid(), addState2 = Guid.NewGuid();
client.AddCompleted += (s, e) =>
{
if ((Guid)e.UserState == addState1)
{
// Do something with AddCompletedEventArgs
client.AddAsync(e.Result, 10, addState2);
}
else if ((Guid)e.UserState == addState2)
{
// Do something with AddCompletedEventArgs
client.SubtractAsync(e.Result, 5);
}
};
client.SubtractCompleted += (s, e) =>
{
// Do something with SubtractCompletedEventArgs
};
client.AddAsync(1, 1, addState1);
}
Synchronous WCF Service Call
void SyncTest()
{
CalculatorServiceClient client = new CalculatorServiceClient();
ThreadPool.QueueUserWorkItem(x =>
{
AddCompletedEventArgs addArgs1 =
client.SynchronousCall<AddCompletedEventArgs>("Add", 1, 1);
// Do something with AddCompletedEventArgs
AddCompletedEventArgs addArgs2 =
client.SynchronousCall<AddCompletedEventArgs>("Add",
addArgs1.Result, 10);
// Do something with AddCompletedEventArgs
SubtractCompletedEventArgs subtractArgs =
client.SynchronousCall<SubtractCompletedEventArgs>("Subtract",
addArgs2.Result, 5);
// Do something with SubtractCompletedEventArgs
});
}
Updated 3/21/2010