Simplifying the Process of Calling a WCF Service from Silverlight (or any .NET Application)

I’m currently working on a large Silverlight 3 project for a client that will ultimately have hundreds of calls from Silverlight to WCF service operations.  Initially I did things the standard way which is to define a client-side class that’s used to call the WCF service using the generated service proxy (I’m basically following a service agent type of pattern).  The service agent class handles instantiating the WCF proxy object and calling the appropriate operations.  Here are a few examples….each of these methods accept the parameters passed to the WCF service as well as a callback method that’s called when the service returns data.

public void GetJobs(EventHandler<GetJobsCompletedEventArgs> callback)
{
    IJobPlanService proxy = GetProxy();
    proxy.GetJobsCompleted += callback;
    proxy.GetJobsAsync();
}

public void GetEmployees(EventHandler<GetEmployeesCompletedEventArgs> callback)
{
    IJobPlanService proxy = GetProxy();
    proxy.GetEmployeesCompleted += callback;
    proxy.GetEmployeesAsync();
}

public void GetEmployeesByJobID(int jobID, EventHandler<GetEmployeesByJobIDCompletedEventArgs> callback)
{
    IJobPlanService proxy = GetProxy();
    proxy.GetEmployeesByJobIDCompleted += callback;
    proxy.GetEmployeesByJobIDAsync(jobID);
}

public void GetTimeSheetViews(int? jobID, int? empID, string weekEnding, EventHandler<GetTimeSheetViewsCompletedEventArgs> callback)
{
    IJobPlanService proxy = GetProxy();
    proxy.GetTimeSheetViewsCompleted += callback;
    proxy.GetTimeSheetViewsAsync(jobID, empID, weekEnding);
}

After writing nearly the same code method after method (aside from changes in the event name and method name) I opted for a simplified approach that allows me to call any WCF service operation using a single method.  That way I don’t have to continually write wrapper methods just to call a service operation which really seems pointless to me.  Some people will like the following code since it can eliminate a ton of code from your Silverlight project (the code would work fine outside of Silverlight too by the way) while others won’t like it since reflection is involved (I personally don’t view reflection as a big deal here since it’s running on the client-side).

Here’s how it works:

  1. The CallService method shown next accepts an async callback delegate as well as the parameter data that should be passed to the service.  In other words, tell it what method to call once the service returns data as well as what data to pass up to the service operation.
  2. When calling the CallService method you must supply the type of EventArgs that will be returned from the service call.  This is done using generics. 
/// <summary>
/// Used to call a WCF service operation.
/// </summary>
/// <typeparam name="T">The type of EventArgs that will be returned by the service operation.</typeparam>
/// <param name="callback">The method to call once the WCF call returns (the callback).</param>
/// <param name="parameters">Any parameters that the service operation expects.</param>
public void CallService<T>(EventHandler<T> callback, params object[] parameters) where T : EventArgs
{
    string action = typeof(T).Name.Replace("CompletedEventArgs", String.Empty);
    IJobPlanService proxy = new JobPlanServiceClient();
    Type t = typeof(JobPlanServiceClient);
    t.GetEvent(action + "Completed").AddEventHandler(proxy, callback);
    t.InvokeMember(action + "Async", BindingFlags.InvokeMethod, null, proxy, parameters);
}


An example of using the CallService method is shown next:

public void GetAreasByJobID()
{
    _Proxy.CallService<GetAreasByJobIDCompletedEventArgs>((s,e) => this.CurrentTimeSheetView.Areas = e.Result,
        this.TimeSheetJob.JobID);
}

The GetAreasByJobID method passes a type of GetAreasByJobIDCompletedEventArgs as the generic type to CallService, defines the async callback using a lambda expression (although a separate method could certainly be defined) and passes the parameter that the service operation expects (multiple parameters can be passed when needed).  CallService knows which WCF service operation to call based upon the generic type that’s passed since WCF service proxy object’s always create EventArgs classes that end with “CompletedEventArgs”.  The method simply removes the string “CompletedEventArgs” to get the operation name, instantiates the service proxy object and then uses reflection to wire up the appropriate event and async method to call.  If you use several different service proxy objects you could certainly handle that as well with generics.  I played around with ChannelFactory as well but opted for this approach mainly because it was more straightforward in my opinion and because I wanted to use the generated WCF proxy anyway.

If scalability was an issue (which is not the case here since this runs on the client-side) then I’d go with the standard approach of using the WCF proxy methods that are generated as shown in the first section of code above.  For this application it’s eliminated hundreds of lines of redundant code though (which would’ve grown to 1000s since we’re only 25% of the way through the project) which means we have less to worry about plus this eliminates the time it would’ve taken to write the wrapper methods.  Ultimately I view this as a significant benefit to future maintenance since there’s less wrapper code that can get out of sync. 

 

Logo

For more information about onsite, online and video training, mentoring and consulting solutions for .NET, SharePoint or Silverlight please visit http://www.thewahlingroup.com/.

comments powered by Disqus

10 Comments

  • How about session? You create a new Proxy with each request and thus loose session. The other problem is that I do not see any disposal or closing of the proxy.

  • Way to Go !!! Thanks for sharing.

  • webbes:

    Good points. I don't use session functionality so I don't worry about that particular feature. As far as closing, that functionality can certainly be handled in the callback if desired: ((YourProxyType)sender).CloseAsync();

  • Fantastic idea. We are at the stage of implementing the WCF services we have also. I will for sure put this in my base class's. No need to write 1000 functions. Great use of the Param -- I have not had a chance to use it.

  • @webbes

    I wouldn't expect IIS or WCF to hold your session state -- If you need a session, make a token that's passed with each call from the SL app -- such as a GUID or something, and handle the users session via your application. &nbsp;You don't want your app to reply on an intermediate party (like IIS) which could dump the users session when the app pool recycles. Also allows you to scale to different servers/services with very little work.

    I'm looking at extending this example so it will detect the client class and interface associated -- our WCF services host many different interfaces, and even different service locations, so this is a good starting point to help abstract our calls a bit more.

    Again, great idea with this. I appreciate the post. &nbsp;Once I get your idea on paper working with my idea, ill post it up and link back.

  • Travis:

    I'm definitely interested in any enhancements you make. The initial concept doesn't account for different service contracts or proxy objects but could of course so let me know what you come up with.

  • WCF proxies can incur significant penalty if they are being created all the time?
    You are also using reflection that has small penalty also.

  • Radenko:

    In this case I'd argue no but if your application is making tons of calls triggered by code you may want to instantiate the proxy at the class level and then use it in the CallService method. By re-using the proxy you do have to watch out for faulted channels though....I didn't want to deal with that and since the end user triggers most of the app's calls I create it as needed. But, every application is different. :-)

  • fantastic post!

    -thanks.

  • Another approach for dealing with redundant code, instead of using reflection and to get around the other problems people have mentioned, is to use the M modeling language built into Visual Studio to define a domain specific language (DSL) that generates the wrapper methods. Microsoft is using M for many of the wizards and other code generators already. It is an idea way to keep the wrapper methods in sync with the code plus get compile time syntax checks as the model generates code during each build automatically.

Comments have been disabled for this content.