Manish Dalal's blog

Exploring .net!

Silverlight ServiceReferences.ClientConfig Alternatives

Silverlight uses the ServiceReferences.ClientConfig file to store and lookup WCF related configuration. Since the ServiceReferences.ClientConfig file is packaged and deployed along with the application in a XAP file, you have to change it for QA, Production, and other deployment scenarios. One option to automate the process is to use the ConfigSwitcher utility that switches proper configuration file based on the selected solution configuration, to perform compile time static linking.

As long as you are not doing any cross domain service access, a simpler alternative is to completely bypass the ServiceReferences.ClientConfig and to do dynamic or run time configuration. It is really easy with the help of SilverlightHost class that provides details about the Silverlight-based application's instantiation settings, and exposes some of the HTML DOM values for the hosted Silverlight plug-in instance. In particular, SilverlightHost.Source property gets the URI of the XAP package and can be used to provide Uri for the service EndpointAddress.

Dynamic Configuration

In a dynamic configuration, we make use of the SilverlightHost properties to dynamically configure and return a proper service client. Lets first start with the traditional ServiceReferences.ClientConfig approach and we will modify it to implement the runtime configuration.  Create a new SilverlightApplication and the corresponding Web Application to host and test the SilverlightApplication. Add a new class, Person.cs to the Silverlight Web Application as shown:

[DataContract]
public class Person {
 
    private string _firstName;
    [DataMember]
    public string FirstName {
        get { return _firstName; }
        set { _firstName = value; }
    }
 
    private string _lastName;
    [DataMember]
    public string LastName {
        get { return _lastName; }
        set { _lastName = value; }
    }
 
    private int _age;
    [DataMember]
    public int Age {
        get { return _age; }
        set { _age = value; }
    }    
}

Next add a new Silverlight enabled WCF service, PeopleService:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class PeopleService {
    [OperationContract]
    public List<Person> GetData() {
        List<Person> personList = new List<Person>();
        for (int i = 0; i < 10; i++) {
            personList.Add(new Person() {
                FirstName = string.Format("First Name {0}", i),
                LastName = string.Format("Last Name {0}", i),
                Age = i
            });
        }
        //
        return personList;
    }
}

Add a service reference to the Silverlight client and call it PeopleServiceReference. (use Discover button to find PeopleService). Also add System.Windows.Control.Data reference to the SilverlightApplication project.

 

 

 

Add following xaml to page.xaml:

<UserControl x:Class="SilverlightApplication.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    >
    <Grid x:Name="LayoutRoot" Background="White">
        <data:DataGrid x:Name="peopleDataGrid" AutoGenerateColumns="True" Margin="10"/>
    </Grid>
</UserControl>

Add following code to page.xaml.cs code behind:

public partial class Page : UserControl {
    public Page() {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(Page_Loaded);
    }
 
    void Page_Loaded(object sender, RoutedEventArgs e) {
        GetData();
    }
 
    private void GetData() {
        //
        PeopleServiceClient proxy = new PeopleServiceClient();
        proxy.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(proxy_GetDataCompleted);
        this.Cursor = Cursors.Wait;
        proxy.GetDataAsync();
    }
 
    void proxy_GetDataCompleted(object sender, GetDataCompletedEventArgs e) {
        this.Cursor = Cursors.Arrow;
        if (null != e.Error) {
            MessageBox.Show(e.Error.Message, "Error Getting Data", MessageBoxButton.OK);
            return;
        }
        //
        peopleDataGrid.ItemsSource = e.Result;
    }
 
}

Build and run the application and make sure it is working properly. Note that we are making use of the ServiceReferences.ClientConfig file to configure the PeopleClient. Now just exclude or rename the ServiceReferences.ClientConfig file from the project. If you now build and run the application, you should get the following error:

image

Add a new file called ServiceUtil.cs as shown; to the Silverlight client application

public class ServiceUtil {
    public static PeopleServiceClient GetPeopleServiceClient() {
        BasicHttpBinding binding = new BasicHttpBinding(
            Application.Current.Host.Source.Scheme.Equals("https", StringComparison.InvariantCultureIgnoreCase) 
            ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None);
        binding.MaxReceivedMessageSize = int.MaxValue;
        binding.MaxBufferSize = int.MaxValue;
        return new PeopleServiceClient(binding, new EndpointAddress(
            new Uri(Application.Current.Host.Source, "../PeopleService.svc")));
    }
}

Here we are using Host.Source.Scheme to determine the security mode(https) and Host.Source to setup the proper EndpointAddress Uri. Application.Current.Host.Source provides a Uri, that points to the location of the XAP file, which normally is ClientBin folder under  the web site root. We use that as the base Uri and provide the address of PeopleSerivce as a relative Uri to construct the service endpoint address.

Next modify GetData method in page.xaml.cs to use above method

private void GetData() {
    //
    //PeopleServiceClient proxy = new PeopleServiceClient();
    PeopleServiceClient proxy = ServiceUtil.GetPeopleServiceClient();
    proxy.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(proxy_GetDataCompleted);
    this.Cursor = Cursors.Wait;
    proxy.GetDataAsync();
}

Build and run the application and verify that it is working properly. Because we are determining the host Uri and scheme at the runtime, this code can be deployed to various target sites without any modification.

Dynamic Switch

Another alternative is to combine the static ServiceReferences.ClientConfig file with a dynamic lookup. Modify ServiceReferences.ClientConfig  to include multiple named entries, each for the desired target site. Then pass in the proper site information as part of the init parameters. Now when creating the service client, just append proper site information to the service name.

PeopleServiceClient proxy = new PeopleServiceClient("BasicHttpBinding_PeopleService_"+ ((App)Application.Current).InitParams["SiteName"]);

 
ServiceReferences.ClientConfig file:
 
<client>
    <endpoint address="http://localhost:1896/PeopleService.svc" binding="basicHttpBinding"
        bindingConfiguration="BasicHttpBinding_PeopleService" contract="PeopleServiceReference.PeopleService"
        name="BasicHttpBinding_PeopleService_DEV" />
  <endpoint address="http://somesite.com/QA/PeopleService.svc" binding="basicHttpBinding"
      bindingConfiguration="BasicHttpBinding_PeopleService" contract="PeopleServiceReference.PeopleService"
      name="BasicHttpBinding_PeopleService_QA" />
  <endpoint address="http://somesite.com/Test/PeopleService.svc" binding="basicHttpBinding"
      bindingConfiguration="BasicHttpBinding_PeopleService" contract="PeopleServiceReference.PeopleService"
      name="BasicHttpBinding_PeopleService_Test" />
</client>

Approach Comparison

Following is the comparison of various approaches:

Approach Pro Con
ConfigSwitcher - Static compile time linking Works in all cases Requires multiple ServiceReferences.ClientConfig files, each per target site
Dynamic/Runtime configuration Same package can be deployed to any site No Cross domain support
Dynamic Switch - Static compile time linking with Dynamic runtime selection Works in all cases Hosting in ASPX only

Source Code: ServiceUtil.zip (173 KB)

 

 

 

 

 

 

 

 

 

Technorati Tags:

Comments

Ozange said:

Hi,

Interesting another way is to download a file (xml format like serviceclient.config or web.config/appsettings) at Silverlight app. loading, Fill a personal ConfigurationManager static dictionnary and use it to configure your endpoint in code - behind. So you can change your xml file config and every new loading of your app will contain the new configuration, without xap recompiling / repackaging.

Ozanges.

# February 24, 2009 4:12 AM

Javier said:

Thanks for the example.

# March 18, 2009 1:01 PM

Girish Raja said:

Thanks for the article. Found it very useful when working on a Dynamics CRM add-in using Silverlight.

# December 17, 2009 4:05 PM