views:

9298

answers:

4

I'd like to programatically 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>
+12  A: 

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.

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

marc_s
How can you programatically add the binding configuration and contract to the endpoint?
ing0
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
marc_s
This should be the correct answer, it is much more detailed and helpful.
Nathan Ridley
+2  A: 

I think what you want is to swop out at runtime a version of your config file, if so create a copy of your config file (also give it the relevant extention 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.

heres an example of a postbuild event that ive 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

otherwise you could do this in code which could be a bit hairy but heres an example;

//Programmatically you can change this address also.

EndpointAddress ar = new EndpointAddress("http://localhost:8000/servicemodelsamples/service");

CalculatorClient client = new CalculatorClient("test", ar);
almog.ori
I ended up doing it programatically, thanks.
alchemical
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 =(
Eugarps
+4  A: 

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";
        }
    }
}
Malcolm Swaine
This a bit of a hack, but I upvoted you anyway as I thought it was a neat workaround.
Pretzel
a hack or not, this might be very usefull when there's no support later on for users who use the program
djerry
A: 

Hi misters,

how can I use ConfigSettings class if my app.config-web.config is like this:

<system.serviceModel>
    <bindings configSource="Config\system.serviceModel.bindings.config" />
    <client configSource="Config\system.serviceModel.client.config" />
</system.serviceModel>

Thanks in advanced, greetings

Alhambra Eidos
Your question is not an answer to the original question. You should delete this post and open a new question.
Pretzel
I agree, open a new post. However, whether you include your sections from different files or not does not make any difference when you are referencing the config sections in code. The code should look exactly the same.
Erik A. Brandstadmoen