close

establecer mediante programación de configuración de WCF

Parece como si hay alguien en Microsoft, que es único propósito en la vida es averiguar cómo puede ser que desee utilizar algo (WCF) y, a continuación, asegúrese de que no puedo usarlo en la forma que yo quiero. La edición de hoy, mediante programación la configuración de una aplicación cliente de WCF.

Fondo

He creado un almacén de depósito central en forma de una base de datos SQL, que en pocas palabras, contiene pares clave / valor que pueda consultar. Todos mis aplicaciones (o cada machine.config) sólo tiene que especificar una única cadena de conexión de base de datos para esta base de datos de configuración. Todos los demás valores de configuración se recuperan de la base de datos (y en caché), por lo que es fácil hacer el seguimiento de la configuración, ver todos los valores de configuración actuales, y desplegar las aplicaciones en los servidores nuevos.

Ampliando mi definición simple, hay una segunda "clave", que corresponde a lo que el medio ambiente (producción, puesta en escena, el desarrollo) la aplicación se implementa. Así que mi base de datos de configuración de un solo controla la configuración de todas las aplicaciones en todos los ambientes. Para aquellas configuraciones que son los mismos en todos los entornos, no se especifica ninguna clave de despliegue.

Hasta ahora esto ha funcionado muy bien, y yo quería añadir información de configuración de un servicio al cliente de WCF.

WCF

WCF necesita dos propiedades establecidas, una unión y un punto final, y cada una de estas propiedades tienen propiedades 2 sub que puede o no puede tener valores que se deben establecer. Fuera de la caja, WCF se configura más fácilmente usando archivos de aplicación o de configuración web. Todas las herramientas de soporte de los archivos de configuración y es fácil ver lo que está pasando (bueno más o menos).

Yo, básicamente, quería tomar las secciones XML que definen la unión y el punto final, y colocarlo en mi base de datos de configuración. Por alguna razón, lo tengo en mi cabeza que esto sería muy fácil de hacer, pero descubrí que no lo es. Buscar a través de Google dio ninguna solución directa. Si bien se puede establecer mediante programación las propiedades de unión y el punto final de un objeto cliente clase de proxy, yo tampoco tiene que tener 1 ajuste por propiedad (y la propiedad sub) en mi base de datos de configuración, o llegar a un esquema XML, analizar el esquema y establecer Los valores. Este fue el camino que estaba frunciendo, pero en vez de hacer mi propio esquema, sólo utiliza el esquema de configuración XML proporcionado fuera de la caja, con la esperanza de que un día yo podría pasar esos tramos de unión o de punto final objetos y tienen que analizar para yo.

Después de la creación de clases con analizar una sola unión (wsHttpBinding), decidí que debe haber una mejor manera. El código para analizar los archivos de configuración deben existir en algún lugar de System.ServiceModel, así que decidió ir espeleología usando reflector para ver lo que estaba pasando, y determinar si podía hackear algo juntos.

Implementación

Descripción general Después de pasar un par de horas en el reflector, se dio cuenta de que el puesto de control principal fue el uso del espacio de nombres System.Configuration para manejar los datos de configuración para System.ServiceModel. System.Configuration sólo es compatible con el sistema de archivos, y no proporciona los métodos de extensión en este momento (algo que me gustaría ver de Microsoft en el futuro). Esto me dejó con una sola opción, que era crear archivo XML temporal con mis datos de configuración de mi base de datos y, a continuación, leer el archivo de configuración mediante los adecuados clases / métodos en System.Configuration. Me tomó un rato para aceptar esta opción, como escribir un archivo temporal parecía desordenado, pero luego me di cuenta, los archivos temporales son un hecho de la vida en .Net, programación, sistemas operativos, etc. Configuración tienda Mi tienda de configuración ya se definió como yo previamente mencionado. Las 2 muestras XML a continuación se almacenan como entradas separadas en mi base de datos de configuración. A continuación se muestra el código XML Almacené para el manejo de los enlaces: <fijaciones><WsHttpBinding>   <Nombre del enlace = "WSHttpBinding_ITwoWayAsyncVoid" CloseTimeout = "00:01:00"           OpenTimeout = "00:00:30" ReceiveTimeout = "00:02:00" SendTimeout = "00:00:15"           bypassProxyOnLocal = "false" transactionFlow = hostNameComparisonMode "falsa" = "StrongWildcard"           maxBufferPoolSize = "524288" maxReceivedMessageSize = "65536" messageEncoding = "texto"           textEncoding = "UTF-8" useDefaultWebProxy = "true" AllowCookies = "true">           <ReaderQuotas maxDepth = "32" MaxStringContentLength = "8192" maxArrayLength = "16384"             maxBytesPerRead = "4096" maxNameTableCharCount = "16384" />           <ReliableSession ordenó = "true" InactivityTimeout = "00:10:00"             enabled = "true" />           <= Modo de seguridad "mensaje">             <ClientCredentialType transporte = "Windows" proxyCredentialType = "None"               realm = "" />             <Mensaje clientCredentialType = "Windows" negotiateServiceCredential = "true"               algorithmSuite = "Default"EstablishSecurityContext = "true" />           </ Seguridad>   </ Binding>  </ WsHttpBinding> </ Fijaciones>
Soy capaz de almacenar varios enlaces y configuraciones de unión en una sola entrada en mi base de datos de configuración (véase más adelante). En este ejemplo, sólo tengo una sola configuración de unión y vinculante. Yo estaba originalmente planeado para tener 1 entrada para cada unión, pero no creo que sea necesario, en todo caso voy a tener una única entrada para todos los enlaces necesarios por el medio ambiente, e incluso que podría ser excesiva. Este es el XML Almacené para el punto final: <fijaciones>  <WsHttpBinding>   <Name = "WSHttpBinding_ITwoWayAsyncVoid" unión />  </ WsHttpBinding> </ Fijaciones> <Cliente>  <Dirección de punto final = "unión =" wsHttpBinding "bindingConfiguration =" WSHttpBinding_ITwoWayAsyncVoid "         contrato = "TsiCbsEsbReceiver.WcfService_TersoSolutions_CBS_Fusion_BizTalk_InitialProcess"         name = "WSHttpBinding_ITwoWayAsyncVoid">         <Identity>           <UserPrincipalName value = "TERSODEMODEV1BizTalkWebServices" />         </ Identidad>  </ Endpoint> </ Cliente>
Nótese la repetición de la sección <fijaciones>. Esto era necesario si quería utilizar las clases en las System.ServiceModel para analizar el punto final, que necesitaba para definir un esqueleto para los enlaces.
Separé los criterios de valoración de las ataduras, ya que es lo que va a cambiar entre los entornos de la mayoría de los para mí. El punto final se determina el servidor (prueba, producción, puesta en escena), y la identidad (por escenarios de prueba de servidor único, la cuenta será local, para entornos de múltiples servidores de producción, la identidad será una cuenta de dominio).
Código
He creado 2 clases debajo de un espacio de nombres llamado ConfigSystem. La primera clase, llamada ConfigMgr contiene código que me permite ir de mi base de datos de configuración a un objeto System.Configuration.Configuration por medio de un archivo temporal. La segunda clase llamada WCF, contiene código específico a la aplicación de configuración de WCF, y contiene código inferirse de reflector.
Todo el código en ConfigMgr utiliza clases públicas y es bastante simple.
clase estática pública ConfigMgr  {   MemberVars #REGION   "<1,0 privada configFileHeader const string =? Xml version =" "?> <Configuración>";   privada configFileFooter const string = "</ configuration>";      #endregion   Métodos #REGION - Público   public static ConfigKey ConfigurationSection GetSection (cadena,    xmlConfigFileHeaderExtra cadena, cadena xmlConfigFileFooterExtra, cadena sectionname)   {    // Obtener un documento XML desde la base de datos de configuración para almacenar como un archivo tempory.    System.Xml.XmlDocument doc = PrepareXmlDocument (ConfigKey, xmlConfigFileHeaderExtra, xmlConfigFileFooterExtra);    // Ahora escribir en el fichero de temp.    string path = System.IO.Path.GetTempFileName ();    usando (System.Xml.XmlWriter escritor = System.Xml.XmlWriter.Create (camino))    {     doc.WriteTo (escritor);     writer.Close ();    }        // Mapa archivo de configuración de la instalación de manera que podemos utilizar archivos estándar de configuración basado en archivos    mapa ExeConfigurationFileMap = new ExeConfigurationFileMap ();    map.ExeConfigFilename =camino;    System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration (mapa, ConfigurationUserLevel.None);        // Ahora vuelve la sección especificada    volver config.GetSection (sectionname);   }   privada estática System.Xml.XmlDocument PrepareXmlDocument (cadena ConfigKey, xmlConfigFileHeaderExtra cadena, cadena xmlConfigFileFooterExtra)   {    System.Text.StringBuilder sb = new System.Text.StringBuilder ();        // Agregar encabezado    sb.Append (configFileHeader);    sb.Append (xmlConfigFileHeaderExtra);    sb.Append (ConfigHandler.GetValue (ConfigKey));    // Añadir pie de página    sb.Append (xmlConfigFileFooterExtra);    sb.Append (configFileFooter);    System.Xml.XmlDocument doc = new System.Xml.XmlDocument ();    doc.LoadXml (sb.ToString ());    doc regresar;   }   #endregion  }
El código de la clase WCF utiliza algunos métodos que he copiado a través del reflector, y debido a que no sé las ramificaciones legales de eso, yo sólo estoy proporcionando un esbozo de lo que hice.
GetBinding: Llamadas sección ConfigMgr.Get y devuelve la sección <fijaciones>. Esta es arrojado a un objeto BindingsSection. Código tomado de System.ServiceModel.Configuration crea una BindingCollectionElement que es el tipo correcto asociado con la unión almacena en la configuración XML. Un nuevo objeto de unión se ha creado usando Activator.CreateInstance (BindingCollectionElement.BindingType) Código adicional tomada de System.ServiceModel.Configuration bucles a través BindingCollectionElement.ConfiguredBindings e inicializa el objeto de unión creada anteriormente. Se realiza una comprobación para asegurarse de que varios enlaces del mismo tipo no se crean. Por último, el objeto de enlace es devuelto a la persona que llama.
GetChannelEndpointElement: Esta clase utiliza sólo las clases y métodos, así que estoy incluyendo públicos. EnpointElement contiene los datos de configuración para un punto final, incluyendo el URI, la identidad, la unión, y la configuración de unión. Los datos de configuración de unión y la unión es en realidad pasa a mi método GetBinding para devolver la unión correcta con la configuración correcta de unión. public static ChannelEndpointElement GetChannelEndpointElement (cadena ConfigKey)   {    sección ClientSection = (ClientSection) ConfigMgr.GetSection (ConfigKey, system_ServiceModelAsOpenXml, system_ServiceModelAsCloseXml, clientSection);    si (la sección == null)     throw new ArgumentException (string.Format ( "sección de cliente regresó de db config para la clave {0} fue nula", ConfigKey));    si (section.Endpoints.Count! = 1)     throw new ArgumentException (string.Format ( "Tiene que ser exactamente 1 punto final regresó de la configuración {0} puntos finales fueron devueltos por tecla {1}.", section.Endpoints.Count, ConfigKey));volver section.Endpoints [0];       }
GetEndpointAddress: Otro método creé que casi se utiliza todos los métodos públicos. Se necesita un ChannelEnpointElement y devuelve un EndpointAddress que puede ser asignada a una clase de proxy WCF. public static EndpointAddress GetEndpointAddress (elemento ChannelEndpointElement)   {    // Crear un nuevo constructor, ya que la dirección de punto final es una clase inmutable    EndpointAddressBuilder constructor = new EndpointAddressBuilder ();    builder.Identity = LoadIdentity (element.Identity);    builder.Uri = element.Address;    builder.ToEndpointAddress volver ();   }
LoadIdentity: Este último método fue tomado de System.ServiceModel.Description.Configloader. Este método toma un IdentityElement (una propiedad de ChannelEnpointElement) y devuelve un EndpointIdentity que se pasa a la EndpointAddressBuilder en mi método GetEndpointAddress. El método comprueba básicamente la existencia de ciertas propiedades, y si existe, devuelve la identidad apropiada usando métodos de fábrica públicas. EndpointIdentity.CreateUpnIdentity EndpointIdentity.CreateSpnIdentity EndpointIdentity.CreateDnsIdentity EndpointIdentity.CreateRsaIdentity EndpointIdentity.CreateX509CertificateIdentity
Hay un tipo de identidad de referencia certificado que he podido añadir soporte para, porque hace referencia a las clases internas adicionales, que no me desea duplicar en este momento ya que no tenía necesidad de ese tipo de identidad.

Cuestiones

Puesto que esto es completamente soportado por Microsoft, y utiliza el código que nunca fue pensado para uso externo, en una versión futura del marco, algo que podría romper. Yo tampoco soy un experto legal cuando se trata con lo que alguien puede hacer con código MSIL descubierto a través del reflector, así que por ahora, el código real que escribí no está disponible públicamente. Con suerte que he proporcionado suficiente información para permitir a alguien para reproducir mis pasos y poner en práctica algo por su cuenta.

Otra realización algo decepcionante llegué a, era que esto puede hacer que sea difícil para apoyar operaciones de escritura en mi base de datos de configuración. Actualmente la base de datos de configuración se gestiona a través de secuencias de comandos y la edición directa. Si el soporte de escritura a la base de datos de configuración se convirtió en un requisito, probablemente me trato de hacer lo contrario y utilizar el construido en el archivo de la funcionalidad de guardar en System.Configuration para guardar un archivo XML temporal, leerlo y luego guardarlo en mi base de datos de configuración.

Conclusión

Este fue mi primer intento de hacer algo como esto, la excavación en código de marco para ver cómo funciona realmente y tratando de llegar a una solución. Voy a seguir adelante con el código que he escrito esbozado en este post, ya que tengo el apoyo completo de lectura de configuración XML WCF. La cantidad de código que tenía que escribir y la prueba es significativamente menos entonces, si yo hubiera querido para obtener la misma cantidad de funcionalidad (la configuración para todos los enlaces y configuraciones de punto final).

Compartir este:

Previous Post     Next Post


TAGS


CATEGORIES

.