How to programmatically modify WCF app.config endpoint address setting?

101,454

Solution 1

I think what you want is to swap out at runtime a version of your config file, if so create a copy of your config file (also give it the relevant extension like .Debug or .Release) that has the correct addresses (which gives you a debug version and a runtime version ) and create a postbuild step that copies the correct file depending on the build type.

Here is an example of a postbuild event that I've used in the past which overrides the output file with the correct version (debug/runtime)

copy "$(ProjectDir)ServiceReferences.ClientConfig.$(ConfigurationName)" "$(ProjectDir)ServiceReferences.ClientConfig" /Y

where : $(ProjectDir) is the project directory where the config files are located $(ConfigurationName) is the active configuration build type

EDIT: Please see Marc's answer for a detailed explanation on how to do this programmatically.

Solution 2

Is this on the client side of things??

If so, you need to create an instance of WsHttpBinding, and an EndpointAddress, and then pass those two to the proxy client constructor that takes these two as parameters.

// using System.ServiceModel;
WSHttpBinding binding = new WSHttpBinding();
EndpointAddress endpoint = new EndpointAddress(new Uri("http://localhost:9000/MyService"));

MyServiceClient client = new MyServiceClient(binding, endpoint);

If it's on the server side of things, you'll need to programmatically create your own instance of ServiceHost, and add the appropriate service endpoints to it.

ServiceHost svcHost = new ServiceHost(typeof(MyService), null);

svcHost.AddServiceEndpoint(typeof(IMyService), 
                           new WSHttpBinding(), 
                           "http://localhost:9000/MyService");

Of course you can have multiple of those service endpoints added to your service host. Once you're done, you need to open the service host by calling the .Open() method.

If you want to be able to dynamically - at runtime - pick which configuration to use, you could define multiple configurations, each with a unique name, and then call the appropriate constructor (for your service host, or your proxy client) with the configuration name you wish to use.

E.g. you could easily have:

<endpoint address="http://mydomain/MyService.svc"
        binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IASRService"
        contract="ASRService.IASRService" 
        name="WSHttpBinding_IASRService">
        <identity>
            <dns value="localhost" />
        </identity>
</endpoint>

<endpoint address="https://mydomain/MyService2.svc"
        binding="wsHttpBinding" bindingConfiguration="SecureHttpBinding_IASRService"
        contract="ASRService.IASRService" 
        name="SecureWSHttpBinding_IASRService">
        <identity>
            <dns value="localhost" />
        </identity>
</endpoint>

<endpoint address="net.tcp://mydomain/MyService3.svc"
        binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IASRService"
        contract="ASRService.IASRService" 
        name="NetTcpBinding_IASRService">
        <identity>
            <dns value="localhost" />
        </identity>
</endpoint>

(three different names, different parameters by specifying different bindingConfigurations) and then just pick the right one to instantiate your server (or client proxy).

But in both cases - server and client - you have to pick before actually creating the service host or the proxy client. Once created, these are immutable - you cannot tweak them once they're up and running.

Marc

Solution 3

I use the following code to change the endpoint address in the App.Config file. You may want to modify or remove the namespace before usage.

using System;
using System.Xml;
using System.Configuration;
using System.Reflection;
//...

namespace Glenlough.Generations.SupervisorII
{
    public class ConfigSettings
    {

        private static string NodePath = "//system.serviceModel//client//endpoint";
        private ConfigSettings() { }

        public static string GetEndpointAddress()
        {
            return ConfigSettings.loadConfigDocument().SelectSingleNode(NodePath).Attributes["address"].Value;
        }

        public static void SaveEndpointAddress(string endpointAddress)
        {
            // load config document for current assembly
            XmlDocument doc = loadConfigDocument();

            // retrieve appSettings node
            XmlNode node = doc.SelectSingleNode(NodePath);

            if (node == null)
                throw new InvalidOperationException("Error. Could not find endpoint node in config file.");

            try
            {
                // select the 'add' element that contains the key
                //XmlElement elem = (XmlElement)node.SelectSingleNode(string.Format("//add[@key='{0}']", key));
                node.Attributes["address"].Value = endpointAddress;

                doc.Save(getConfigFilePath());
            }
            catch( Exception e )
            {
                throw e;
            }
        }

        public static XmlDocument loadConfigDocument()
        {
            XmlDocument doc = null;
            try
            {
                doc = new XmlDocument();
                doc.Load(getConfigFilePath());
                return doc;
            }
            catch (System.IO.FileNotFoundException e)
            {
                throw new Exception("No configuration file found.", e);
            }
        }

        private static string getConfigFilePath()
        {
            return Assembly.GetExecutingAssembly().Location + ".config";
        }
    }
}

Solution 4

SomeServiceClient client = new SomeServiceClient();

var endpointAddress = client.Endpoint.Address; //gets the default endpoint address

EndpointAddressBuilder newEndpointAddress = new EndpointAddressBuilder(endpointAddress);
                newEndpointAddress.Uri = new Uri("net.tcp://serverName:8000/SomeServiceName/");
                client = new SomeServiceClient("EndpointConfigurationName", newEndpointAddress.ToEndpointAddress());

I did it like this. The good thing is it still picks up the rest of your endpoint binding settings from the config and just replaces the URI.

Solution 5

this short code worked for me:

Configuration wConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ServiceModelSectionGroup wServiceSection = ServiceModelSectionGroup.GetSectionGroup(wConfig);

ClientSection wClientSection = wServiceSection.Client;
wClientSection.Endpoints[0].Address = <your address>;
wConfig.Save();

Of course you have to create the ServiceClient proxy AFTER the config has changed. You also need to reference the System.Configuration and System.ServiceModel assemblies to make this work.

Cheers

Share:
101,454
alchemical
Author by

alchemical

Thus have I heard: Premature optimization is the root of all evil.

Updated on July 09, 2022

Comments

  • alchemical
    alchemical almost 2 years

    I'd like to programmatically modify my app.config file to set which service file endpoint should be used. What is the best way to do this at runtime? For reference:

    <endpoint address="http://mydomain/MyService.svc"
        binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IASRService"
        contract="ASRService.IASRService" name="WSHttpBinding_IASRService">
        <identity>
            <dns value="localhost" />
        </identity>
    </endpoint>
    
  • jlemley
    jlemley about 14 years
    This a bit of a hack, but I upvoted you anyway as I thought it was a neat workaround.
  • ingh.am
    ingh.am about 14 years
    How can you programatically add the binding configuration and contract to the endpoint?
  • marc_s
    marc_s about 14 years
    See the top of my answer - you need to create a binding and an endpoint address and then create your service endpoint (on the server side) or your client side proxy based on those two items. You cannot "add" a binding to an endpoint - an endpoint consists of a threesome of address (URI), binding and contract
  • Terry
    Terry about 14 years
    a hack or not, this might be very usefull when there's no support later on for users who use the program
  • Sprague
    Sprague almost 14 years
    You can't use a binding name which isn't in the .config. "test" will return an error. This is a problem because channel factory caching can only occur if you specify binding configuration name, and not a binding object =(
  • Nathan Ridley
    Nathan Ridley over 13 years
    This should be the correct answer, it is much more detailed and helpful.
  • defines
    defines over 10 years
    Awesome, thanks for sharing this and saving me the minutes! :D
  • Rick Riensche
    Rick Riensche about 10 years
    You can also further simplify by skipping manual creation of EndpointAddress and just specifying the address directly in the client constructor, e.g., var client = new SomeServiceClient("EndpointConfigurationName", "net.tcp://servername:8000/SomeServiceName/")
  • leo
    leo over 9 years
    VS2010 .NET 4.0, it's ConfigurationSettings, not ConfigSettings
  • Doolali
    Doolali about 9 years
    You can use: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile instead of Assembly.GetExecutingAssembly().Location + ".config";
  • Phu Minh Pham
    Phu Minh Pham over 8 years
    This also worked for me. I you want to modify multiple endpoints, you can create a simple for loop: for (int i = 0; i < wClientSection.Endpoints.Count; i++) { wClientSection.Endpoints[i].Address = new Uri(value); }
  • guru_florida
    guru_florida over 7 years
    This worked awesome! I used this to loop through all endpoints and update the IP address at run-time so I now have a single app-setting for services IP address.
  • Anton Krouglov
    Anton Krouglov about 5 years
    This is a excellent answer. I would just add that it is possible to change/update/amend existing configured bindings in startup code without explicit host creation. Something like this.