Como modificar programaticamente a configuração do endereço do ponto de extremidade do WCF app.config?

Gostaria de modificar programaticamente meu arquivo app.config para definir qual ponto de extremidade do arquivo de serviço deve ser usado. Qual é a melhor maneira de fazer isso em tempo de execução? Para referência:

     

Eu acho que o que você quer é trocar em tempo de execução uma versão do seu arquivo de configuração, então crie uma cópia do seu arquivo de configuração (também dê a extensão relevante como .Debug ou .Release) que tenha os endereços corretos uma versão de debugging e uma versão de tempo de execução) e crie uma etapa de pós-construção que copie o arquivo correto, dependendo do tipo de construção.

Aqui está um exemplo de um evento postbuild que eu usei no passado, que substitui o arquivo de saída com a versão correta (debug / runtime)

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

onde: $ (ProjectDir) é o diretório do projeto no qual os arquivos de configuração estão localizados $ (ConfigurationName) é o tipo de construção da configuração ativa

EDIT: Por favor, veja a resposta de Marc para uma explicação detalhada sobre como fazer isso de forma programática.

Isso está do lado do cliente?

Em caso afirmativo, você precisa criar uma instância de WsHttpBinding e um EndpointAddress e, em seguida, passar esses dois para o construtor de cliente proxy que leva esses dois como parâmetros.

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

Se estiver no lado do servidor, você precisará criar programaticamente sua própria instância de ServiceHost e adicionar os pontos de extremidade de serviço apropriados a ela.

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

É claro que você pode ter vários desses terminais de serviço adicionados ao seu host de serviço. Quando terminar, você precisará abrir o host de serviço chamando o método .Open ().

Se você quiser poder dinamicamente – em tempo de execução – escolher qual configuração usar, poderá definir várias configurações, cada uma com um nome exclusivo e, em seguida, chamar o construtor apropriado (para seu host de serviço ou seu cliente proxy) com a configuração nome que você deseja usar.

Por exemplo, você poderia facilmente ter:

                

(três nomes diferentes, parâmetros diferentes, especificando bindingConfigurations diferentes) e, em seguida, basta escolher o caminho certo para instanciar o seu servidor (ou proxy do cliente).

Mas em ambos os casos – servidor e cliente – você precisa escolher antes de criar realmente o host de serviço ou o cliente proxy. Uma vez criadas, elas são imutáveis – você não pode ajustá-las quando estiverem funcionando.

Marc

Eu uso o seguinte código para alterar o endereço do ponto de extremidade no arquivo App.Config. Você pode querer modificar ou remover o namespace antes do uso.

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

Eu fiz assim. O bom é que ainda pega o restante das configurações de binding do terminal da configuração e apenas substitui o URI .

esse código curto funcionou para mim:

 Configuration wConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ServiceModelSectionGroup wServiceSection = ServiceModelSectionGroup.GetSectionGroup(wConfig); ClientSection wClientSection = wServiceSection.Client; wClientSection.Endpoints[0].Address = ; wConfig.Save(); 

Claro que você tem que criar o proxy ServiceClient depois que a configuração foi alterada. Você também precisa referenciar os conjuntos System.Configuration e System.ServiceModel para fazer isso funcionar.

Felicidades

Eu modifiquei e estendi o código de Malcolm Swaine para modificar um nó específico pelo seu atributo name e também para modificar um arquivo de configuração externo. Espero que ajude.

 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"; } } 

}

Este é o código mais curto que você pode usar para atualizar o arquivo de configuração do aplicativo, mesmo que não tenha uma seção de configuração definida:

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

É muito fácil modificar o uri na configuração ou vincular informações na configuração. É isso que voce quer?

Você pode fazer assim:

  • Mantenha suas configurações em um arquivo xml separado e leia-o ao criar um proxy para o seu serviço.

Por exemplo, eu quero modificar meu endereço de ponto de extremidade de serviço em tempo de execução para que eu tenha o seguinte arquivo ServiceEndpoint.xml .

          
  • Para ler seu 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; } } } } 
  • Em seguida, obtenha seu arquivo web.config de seu cliente no tempo de execução e atribua o endereço do terminal de serviço como:

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

Por que vale a pena, eu precisava atualizar a porta e esquema para SSL para o meu serviço RESTFul. Isso foi o que eu fiz. Desculpas que é um pouco mais que a pergunta original, mas esperamos que seja útil para alguém.

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

veja se você está colocando a seção do cliente no arquivo web.config correto. O SharePoint tem cerca de 6 a 7 arquivos de configuração. 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 )

Poste isso, você pode simplesmente tentar

ServiceClient client = new ServiceClient("ServiceSOAP");