Handling WCF Service Paths in Silverlight 4 – Relative Path Support

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));
        }
    }

    //Used to get URL for file uploads and base WCF service location 
    public static string GetServiceUrlBase()
    {
        if (!IsDesignTime)
        {
            string url = Application.Current.Host.Source.OriginalString;
            return url.Substring(0, url.IndexOf("/ClientBin", StringComparison.InvariantCultureIgnoreCase));
        }
        return null;
    }
}

After taking advantage of relative paths in Silverlight 4 (which you won’t see quite yet) we changed the code to the following:

public class ServiceProxyBase
{
    public ServiceProxyBase(string serviceUrl)
    {
        ServiceUrl = serviceUrl;
    }

    public string ServiceUrl { get; set; }

    public static bool IsDesignTime
    {
        get
        {
            return (Application.Current == null) || (Application.Current.GetType() == typeof (Application));
        }
    }

    //Used only for file uploads and other activities
    public static string GetUrlBase()
    {
        if (!IsDesignTime)
        {
            string url = Application.Current.Host.Source.OriginalString;
            return url.Substring(0, url.IndexOf("/ClientBin", StringComparison.InvariantCultureIgnoreCase));
        }
        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 to the service:

 

public class ServiceProxy : ServiceProxyBase, IServiceProxy
{
    private const string CompletedEventargs = "CompletedEventArgs";
    private const string Completed = "Completed";
    private const string Async = "Async";
    private const string ServiceUrlPath = "../Services/JobPlanService.svc";

    private readonly CustomBinding _Binding;
    private readonly EndpointAddress _EndPointAddress;
    private readonly Uri _ServiceUri;
    private readonly Type _ProxyType = typeof(JobPlanServiceClient);

    public ServiceProxy() : base(ServiceUrlPath)
    {
        _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);

        // insert your favorite address resolution algorithm here
        _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.

 

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

No Comments