Manipulating Service References in Silverlight 2

Tuesday, January 13 2009 - , , ,

When developing a Silverlight 2 project that uses WCF service references or ADO.NET Data Services Astoria references, and you want to push the builds to a test server, there are a few changes that need to be made so the references point to the test server and not the dev box.

Shawn Wildermuth has a great post on this topic that explains the problem and also explains that when dealing with WCF service references, the ServiceReference.ClientConfig file can either be simply edited or a better option is to use compilation symbols to make the changes at compile time. For example, Shawn points out that you can create 2 endpoints in code in the ServiceReference.ClientConfig file in the Silverlight project: 1 for dev box and 1 for the test server. Then, in code the service can be instantiated using the following code (from Shawn’s post):

#if DEBUG
  MyTestServiceClient svc = new MyTestServiceClient("TestEndpoint");
#else
  MyTestServiceClient svc = new MyTestServiceClient("RealEndpoint");
#endif

This same technique can be adapted for Astoria by flipping the service reference’s context object’s instantiation as shown below:

#if DEBUG
        protected MyEntities Context
        {
            get
            {
                if (_context == null)
                    _context = new MyEntities(
                        new Uri("MyService.svc", UriKind.Relative));
                return _context;
            }
        }
#else
        protected MyEntities Context
        {
            get
            {
                if (_context == null)
                    _context = new MyEntities(
                        new Uri("http://testbox/foo/MyService.svc", 
                        UriKind. Absolute));
                return _context;
            }
#endif

Now simply choose the compilation options for when building and you are all set! Quite simple and effective.

6 comment(s)

Steve Buchok wrote on Tuesday, January 13 2009

Nice example. However, would it not be a better solution to create your own mechanism for having a config type file that holds this information. That way in each environment you just have different config info and don't need to duplicate code. Just a thought.

I think what Steve is describing is basically the way we have implemented this. It’s the same concept described in this article used to manage Web.Config files.

www.hanselman.com/.../ManagingMultipl

True. But the trick in Silverlight is that the config file is inside the XAP (for WCF). So if you want the SIlverlight app self contained then it must remain contained within the XAP. In that case, you'd just need to use this type of method that Scott H mentions to update the config file for Silverlight using build events, build configurations and batch files.

For Astoria config file settings can be added, too. But by default the URI is not in a config. SImple enough to to add a config setting tho.

Thanks for the comments.

\ In this issue: Boyan Mihaylov, Thiago Felix, Bart Czernicki, Daomon Payne, Nigel Sampson, Jeff Weber

There are better ways to do this. We made our own helper class that takes care of this. I would highly recommend something like the following.

svcVehicleValuations.SLVehicleValuationsClient _Client = SLLocation.GetClient<svcVehicleValuations.SLVehicleValuationsClient>("SLVehicleValuations.svc");

This is the jist of it

public static BasicHttpBinding WCFHttpBinding(long? MaxReceivedMessageSize)

{

BasicHttpBinding ret = new BasicHttpBinding();

//had to wrap this in a try catch because Expression Blend would throw a fit over this at design time if

//referencing GetClient in the constructor (not in a method basically)

try

{

if (System.Windows.Browser.HtmlPage.Document.DocumentUri.Scheme.ToLower() == "https")

{

ret.Security.Mode = BasicHttpSecurityMode.Transport;

}

else

{

ret.Security.Mode = BasicHttpSecurityMode.None;

}

ret.MaxReceivedMessageSize = MaxReceivedMessageSize ?? 65536;//65536 is default

}

catch { }

return ret;

}

private static System.ServiceModel.EndpointAddress GetEndpoint(string serviceName)

{

Uri uri = new Uri(SLLocation.ServiceURL + serviceName);

return new System.ServiceModel.EndpointAddress(uri.ToString());

}

public static T GetClient<T>(string serviceName) where T : class

{

return GetClient<T>(serviceName, null);

}

public static T GetClient<T>(string serviceName, long? MaxReceivedMessageSize) where T : class

{

object[] p = new object[] { WCFHttpBinding(MaxReceivedMessageSize), GetEndpoint(serviceName) };

return System.Activator.CreateInstance(typeof(T), p) as T;

}

Pretty good post. I just stumbled upon your blog and wanted to say

that I have really enjoyed reading your blog posts. Any way I’ll be subscribing to your feed and I hope you post again soon.