If you’re building Silverlight applications that consume data then you’re probably making calls to Web Services. We’ve been successfully using WCF along with Silverlight for several client Line of Business (LOB) applications and passing a lot of data back and forth. Due to the pain involved with updating the ServiceReferences.ClientConfig file generated by a Silverlight service proxy (see Tim Heuer’s post on that subject to see different ways to deal with it) we’ve been using our own technique to figure out the service URL. Going that route makes it a peace of cake to switch between development, staging and production environments. To start, we have a ServiceProxyBase class that handles identifying the URL to use based on the XAP file’s location (this assumes that the service is in the same Web project that serves up the XAP file). The GetServiceUrlBase() method handles this work: public class ServiceProxyBase
{
public ServiceProxyBase()
{
if (!IsDesignTime)
{
ServiceUrlBase = GetServiceUrlBase();
}
}
public string ServiceUrlBase { get; set; }
public static bool IsDesignTime
{
get
{
return (Application.Current == null) || (Application.Current.GetType() == typeof (Application));
}
}
public static string GetServiceUrlBase()
{
if (!IsDesignTime)
{
string url = Application.Current.Host.Source.OriginalString;
return url.Substring(0, url.IndexOf("/ClientBin", StringComparison.InvariantCultureIgnoreCase));
}
return null;
}
}
Silverlight 4 now supports relative paths to services which greatly simplifies things. We changed the code above to the following:
public class ServiceProxyBase
{
private const string ServiceUrlPath = "../Services/JobPlanService.svc";
public ServiceProxyBase()
{
if (!IsDesignTime)
{
ServiceUrl = ServiceUrlPath;
}
}
public string ServiceUrl { get; set; }
public static bool IsDesignTime
{
get
{
return (Application.Current == null) || (Application.Current.GetType() == typeof (Application));
}
}
public static string GetServiceUrl()
{
if (!IsDesignTime)
{
return ServiceUrlPath;
}
return null;
}
}
Our ServiceProxy class derives from ServiceProxyBase and handles creating the ABC’s (Address, Binding, Contract) needed for a WCF service call. Looking through the code (mainly the constructor) you’ll notice that the service URI is created by supplying the base path to the XAP file along with the relative path defined in ServiceProxyBase:
public class ServiceProxy : ServiceProxyBase, IServiceProxy
{
private const string CompletedEventargs = "CompletedEventArgs";
private const string Completed = "Completed";
private const string Async = "Async";
private readonly CustomBinding _Binding;
private readonly EndpointAddress _EndPointAddress;
private readonly Uri _ServiceUri;
private readonly Type _ProxyType = typeof(JobPlanServiceClient);
public ServiceProxy()
{
_ServiceUri = new Uri(Application.Current.Host.Source, ServiceUrl);
var elements = new BindingElementCollection
{
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement
{
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647
}
};
// order of entries in collection is significant: dumb
_Binding = new CustomBinding(elements);
_EndPointAddress = new EndpointAddress(_ServiceUri);
}
#region IServiceProxy Members
/// <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
{
try
{
var proxy = new JobPlanServiceClient(_Binding, _EndPointAddress);
string action = typeof (T).Name.Replace(CompletedEventargs, String.Empty);
_ProxyType.GetEvent(action + Completed).AddEventHandler(proxy, callback);
_ProxyType.InvokeMember(action + Async, BindingFlags.InvokeMethod, null, proxy, parameters);
}
catch (Exception exp)
{
MessageBox.Show("Unable to use ServiceProxy.CallService to retrieve data: " + exp.Message);
}
}
#endregion
}
The relative path support for calling services in Silverlight 4 definitely simplifies code and is yet another good reason to move from Silverlight 3 to Silverlight 4.
For more information about onsite, online and video training, mentoring and consulting solutions for .NET, SharePoint or Silverlight please visit http://www.thewahlingroup.com.