WCF set Endpoint and Binding dynamically in code

Can Poyrazoğlu picture Can Poyrazoğlu · Aug 26, 2011 · Viewed 8.4k times · Source

Yes, I've read the other questions on SO, MSDN, and other sites, but I found no answers as clear as I can understand. I need to set my Silverlight application's WCF references relative to the site that it's loaded from, but I can't get it to work. There is no problem with the service itself, it is working. When I move from local to my real server, I get errors in my SL app complaining about not connecting to localhost.

Here is my ServiceReferences.ClientConfig file:

    <configuration>
        <system.serviceModel>
            <bindings>
                <customBinding>
                    <binding name="CustomBinding_AccountManager">
                        <binaryMessageEncoding />
                        <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
                    </binding>
                    <binding name="CustomBinding_FileManager">
                        <binaryMessageEncoding />
                        <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
                    </binding>
                    <binding name="CustomBinding_SiteManager">
                        <binaryMessageEncoding />
                        <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
                    </binding>
                </customBinding>
            </bindings>
            <client>
                <endpoint address="http://localhost:60322/AccountManager.svc"
                    binding="customBinding" bindingConfiguration="CustomBinding_AccountManager"
                    contract="AccountManager.AccountManager" name="CustomBinding_AccountManager" />
                <endpoint address="http://localhost:60322/FileManager.svc" binding="customBinding"
                    bindingConfiguration="CustomBinding_FileManager" contract="FileManager.FileManager"
                    name="CustomBinding_FileManager" />
                <endpoint address="http://localhost:60322/SiteManager.svc" binding="customBinding"
                    bindingConfiguration="CustomBinding_SiteManager" contract="SiteManager.SiteManager"
                    name="CustomBinding_SiteManager" />
            </client>
        </system.serviceModel>
    </configuration>

Yes, I will optimize the buffer/message sizes and I know the possible DoS exploits, forget about it for now, I need them for big file transfers. An approach I tried is while instantiating the clients, I've used this code:

    fileManager = new FileManagerClient(new BasicHttpBinding(), new EndpointAddress("http://" + Settings.Host + "/FileManager.svc"));
    accManager = new AccountManagerClient(new BasicHttpBinding(), new EndpointAddress("http://" + Settings.Host + "/AccountManager.svc"));

where Settings.Host is my own method which returns me the host that the SL app is running from, tested, works. When I uploaded my XAP and tried, it still wanted to go for http://localhost:60322/AccountManager.svc, after further investigation, I've realized that there are still lots of references to localhost, in unseen files:

AccountManager.disco:

<?xml version="1.0" encoding="utf-8"?>
<discovery xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/disco/">
  <contractRef ref="http://localhost:60322/AccountManager.svc?wsdl" docRef="http://localhost:60322/AccountManager.svc" xmlns="http://schemas.xmlsoap.org/disco/scl/" />
</discovery>

Parts of AccountManager.wsdl: ... <wsdl:import namespace="" location="http://localhost:60322/AccountManager.svc?wsdl=wsdl0" />... ...[lots of operation declarations]...

<wsdl:service name="AccountManager">
    <wsdl:port name="CustomBinding_AccountManager" binding="tns:CustomBinding_AccountManager">
      <soap12:address location="http://localhost:60322/AccountManager.svc" />
      <wsa10:EndpointReference>
        <wsa10:Address>http://localhost:60322/AccountManager.svc</wsa10:Address>
      </wsa10:EndpointReference>
    </wsdl:port>
  </wsdl:service>

Part of AccountManager1.xsd:

<xs:import schemaLocation="http://localhost:60322/AccountManager.svc?xsd=xsd2" namespace="http://schemas.datacontract.org/2004/07/Leftouch.Data.Summary" />

Part of configuration.svcinfo:

<endpoint normalizedDigest="&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt;&lt;Data address=&quot;http://localhost:60322/AccountManager.svc&quot; binding=&quot;customBinding&quot; bindingConfiguration=&quot;CustomBinding_AccountManager&quot; contract=&quot;AccountManager.AccountManager&quot; name=&quot;CustomBinding_AccountManager&quot; /&gt;" digest="&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt;&lt;Data address=&quot;http://localhost:60322/AccountManager.svc&quot; binding=&quot;customBinding&quot; bindingConfiguration=&quot;CustomBinding_AccountManager&quot; contract=&quot;AccountManager.AccountManager&quot; name=&quot;CustomBinding_AccountManager&quot; /&gt;" contractName="AccountManager.AccountManager" name="CustomBinding_AccountManager" />

Part of Reference.svcmap:

</ClientOptions>
  <MetadataSources>
    <MetadataSource Address="http://localhost:60322/AccountManager.svc" Protocol="http" SourceId="1" />
  </MetadataSources>
  <Metadata>
    <MetadataFile FileName="AccountManager2.xsd" MetadataType="Schema" ID="e473b2d5-7af3-4390-87c3-a4fc3f54fb96" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?xsd=xsd2" />
    <MetadataFile FileName="AccountManager1.xsd" MetadataType="Schema" ID="fd3a1ae0-b38b-4586-8622-5b0ee07e39fb" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?xsd=xsd0" />
    <MetadataFile FileName="AccountManager.xsd" MetadataType="Schema" ID="6a49ee64-6eac-40e2-bcff-26418435e777" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?xsd=xsd1" />
    <MetadataFile FileName="AccountManager.disco" MetadataType="Disco" ID="9ec9a8cc-0cf0-4264-a526-b5a6c08f7d36" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?disco" />
    <MetadataFile FileName="AccountManager1.wsdl" MetadataType="Wsdl" ID="54a5b2c0-9d0e-4043-a7e4-d27ae6674bfc" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?wsdl=wsdl0" />
    <MetadataFile FileName="AccountManager.wsdl" MetadataType="Wsdl" ID="f8923013-3a6c-412b-b7da-bee5a5a7bb64" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?wsdl" />

..and ALL these again for other 2 services too.

I am not a master of web services/bindings/endpoints/operation contracts or any related stuff. I just want to make my totally already-nice-working (when URI is hardcoded) system work for relative URIs, that's all I need. There must be a simple solution. Can someone explain what exactly those file types and declarations resemble, which are important and which are optional, and how can I create dynamic service references in the cleanest form. Please, with explanations. I've already seen lots of posts and articles about dynamic service bindings and references, but honestly, everything gets messed up and I end up not understanding anything from it. Any constructive criticism and solutions are welcome.

Answer

Tim picture Tim · Aug 27, 2011

Is the config file for your service (not the client) using endpoints with fully qualified addresses (like your client config)? If so, the reason you're seeing all those localhost references is because when you add the service reference, it's going to carry through that address in the generated files.

Update the endpoint in your service config file to either use the fully qualified address on that machine (i.e., http://somname.com/service.svc), or set the baseAddresses in the config file to the machine name.

Additionally, if you're using WCF 4.0 (VS 2010/.NET 4.0), you can have WCF create a default endpoint for your service(s) by ommitting the endpoints from the config file altogether (as I understand it - we're just moving to 4.0 at work now, so I haven't played with the new features).

EDITED TO ADD

New approach, same basic idea (that the URI is being imported from somewhere). Based on your comments below, it sounds like your service's config is set up fine, with no hard-coded URIs pointing to localhost.

When you move your application to the target server(s), are you updating your reference to your service (via the Add Service Reference) as well, or simply moving the files that generates from your local box to the target server?

If you are, I'm wondering if that might be the source of your problem. I would think that specifying the service address when you create the client should override anything in the WSDL-related files, but maybe its not.

Something to try:

Delete the <client> section from your Web.config. Then, when you create the client, do so like this:

fileManager = new FileManagerClient(new BasicHttpBinding("CustomBinding_FileManager"), new EndpointAddress("http://" + Settings.Host + "/FileManager.svc"));

Make sure you pass in the name of the binding configuration section in the BasicHttpBinding constructor, otherwise you'll get the binding with the default values, not the larger values you specified.

The idea here is to eliminate any chance that the client config file settings are overriding what you're passing in on the FileManagerClient creation.

I'd consider it less than ideal to have to update the service reference for every deployment to each individual server - what you're trying to accomplish makes sense. I do something similar in an n-tier app I've written - the only difference is that I don't use service references, I generate the proxy via SvcUtil and then generate channels via ChannelFactory<T>, which is another route you might want to look at.

If none of this help, please feel free to e-mail me (my e-mail address is in my profile) - it might be easier to figure this out via e-mail exchange and then post the final solution.