2009-06-08 17 views
49

Me gustaría modificar programáticamente mi archivo app.config para establecer qué punto final del archivo de servicio se debe usar. ¿Cuál es la mejor manera de hacer esto en tiempo de ejecución? Para referencia:Cómo modificar programáticamente la configuración de la dirección del punto final WCF app.config?

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

alquímico, es posible que desee volver a evaluar su opción de respuesta aceptada –

+0

En mi experiencia, si ves que necesitas para modificar un app.config en tiempo de ejecución, probablemente significa que se echa en falta un medio proporcionado .NET de lograr lo que estás tratando de hacer. La respuesta de Alex Knott a continuación lo aclara en este caso si todo lo que realmente intenta hacer es presionar una dirección diferente donde se aloja el mismo servicio exacto. –

Respuesta

2

Creo que lo que queremos es que cambiar en tiempo de ejecución de una versión de su archivo de configuración, si así se creará una copia de su archivo de configuración (también darle la extensión relevante como .debug o .Release) que tiene las direcciones correctas (que le proporciona una versión de depuración y una versión en tiempo de ejecución) y crea un paso posterior a la compilación que copia el archivo correcto según el tipo de compilación.

Aquí es un ejemplo de un evento postbuild que he utilizado en el pasado que anula el archivo de salida con la versión correcta (debug/tiempo de ejecución)

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

donde: $ (ProjectDir) es el directorio del proyecto, donde se encuentran los archivos de configuración $ (ConfigurationName) es el tipo de configuración de generación activa

EDIT: véase la respuesta de Marc como una explicación detallada sobre cómo hacer esto mediante programación.

+0

Terminé haciéndolo programáticamente, gracias. – alchemical

+0

No puede usar un nombre de enlace que no esté en .config. "prueba" devolverá un error. Esto es un problema porque el almacenamiento en caché de fábrica de canales solo puede ocurrir si especifica el nombre de la configuración de enlace, y no un objeto vinculante = ( – Sprague

+0

Sugiera que cambie el código de "prueba" de código anterior. – MrEdmundo

94

¿Esto está del lado del cliente?

Si es así, debe crear una instancia de WsHttpBinding y una EndpointAddress, y luego pasar esos dos al constructor del cliente proxy que toma estos dos 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); 

Si está en el lado del servidor de las cosas, que necesita para crear mediante programación a su propia instancia de ServiceHost, y añadir los puntos finales de servicios adecuados a la misma.

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

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

Por supuesto, puede agregar varios de estos puntos finales de servicio a su host de servicio. Una vez que haya terminado, debe abrir el host del servicio llamando al método .Open().

Si desea poder dinámicamente, en tiempo de ejecución, elegir qué configuración usar, puede definir varias configuraciones, cada una con un nombre único, y luego llamar al constructor apropiado (para su servidor de servicio o su cliente proxy) con el nombre de configuración que desea usar.

E.g. usted puede fácilmente tener:

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

(tres nombres diferentes, diferentes parámetros mediante la especificación de diferentes bindingConfigurations) y luego simplemente elegir el más adecuado para crear instancias de su servidor (o proxy de cliente).

Pero en ambos casos, servidor y cliente, debe elegir antes de crear realmente el servidor de servicio o el cliente proxy. Una vez creados, estos son inmutables; no puede modificarlos una vez que estén en funcionamiento.

Marc

+0

¿Cómo se puede agregar programáticamente la configuración de enlace y el contrato al punto final? –

+0

Consulte la parte superior de mi respuesta: debe crear una dirección de enlace y una dirección de punto final y luego crear su punto extremo de servicio (en el lado del servidor) o su proxy del lado del cliente en función de esos dos elementos. No puede "agregar" un enlace a un punto final: un punto final consiste en un conjunto de tres direcciones (URI), vinculante y contrato –

+11

Esta debería ser la respuesta correcta, es mucho más detallada y útil. –

26

Utilizo el siguiente código para cambiar la dirección del punto final en el archivo App.Config. Es posible que desee modificar o eliminar el espacio de nombres antes de su 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"; 
     } 
    } 
} 
+1

Esto es un truco, pero he votado a favor de todos modos, ya que pensé que era una buena solución. – Pretzel

+3

un hack o no, esto podría ser muy útil cuando no hay soporte más adelante para los usuarios que usan el programa – Terry

+0

VS2010 .NET 4.0, es 'ConfigurationSettings', no' ConfigSettings' – leo

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

lo hice así. Lo bueno es que aún recoge el resto de la configuración de enlace de su endpoint desde la configuración y simplemente reemplaza el URI.

+0

También puede simplificar aún más omitiendo la creación manual de EndpointAddress y simplemente especificando la dirección directamente en el constructor del cliente, por ejemplo, var client = new SomeServiceClient ("EndpointConfigurationName", "net.tcp: // servername: 8000/SomeServiceName /") –

2

He modificado y extendido el código de Malcolm Swaine para modificar un nodo específico por su atributo de nombre, y también para modificar un archivo de configuración externo. Espero eso ayude.

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

}

+0

Impresionante, gracias por compartir esto y por ahorrarme los minutos. : D – defines

2

Este es el código más corto que se puede utilizar para actualizar el archivo de la aplicación de configuración, incluso si no tienen una sección de configuración 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"); 
} 
1
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 } 

Es muy fácil de modificar el uri en configuración o información de enlace en config. ¿Es esto lo que quieres?

4

este breve código que funcionó para mí:

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

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

Por supuesto, usted tiene que crear el proxy ServiceClient DESPUÉS de la configuración ha cambiado. También necesita hacer referencia al System.Configuration y System.ServiceModel ensambles para que esto funcione.

Saludos

+0

Esto también funcionó para mí. Si desea modificar múltiples puntos finales, puede crear un ciclo simple para: para (int i = 0; i

+0

¡Esto funcionó increíble! Usé esto para recorrer todos los puntos finales y actualizar la dirección IP en tiempo de ejecución, así que ahora tengo una configuración de aplicación única para la dirección IP de los servicios. –

1

Usted puede hacerlo de esta manera:

  • Conservar la configuración en un archivo XML independiente y leer a través de él cuando usted crea un proxy para su servicio.

Por ejemplo, quiero modificar mi dirección de punto final del servicio en tiempo de ejecución, así que tengo el siguiente archivo de ServiceEndpoint.xml.

 <?xml version="1.0" encoding="utf-8" ?> 
    <Services> 
     <Service name="FileTransferService"> 
      <Endpoints> 
       <Endpoint name="ep1" address="http://localhost:8080/FileTransferService.svc" /> 
      </Endpoints> 
     </Service> 
    </Services> 
  • Para leer el 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; 
          } 
         } 
        } 
    } 
    
  • luego obtener su archivo web.config de su cliente en tiempo de ejecución asignar y la dirección de extremo de servicio 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(); 
    
1

Por lo que vale, necesitaba t o actualizar el puerto y el esquema para SSL para mi servicio RESTFul.Esto es lo que hice. Disculpa que es un poco más que la pregunta original, pero afortunadamente útil para alguien.

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