Reputation: 2783
When building an app, it is often deployed in different environments (test, dev, prod), and therefore the endpoint addresses are changing. As the ServiceReferences.ClientConfig is built as a part of Silverlight's .xap file, its hard to change the endpoints after building the solution, as often is done with web.config.
I've searched quite a bit for it, but I cant figure out what is best practice here, so my question is:
What is best practice when it comes to dynamic wcf endpoint address configuration in silverlight?
To clarify, depending on which server the app is on (test,dev, prod) the endpoints change:
<endpoint
name="MyService"
address="http://testserv/MyService.svc"
binding="basicHttpBinding"
bindingConfiguration="MybasicHttpBinding"
contract="MyApp.MyService"
/>
<endpoint
name="MyService"
address="http://prodserv/MyService.svc"
binding="basicHttpBinding"
bindingConfiguration="MybasicHttpBinding"
contract="MyApp.MyService"
/>
In some way, i need the silverlight client to know which one to use, depending on which server its on / which build is compiled.
Upvotes: 16
Views: 15145
Reputation: 2783
After reading sLedgem's post, and some googling, I found the perfect solution to make ServiceReferences act like web.config.
First off: Create the different files manually;
ServiceReferences.Debug.ClientConfig
ServiceReferences.Release.ClientConfig
You can add your own as well if you have more than the two default configurations in Visual Studio.
Second: Add the file dependency in the Project.csproj file (Open the project file in a text editor):
<ItemGroup>
<None Include="Properties\AppManifest.xml" />
<Content Include="ServiceReferences.ClientConfig">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="ServiceReferences.Debug.ClientConfig">
<DependentUpon>ServiceReferences.ClientConfig</DependentUpon>
</Content >
<Content Include="ServiceReferences.Release.ClientConfig">
<DependentUpon>ServiceReferences.ClientConfig</DependentUpon>
</Content >
</ItemGroup>
Now, when you reload the project, you will see that ServiceReferences.Release.ClientConfig is expandable in the Solution Explorer, and when you expand it, you will see the Release and Debug file.
Third: Add the Transformation rules to the Project file just before the closing </Project>
(again, open it in a text editor)
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. -->
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('ServiceReferences.$(Configuration).ClientConfig')">
<!-- Generate transformed ServiceReferences config in the intermediate directory -->
<TransformXml Source="ServiceReferences.ClientConfig" Destination="$(IntermediateOutputPath)$(TargetFileName).ClientConfig" Transform="ServiceReferences.$(Configuration).ClientConfig" />
<!-- Force build process to use the transformed configuration file from now on. -->
<ItemGroup>
<ServiceReferencesConfigWithTargetPath Remove="ServiceReferences.ClientConfig" />
<ServiceReferencesConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).ClientConfig">
<TargetPath>$(TargetFileName).ClientConfig</TargetPath>
</ServiceReferencesConfigWithTargetPath>
</ItemGroup>
</Target>
What it does is to look in the corresponding servicereferences file, depending on your configuration, and copy / replace code using the same TransformXML library that web.config uses.
Example:
in my ServiceReferences.ClientConfig i have the following code:
<endpoint name="ICatalogueService"
address="address"
binding="basicHttpBinding"
bindingConfiguration="My_basicHttpBinding"
contract="Services.ServiceInterfaces.ICatalogueService"/>
ServiceReferences.Release.ClientConfig:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.serviceModel>
<client>
<endpoint
name="ICatalogueService"
address="http://server/Services/CatalogueService.svc"
binding="basicHttpBinding"
bindingConfiguration="My_basicHttpBinding"
contract="Services.ServiceInterfaces.ICatalogueService"
xdt:Transform="Replace" xdt:Locator="Match(name)" />
</client>
<extensions />
</system.serviceModel>
</configuration>
As you can see, the endpoint will be replaced, and the match is done on the name attribute.
If you have any questions, let me know :)
Upvotes: 27
Reputation: 136
Great solution to the problem
I could not get the <ItemGroup></ItemGroup>
section to work effectively in my solution.
I removed it and added the following script to my Prebuild event in the project:
del $(ProjectDir)ServiceReferences.ClientConfig;
copy $(ProjectDir)ServiceReferences.$(ConfigurationName).ClientConfig $(ProjectDir)ServiceReferences.ClientConfig;
Upvotes: 6
Reputation: 328
randoms' response is spot on except for ONE little thing. Don't mark the .Debug.ClientConfig and .Release.ClientConfig as "Content". Mark them as "None". That way your .Debug.ClientConfig and .Release.ClientConfig aren't put into your .xap file. Here's what's in my Silverilght project file (and it works great):
<Content Include="ServiceReferences.ClientConfig">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="ServiceReferences.DEV.ClientConfig">
<DependentUpon>ServiceReferences.ClientConfig</DependentUpon>
</None>
<None Include="ServiceReferences.TEST.ClientConfig">
<DependentUpon>ServiceReferences.ClientConfig</DependentUpon>
</None>
<None Include="ServiceReferences.PROD.ClientConfig">
<DependentUpon>ServiceReferences.ClientConfig</DependentUpon>
</None>
Upvotes: 1
Reputation: 1554
You can do it during runtime by using the constructor of the WCF client in SL that takes endpoint configuration name and the address. The endpoint configuration name is just "MyService" in your example. The address argument you provide will override the one included in ClientConfig.
One of the ways to calculate the address of your service during runtime from SL is (I don't guarantee it will work in every environment configuration):
Extra Info:
This may look complicated when you have many services, but it all can be nicely refactored and with a help of Unity made pretty easy to use for any service. For example, I use a helper function which registers a service client with and it's call looks like this: ServicesHelper.RegisterService<MyServiceContractClient, IMyServiceContract>( "MyService" ); When I need to create an instance of the service client I just resolve MyServiceContractClient type with Unity which uses an injection constructor to create a new instance of my service already properly configured. It can also handle HTTPS situation. Let me know if you need more information on any of that.
Upvotes: 3
Reputation: 501
Have a look here:
then here
http://www.funkymule.com/post/2010/03/08/XML-Transform-on-Silverlight-ClientConfig-Files.aspx
It uses the same principle behind the web.config transformations (ie, web.config is changed depending on what configuration you are compiling (ie release, debug) so that the serviceref.config is changed according to your whim upon compile time. works a charm
Upvotes: 1