Controlling Service References in Silverlight 2


Silverlight Logo

I get this question a lot when I teach the Silverlight Tour and when I do talks at conferences and user groups:

Is there a right way to handle web service endpoints in Silverlight 2 when dealing with development and deployment servers?

The problem is that in Silverlight 2 you don't have a web.config file that can be modified as your application moves from development, to testing, to staging to deployment...or do you?

When you make a Service Reference in a Silverlight 2 project, a new project item called the ServiceReference.ClientConfig is added to your Silverlight 2 project. This file (as long winded the name is) contains the configuration for your endpoints and bindings to your web services. I think the idea around this configuration file was that much like ASP.NET: when you move a project across environments, you can simply edit the configuration file to make the change. This is fine but its not quite as trivial since the ServiceReference.ClientConfig file is embedded in the .xap file. Some people are opening the xap file and changing these bindings but I wanted to mention a different strategy...one that was only enabled in Silverlight 2 RTW (I think).

Let's take a small example of a ServiceReference.ClientConfig file for a project with a single service:

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicEndpoint"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647">
          <security mode="None" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:57929/MyTestService.svc"
                binding="basicHttpBinding"
                bindingConfiguration="BasicEndpoint"
                contract="MyServices.MyTestService"
                name="TestEndpoint" />
    </client>
  </system.serviceModel>
</configuration>

When you create a service reference, it creates this file and creates the configuration for the Endpoint as well as the Binding. In earlier versions of Silverlight 2, I would tell people to just use code to create their endpoints manually if they didn't want to edit this file during deployment. But we now have a different option. If you notice there is a name attribute on the endpoint. We can use this name in a new overload for the generated WebClient class for our service. For example:

MyTestServiceClient svc = new MyTestServiceClient("TestEndpoint");

svc.GetNamesCompleted += (snd, args) =>
  {
    if (args.Error != null)
    {
      throw args.Error;
    }
    HtmlPage.Window.Alert(args.Result);
  };

svc.GetNamesAsync("Shawn");

When we create our client proxy class (MyTestServiceClient in this case), we can specify the name of the Endpoint. This means we can pick the Endpoint at runtime instead of having to manually configure your Endpoint and Bindings in code.  For example, in the simple case we could change our ServiceReference.ClientConfig to support a new Endpoint:

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicEndpoint"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647">
          <security mode="None" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:57929/MyTestService.svc"
                binding="basicHttpBinding"
                bindingConfiguration="BasicEndpoint"
                contract="MyServices.MyTestService"
                name="TestEndpoint" />
      <endpoint address="http://WcsServer/MyTestService.svc"
                binding="basicHttpBinding"
                bindingConfiguration="BasicEndpoint"
                contract="MyServices.MyTestService"
                name="RealEndpoint" />
    </client>
  </system.serviceModel>
</configuration>

(NOTE: We could have created a separate binding as well as specified the binding in the bindingConfiguration attribute of the endpoint.)

Now that we have two endpoints, one for testing and one for deployment we can simply create code that knows the difference.  For example:

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

In this example we simply use the compilation symbol (DEBUG) to determine which endpoint to use.

This technique is just another way of doing this. If your build environment is dynamic, opening the .xap file and editing the ServiceReference.ClientConfig may still be the best option, but this gives you choices




Application Name WilderBlog Environment Name Production
Application Ver 1.1.0.0 Runtime Framework .NETCoreApp,Version=v1.1
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.6.25211.01
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86