50

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>
4

11 回答 11

97

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

于 2009-06-08T18:49:47.500 回答
26

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";
        }
    }
}
于 2010-01-14T22:37:59.293 回答
12
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.

于 2012-02-20T11:50:15.400 回答
6

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

于 2014-04-08T09:19:32.613 回答
3

This is the shortest code that you can use to update the app config file even if don't have a config section defined:

void UpdateAppConfig(string param)
{
   var doc = new XmlDocument();
   doc.Load("YourExeName.exe.config");
   XmlNodeList endpoints = doc.GetElementsByTagName("endpoint");
   foreach (XmlNode item in endpoints)
   {
       var adressAttribute = item.Attributes["address"];
       if (!ReferenceEquals(null, adressAttribute))
       {
           adressAttribute.Value = string.Format("http://mydomain/{0}", param);
       }
   }
   doc.Save("YourExeName.exe.config");
}
于 2012-12-20T08:38:48.283 回答
2

I have modified and extended Malcolm Swaine's code to modify a specific node by it's name attribute, and to also modify an external config file. Hope it helps.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Reflection;

namespace LobbyGuard.UI.Registration
{
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
        XmlNodeList nodes = doc.SelectNodes(NodePath);

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

            //If this isnt the node I want to change, look at the next one
            //Change this string to the name attribute of the node you want to change
            if (node.Attributes["name"].Value != "DataLocal_Endpoint1")
            {
                continue;
            }

            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());

                break;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
    }

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

        // retrieve appSettings node
        XmlNodeList nodes = doc.SelectNodes(NodePath);

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

            //If this isnt the node I want to change, look at the next one
            if (node.Attributes["name"].Value != endpointName)
            {
                continue;
            }

            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(ConfigPath);

                break;
            }
            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);
        }
    }

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

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

}

于 2012-09-10T19:33:52.530 回答
2
MyServiceClient client = new MyServiceClient(binding, endpoint);
client.Endpoint.Address = new EndpointAddress("net.tcp://localhost/webSrvHost/service.svc");
client.Endpoint.Binding = new NetTcpBinding()
            {
                Name = "yourTcpBindConfig",
                ReaderQuotas = XmlDictionaryReaderQuotas.Max,
                ListenBacklog = 40 }

It's very easy to modify the uri in config or binding info in config. Is this what you want?

于 2013-12-02T10:51:01.783 回答
2

For what it's worth, I needed to update the port and scheme for SSL for my RESTFul service. This is what I did. Apologies that it is a bit more that the original question, but hopefully useful to someone.

// Don't forget to add references to System.ServiceModel and System.ServiceModel.Web

using System.ServiceModel;
using System.ServiceModel.Configuration;

var port = 1234;
var isSsl = true;
var scheme = isSsl ? "https" : "http";

var currAssembly = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
Configuration config = ConfigurationManager.OpenExeConfiguration(currAssembly);

ServiceModelSectionGroup serviceModel = ServiceModelSectionGroup.GetSectionGroup(config);

// Get the first endpoint in services.  This is my RESTful service.
var endp = serviceModel.Services.Services[0].Endpoints[0];

// Assign new values for endpoint
UriBuilder b = new UriBuilder(endp.Address);
b.Port = port;
b.Scheme = scheme;
endp.Address = b.Uri;

// Adjust design time baseaddress endpoint
var baseAddress = serviceModel.Services.Services[0].Host.BaseAddresses[0].BaseAddress;
b = new UriBuilder(baseAddress);
b.Port = port;
b.Scheme = scheme;
serviceModel.Services.Services[0].Host.BaseAddresses[0].BaseAddress = b.Uri.ToString();

// Setup the Transport security
BindingsSection bindings = serviceModel.Bindings;
WebHttpBindingCollectionElement x =(WebHttpBindingCollectionElement)bindings["webHttpBinding"];
WebHttpBindingElement y = (WebHttpBindingElement)x.ConfiguredBindings[0];
var e = y.Security;

e.Mode = isSsl ? WebHttpSecurityMode.Transport : WebHttpSecurityMode.None;
e.Transport.ClientCredentialType = HttpClientCredentialType.None;

// Save changes
config.Save();
于 2015-08-03T22:23:03.533 回答
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.

于 2009-06-08T18:59:22.737 回答
1

You can do it like this:

  • Keep your settings in a separate xml file and read through it when you create a proxy for your service.

For example , i want to modify my service endpoint address at runtime so i have the following ServiceEndpoint.xml file.

     <?xml version="1.0" encoding="utf-8" ?>
     <Services>
        <Service name="FileTransferService">
           <Endpoints>
              <Endpoint name="ep1" address="http://localhost:8080/FileTransferService.svc" />
           </Endpoints>
        </Service>
     </Services>
  • For reading your xml :

     var doc = new XmlDocument();
     doc.Load(FileTransferConstants.Constants.SERVICE_ENDPOINTS_XMLPATH);
     XmlNodeList endPoints = doc.SelectNodes("/Services/Service/Endpoints");  
     foreach (XmlNode endPoint in endPoints)
     {
        foreach (XmlNode child in endPoint)
        {
            if (child.Attributes["name"].Value.Equals("ep1"))
            {
                var adressAttribute = child.Attributes["address"];
                if (!ReferenceEquals(null, adressAttribute))
                {
                    address = adressAttribute.Value;
                }
           }
       }
    }  
    
  • Then get your web.config file of your client at runtime and assign the service endpoint address as:

        Configuration wConfig = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap { ExeConfigFilename = @"C:\FileTransferWebsite\web.config" }, ConfigurationUserLevel.None);
        ServiceModelSectionGroup wServiceSection = ServiceModelSectionGroup.GetSectionGroup(wConfig);
    
        ClientSection wClientSection = wServiceSection.Client;
        wClientSection.Endpoints[0].Address = new Uri(address);
        wConfig.Save();
    
于 2015-01-27T07:37:31.183 回答
0

see if you are placing the client section in the correct web.config file. SharePoint has around 6 to 7 config files. http://msdn.microsoft.com/en-us/library/office/ms460914(v=office.14).aspx (http://msdn.microsoft.com/en-us/library/office/ms460914%28v=office.14%29.aspx)

Post this you can simply try

ServiceClient client = new ServiceClient("ServiceSOAP");

于 2014-12-23T13:37:45.843 回答