Move y Merge en TFS

El equipo en el que trabajo está mejorando sus políticas de branching e intentando ser más riguroso a la hora de hacer Scrum.

Recientemente nos vimos en una situación que, aunque debería ser marginal, ocurre en todos los sprints (y probablemente en los de todos los equipos, para ser sinceros): un Product Backlog Item no se completó en el sprint.

Nuestra política de ramas es “un branch por característica”, por lo que tenemos tantos branches como PBIs. Además, para ser un poco más organizados, creamos carpetas padre para los PBIs que se hicieron en un sprint. Por eso, si el sprint es “Release 1 – Sprint 2”, tendremos una carpeta R1S2 que contendrá todos los branches correspondientes a los PBIs de ese sprint.

Solución 1: Llevar a Main

No podemos llevar el código inestable que hay en la rama original a Main y de ahí crear una nueva rama, puesto que estaríamos “rompiendo” Main. Además, tampoco tendría mucho sentido esta duplicidad.

Solución 2: Branch de Branch

Otra posible opción sería hacer un branch del branch original, en la nueva carpeta. Por ejemplo, hacer un branch de R1S2PBIXXX a R1S3PBIXXX, donde XXX es el ID del PBI no completado. El problema de esta solución es que, a la hora de llevar los cambios a Main, nos obligaría a ir hacia atrás en el tiempo, de la última rama a la anterior, y de ahí a Main. También nos obligaría a mantener “viva” la rama original, a la espera de recibir los cambios para poder, después, merguearse a Main y “morir”.

Solución 3: Move del Branch

Está creíamos que era la mejor solución: mover la rama de la carpeta original a la nueva. Por ejemplo, de R1S2 a R1S3. De este modo mantendríamos la rama “enganchada” a Main y podemos hacer el branch directamente desde la nueva posición.

Sin embargo, aquí viene TFS con las rebajas: al hacer el merge de vuelta a Main, interpreta TODOS los ficheros como mergeables, por haber sido movidos. Lo peor es que, si confirmamos el checkin, la historia de ese fichero refleja dicha entrada, aun cuando no hay diferencias reales en su contenido.

Parece ser que este comportamiento es premeditado y probablemente existan buenas razones para ello, pero a nosotros nos obliga a prescindir de esta solución, para no convertir el History de los ficheros en un pequeño caos.

Conclusiones

No existe una buena solución, pero tendremos que tirar con la Solución 2 porque es la única viable a día de hoy.

[WCF] Nombres de máquina incorrectos en WSDL

Nos había pasado anteriormente y nos ha vuelto a pasar: WCF nos generaba el documento WSDL con el nombre de la máquina, no con su dominio.

Esto hacía que, por ejemplo, una máquina con nombre XXX en un dominio YYY, aunque su WSDL estuviera accesible en http://XXX/Servicio.svc?wsdl, internamente las rutas del documento WSDL hicieran referencia a http://XXX.YYY/, lo que hacía el WSDL inservible.

Parece ser que, hasta WCF 4.0, la solución no era sencilla: había que parchear este "bug”, “feature” o como queramos llamarlo, tal como se explica en este KB de Microsoft: http://support.microsoft.com/kb/971842/en-us

Sin embargo, a partir de WCF se ha incorporado la corrección y ya podemos tirar directamente de XML y olvidarnos de DLLs. El fragmento sería tan sencillo como éste:

<behaviors>
  <serviceBehaviors>
    <behavior>
      <!-- Necessary to avoid problems with domain/computer names -->
      <useRequestHeadersForMetadataAddress>
        <defaultPorts>
          <add scheme="http" port="80" />
        </defaultPorts>
      </useRequestHeadersForMetadataAddress>
    </behavior>
  </serviceBehaviors>
</behaviors>

Con esto ya tendremos nuestros WSDLs enlazando internamente de forma correcta el resto de elementos de que se compone.

Rulesets y branches

Desde la versión 2010 de Visual Studio podemos configurar rulesets específicos para cada proyecto, que se guardan como ficheros con extensión .ruleset.

Esta nueva forma de gestionarlo es muy flexible y nos permite controlar cómo de estricto es Code Analysis a la hora de realizar el análisis estático de cada uno de los proyectos de nuestra solución.

Por supuesto, si todo el proyecto comparte unas mismas reglas, es perfectamente posible compartir un único fichero (la solución que, en mi caso, adopto el 99% de las ocasiones).

Puesto que los rulesets pueden definirse para cada proyecto, dicha configuración se refleja en el .csproj. de dicho proyecto. La buena noticia es que la ruta es relativa, con lo cual en el momento de crear nuevas ramas, no tendremos ningún problema. La nueva versión del .csproj en la nueva rama hará referencia directamente a la nueva versión del fichero .ruleset que también habremos brancheado (ojo con esto si estamos haciendo branches parciales).

Sin embargo, hay un comportamiento no deseado: cuando se mueve un proyecto, la ruta relativa no se actualiza. Por lo tanto, deja de hacer referencia a nada que exista, al haberse desplazado la posición del fichero .csproj.

En este caso, al compilar en Visual Studio, recibiremos un warning avisando de que, al no haber podido cargar el ruleset para el proyecto, no se ha ejecutado Code Analysis.

La solución es fácil: abrir las propiedades del proyecto y volver a seleccionar el ruleset correspondiente a la rama en la que nos encontramos. Pero ojo al realizar esta operación, puesto que en el combo de seleccionar, por defecto, no vamos a ver ese fichero.

ruleset

En su lugar, sí nos va a aparecer una entrada relativa al fichero original, aquél del que hemos hecho el branch, y es muy sencillo no percatarnos de ellos y elegirlo (puesto que, al ser nuestro branch una copia, tendrá el mismo nombre).

En definitiva, los rulesets y los branches funcionan perfectamente, pero debemos tener cierto cuidado en caso de que movamos alguno de nuestros proyectos. En tal situación, actualizar las propiedades del proyecto con cierto cuidado de no elegir el ruleset equivocado.

Forzar ejecución en 32 bits

Escenario: una aplicación .NET ejecutándose en una máquina de 64 bits, que falla. Exactamente el mismo ejecutable, sobre una máquina de un compañero en 32 bits, funciona perfectamente. ¿Podemos forzar a esa aplicación a ejecutarse con compatibilidad 32 bits, como hacen montones de aplicación hoy en día en los entornos de 64 (me viene a la cabeza Visual Studio 2010, por ejemplo). Claramente, sí.

Después de un poco de investigación, encontré este enlace describiendo las distintas posibilidades que tenemos. Voy a explicar cuál es la que aplicaba a mi escenario.

Modificar flag en Assembly

En mi caso, dado que era un compilado ya desplegado (fuera la opción VS) y que era una aplicación de escritorio (fuera la opción de IIS), me quedé con la posibilidad de modificar un flag del assembly.

En cada assembly existen unas ciertas cabeceras que son modificables mediante la herramienta “CoreFlags”. En este caso concreto, es el flag 32BIT el que tenemos que modificar. Para hacer el cambio, abriríamos una consola de Visual Studio y ejecutaríamos el siguiente comando, para marcar el flag y asegurarnos de que nuestra aplicación se ejecuta sobre 32 bits

CorFlags Assembly_a_modificar.exe /32BIT+

Para desactivar el flag, el comando es casi idéntico.

CorFlags Assembly_a_modificar.exe /32BIT-

Espero que os resulte tan útil como a mí.

Microsoft Web Deploy: Instalación y Uso

Actualización: He añadido al artículo original una explicación de porqué es más seguro utilizar usuarios de IIS en lugar de cuentas Windows, así como los problemas que podemos tener con el certificado a la hora de realizar los despliegues. Gracias desde aquí a Fernando Guillot por sus sugerencias.

 

Son muchas las novedades que han aparecido en Visual Studio 2010, entre ellas varias relacionadas con el despliegue de aplicaciones. A partir de ahora, en el menú contextual de cada proyecto web en VS2010 tendremos tres nuevas opciones, como se muestra en la siguiente imagen: generar un paquete de despliegue (un archivo zip), publicar utilizando alguna de las tecnologías disponibles y establecer la configuración del empaquetamiento/publicación.

NuevasOpcionesDespliegue

Respecto a la publicación, sin duda la principal novedad es poder utilizar la tecnología de Web Deploy, presente en IIS (Internet Information Services) y que ayudará a administradores a simplificar la migración, gestión y despliegue de servidores web IIS. A continuación, vamos a ver cómo instalar y configurar esta tecnología en un servidor Windows Server 2008 R2. Al final del post se detallará cómo configurar Visual Studio 2010 para poder hacer uso de esta nueva forma de despliegue.

 

Instalación

Vamos a partir de una instalación en limpio de Window Server 2008 R2 Standard.

 

Añadir rol de servidor web

El primer paso es añadir el rol de servidor web a nuestra máquina Windows Server. Lanzamos la herramienta “Server Manager” y pulsamos en la opción “Roles”, en el árbol de la parte izquierda. En el centro de la ventana podremos ver una opción para añadir un nuevo rol, como a continuación.

AnadirRolWeb_Paso1

En el asistente que nos aparece, pulsamos el botón “Siguiente” hasta que nos aparezca la lista de roles que podemos instalar. Seleccionamos el rol “Web Server (IIS)” tal como muestra la imagen bajo estas líneas.

AnadirRolWeb_Paso2

Completaremos la instalación del rol indicando que queremos instalar también el servicio de Management, como se puede ver en la siguiente figura. Después, sólo queda esperar a que la instalación termine.

AnadirRolWeb_Paso3

 

Instalar Web Deploy Tool

Ya tenemos instalado nuestro servidor IIS. Ahora debemos “ampliarlo” instalando la herramienta de Web Deploy. Lo primero será descargarnos el instalable y ejecutarlo.

Una vez ejecutado el fichero que acabamos de descargar, sólo tenemos que seguir el asistente pulsando el botón “Siguiente” hasta que nos ofrezca elegir qué tipo de instalación queremos realizar. Debemos instalar la versión completa si queremos tener a nuestra disposición el servicio “Web Deployment Agent”.

Tras terminar la instalación, será necesario arrancar a mano este servicio, puesto que está configurado por defecto como manual y parado. Lo haremos abriendo una consola y ejecutando el siguiente comando.

net start msdepsvc

 

Configuración del Web Deployment Handler

Ya tenemos Web Deploy instalado. Con esto podríamos generar paquetes de instalación con Visual Studio 2010 y desplegarlos en nuestra máquina destino utilizando IIS Manager. Sin embargo, para poder utilizar un perfil de despliegue desde el menú correspondiente de Visual Studio 2010, aún tenemos que dar algunos pasos más.

Lo primero será activar las conexiones remotas al servicio de Management. Para ello, utilizando el IIS Manager, debemos seleccionar el nodo del servidor y, dentro de la vista de Features, hacer doble click en el icono de “Management Service”. Una vez en la pantalla correspondiente, tal como muestra la siguiente imagen, podremos habilitar las conexiones remotas e indicar que será posible autenticarse tanto con cuentas Windows como con credenciales del IIS Manager.

ActivarConexionesRemotas

El siguiente paso a ejecutar es conceder privilegios sobre la carpeta en la que se van a hacer los despliegues. En este punto tenemos dos estrategias posibles a seguir:

  • Concederle permisos a la cuenta sobre la que corre el servicio de Management, por defecto “Local Service”. Es la opción más sencilla, puesto que no tendremos que crear cuentas extra y, además, más adelante al definir las distintas reglas de despliegue, nos simplificará uno de los pasos.
  • La otra opción es crear una cuenta específica y concederle a ella esos privilegios. También podemos optar por crear un grupo y concederle a dicho grupo los permisos. De este modo, permitir a más usuarios poder realizar el despliegue sería tan sencillo como añadirlos al grupo.

Sea cual sea el enfoque que tomemos, es importante aclarar que los mismos permisos que se asignan aquí, se deberán conceder más adelante sobre el fichero applicationHost.config, por lo que la decisión tiene claras implicaciones de seguridad.

Por simplicidad, aquí vamos a concederle los privilegios a “Local Service”. Lo haremos sobre la carpeta donde vamos a desplegar las aplicaciones web, es decir, sobre Inetpubwwwroot, tal como se puede ver en la imagen.

PermisosDirectorioDeploy

El siguiente paso sería crear el usuario de IIS al que queremos conceder la posibilidad de hacer despliegues remotos de aplicaciones. Podríamos crear, en su lugar, una nueva cuenta Windows y realizar todo el proceso con ella. Sin embargo, tendremos un nivel extra de seguridad si utilizamos un usuario de IIS puesto que, en caso de ser comprometido, este usuario no sería reconocido por el sistema ni por ninguna otra aplicación de la máquina. Para ello, de nuevo es necesario utilizar IIS Manager, pulsar sobre el nodo del servidor y, en la ventana de Features, hacer doble click, esta vez sobre el icono de “IIS Manager Users”. En la parte derecha de la pantalla encontraremos un enlace para añadir nuevos usuarios. Tras pulsar en él, podremos introducir en la ventana emergente un nombre para el usuario y una contraseña.

AnadirIISManagerUsuario

Debemos dar permisos al usuario que acabamos de crear sobre el sitio web en el que va a realizar sus despliegues. Para ello, todavía en IIS Manager, pulsamos sobre el nodo que corresponde al sitio web. Sobre la ventana de Features, en este caso correspondiente al sitio web, debemos hacer doble click en el icono de “IIS Manager Permissions”. En la parte derecha de la pantalla podremos ver un enlace para añadir un usuario, que nos mostrará una ventana como la que se ve el imagen que sigue. Esta pantalla permite elegir entre cuentas Windows o cuentas de IIS Manager. Puesto que el usuario que hemos añadido en el paso anterior es del segundo tipo, usaremos el segundo botón para elegirlo de la lista de usuarios de IIS Manager y añadirlo.

AsignarPermisosSobreSitio

 

Reglas de delegación

Recapitulando, en este punto tenemos correctamente creado nuestro usuario IIS y asignados permisos sobre el sitio web. Necesitamos ahora crear diferentes reglas relacionadas con el despliegue de aplicaciones. Vamos a crear un total de tres, siempre utilizando IIS Manager.

Para crear cada una de las reglas, debemos lanzar IIS Manager si no lo tenemos todavía abierto y pinchar sobre el nodo del servidor. Como en pasos anteriores, pulsaremos en la vista de Features y haremos doble click sobre uno de sus iconos: el que corresponde a “Management Service Delegation”. Pulsando en la parte derecha de la pantalla sobre “Add Rule” aparecerá una ventana emergente como la que muestra la siguiente imagen. Seleccionaremos una regla de tipo “Deploy Applications with Content”. Quedémonos con la secuencia de pasos que nos ha llevado a esta ventana de selección de reglas, puesto que volveremos a ella dos veces más.

AnadirReglaParaUsuario

Aceptamos la configuración por defecto para la regla pulsando OK y en la ventana que aparece despues introducimos el nombre que hayamos dado a nuestro usuario IIS. El resultado que veríamos sería parecido a la siguiente imagen.

ResultadoReglaDespliegue

Hay más reglas que podemos configurar, como se ha visto en la pantalla anterior. Otra de las reglas permite al usuario configurar las listas ACL (Access Control Lists) de los ficheros que componen la aplicación web. Para añadirla, es preciso seguir los pasos anteriores y seleccionar en la pantalla de tipos de regla, la denominada “Set Permissions”. Pulsaremos OK en la ventana que aparecerá para crear la regla con sus valores por defecto, y de nuevo introduciremos el nombre del usuario IIS que creamos varios pasos antes.

La última regla que vamos a añadir permite al usuario la creación de aplicaciones en el servidor IIS. La regla a seleccionar de entre los distintos tipos que aparecen en la ventana tras pulsar el botón de “Add Rule” se llama “Mark Folders as Applications” y presenta una pequeña diferencia con las anteriores: no podemos aceptar la configuración por defecto, puesto que tenemos que seleccionar como cuenta de usuario para ejecutar la regla, la cuenta actual (“Local Service”). Para que la regla funcione correctamente, es necesario que la cuenta sobre la que se ejecuta tenga permisos de escritura sobre el fichero c:windowssystem32inetsvcconfigapplicationHost.config. Por tanto, lo primero será dirigirnos a la carpeta que contiene ese fichero y concederle privilegios de escritura a la cuenta “Local Service”.

PermisosApplicationHosts

Una vez hecho eso, añadiremos la regla siguiendo los pasos anteriores. En la ventana que nos aparece tras pulsar “Add Rule”, debemos seleccionar la opción CurrentUser

ModificacionReglaAplicaciones

Una vez modificada la regla, tras pulsar el botón de OK, la asociaremos con el usuario de IIS como hemos hecho con las anteriores reglas. Con esto, podemos dar la configuración por terminada.

 

Conexión desde IIS Manager

Para comprobar si hemos completado correctamente todo el proceso, podemos descargar la herramienta de administración remota de IIS y tratar de conectarnos al servidor que acabamos de configurar.

Una vez instalada y arrancada, es posible probar si nuestra configuración es correcta intentando conectarnos al sitio con el usuario IIS que creamos. Para ello, tendremos que pulsar en el menú “File” y seleccionar la opción “Connect to a Site”.

En el asistente que va a aparecer tendremos que meter los siguientes datos en la primera pantalla:

  • Server name: dirección IP o nombre de la máquina que hemos configurado.
  • Site name: nombre del sitio. Si es el sitio por defecto de IIS, será “Default Web Site”.

PrimeraPantallaWizardConexion

En la siguiente pantalla del asistente, tendremos que introducir las credenciales de nuestro usuario IIS. Si todo es correcto, tras conectarse nos solicitará confirmación de que confiamos en el certificado con el que se está cifrando la conexión.

AceptarCertificado

Este error nos aparece puesto que el nombre del servidor no coincide con la máquina para la que se ha emitido este certificado. Si pulsamos sobre el botón “View Certificate” para ver más información del mismo, podremos comprobar que en el campo “Issued to” aparecerá siempre “WMSvc-“ seguido del nombre de la máquina. Sin embargo, si generamos un nuevo certificado en IIS con el nombre de la máquina, configuramos el servicio de Management para usarlo con la conexión SSL y especificamos el nombre de la máquina en lugar de su dirección IP para conectarnos a ella, recibiremos este otro aviso.

AvisoCertificadoInvalido_2

En este caso, si pinchamos sobre “View details…” nos encontraremos con un poco útil mensaje indicándonos que ha habido un error desconocido. Si las máquina servidor y cliente estuvieran en un dominio en el que ambas confiaran en una máquina con autoridad certificadora, podríamos generar un certificado de dominio y resolver el problema. Sin embargo, en instalaciones de prueba como la de este ejemplo, bajo grupos de trabajo, no resulta práctico y es más productivo instalar el certificado para poder completar el proceso.

Tras conectarnos y darle un nombre a la nueva conexión, podremos ver información sobre el servidor IIS remoto en nuestra ventana de IIS Manager. Ahora que sabemos que está listo para conectarse a él, nos queda utilizarlo desde Visual Studio 2010.

 

Conexión desde Visual Studio 2010

Ya estamos donde queríamos estar. Tenemos listo Web Deploy en el servidor donde vamos desplegar nuestras aplicaciones web, y ahora sólo nos resta configurar Visual Studio para empezar a disfrutar de las ventajas de esta tecnología.

En cualquier proyecto web que queramos desplegar, sobre el nombre del proyecto, con el botón derecho del ratón, haremos aparecer el menú que veíamos en la primera imagen de este artículo. Debemos pulsar sobre la opción “Publish” para que se muestre una ventana como la siguiente.

PerfilDespliegueVS2010

Los datos a introducir serían:

  • Un nombre para el nuevo perfil de publicación. En el ejemplo, “WebDeploy DevIIS”.
  • Seleccionar un método de publicación: Web Deploy.
  • La dirección del servicio, que será, por defecto, http://servidor:8172/MsDeploy.axd
  • La aplicación web a crear. Puesto que estamos creando sobre el sitio “Default Web Site”, deberá aparecer antes de la barra. Al otro lado, podemos dar el nombre de una aplicación existente o crear una nueva.
  • Si marcamos el checkbox que viene inmediatamente después, estaremos indicando que queremos crear una aplicación en IIS al hacer el despliegue.
  • Necesitamos activar el checkbox relacionado con las credenciales si tenemos un certificado de desarrollo, como en este ejemplo.
  • Por último, introducimos las credenciales del usuario IIS que definimos varios pasos antes en este artículo.

Una vez configurado correctamente el perfil de despliegue, sólo necesitaremos pulsar el botón de Publish para ver cómo, de forma rápida, limpia y segura, tenemos desplegada nuestra aplicación web en nuestro servidor.

 

Conclusiones

Puede que el proceso inicial de configuración del servidor resulte laborioso, pero una vez completados los pasos, la ganancia que en tiempo, facilidad y seguridad de despliegue van a obtener tanto administradores como desarrolladores, bien merece la inversión. Desde el punto de vista de los administradores, la granularidad a la hora de controlar qué tareas se permiten para cada usuario, independizando las cuentas del sistema de las cuentas únicamente destinadas a la gestión del servidor web, justifican el esfuerzo.

Y desde el punto de vista de los desarrolladores, resulta toda una gozada poder desplegar nuestras aplicaciones a entornos de desarrollo, preproducción y producción con en tres clicks. De este modo, pasar de unos entornos a otros dejará de ser la tarea complicada a y propensa a errores que es actualmente.

 

Bibliografía

WCF4: Activación sin fichero .svc

Hoy mismo hablaba con un compañero del trabajo sobre los ficheros svc, esos simpáticos amigos que nos permiten identificar la dirección sobre la que tiene que consumirse un servicio web en WCF. Nuestra conversación giraba en torno a la posibilidad de hostear un servicio WCF en una dirección que terminara en .asmx, para reemplazar un servicio web ASP.NET “de los antiguos” por uno de WCF sin que la URL cambiara. ¿Se podrá, pensábamos? Seguramente sí, pero a partir de WCF4 es seguramente más fácil que nunca gracias a la activación de servicios WCF sin fichero .svc.

¿Cómo hacerlo? Tirando, como no, del app.config. Como siempre, lo más fácil es verlo con un ejemplo. Vamos a partir de un proyecto WCF de ejemplo, de los que nos crea Visual Studio 2010. El contrato de servicio tendrá un método GetData y habrá una clase que implemente dicho servicio. En realidad, nos da un poco igual para lo que vamos a ver.

Vamos al tema. Nuestro fichero app.config tendrá un aspecto parecido a éste:

<configuration>
  <system.serviceModel>
    <serviceHostingEnvironment>
      <serviceActivations>
        <add relativeAddress="MyVirtualSvcFile.svc" service="WcfService.Service"/>
      </serviceActivations>
    </serviceHostingEnvironment>
  </system.serviceModel>
</configuration>

Como ya hemos comentado en post anteriores, la configuración se reduce a la mínima expresión en WCF4. En este caso estamos dejando al framework que se encargue de generarnos los bindings por defecto, y simplemente le indicamos que vamos a tener la dirección virtual con un “fichero” svc que activará el servicio indicado en el atributo “service”.

De este modo, cuando nos dirijamos a la dirección http://dominio/RutaServicio/MyVirtualSvcFile.svc veremos la clásica pantalla de WCF indicándonos que no tenemos los metadatos activados, como se muestra en la siguiente imagen:

ServicelessActivation

 

Todo esto es muy bonito, pero no responde a la pregunta original: ¿podríamos usar una URL terminada en .asmx para activar el servicio WCF? Pues, al menos con esta aproximación mediante las nuevas configuraciones de WCF4… NO. Éste es el bonito error que obtenemos cuando lo intentemos:

ServicelessActivationFailed

Una pena. En cualquier caso, esta nueva forma de activar los servicios nos ahorrará tener que andar preocupados de crear N ficheros .svc, tantos como servicios queramos activar. Mucho mejor tenerlos convenientemente descritos en el fichero app.config.

Actualización: El problema lo da con cualquier extensión que no sea .svc. De paso, hay quien dice que ya no está disponible esta característica. ¡ Para luego hacerle caso a a los de Microsoft !

WCF: OneWay y bloqueo del cliente

Cuando me estaba preparando el MCTS sobre WCF leí una afirmación sorprendente en el Training Kit oficial de Microsoft. En él venían a decir sus autores, en una traducción más o menos libre, lo siguiente sobre el patrón de intercambio de mensajes OneWay:

Dada la naturaleza OneWay del canal, uno podría pensar que, tan pronto el consumidor envía el mensaje, éste se procesa asíncronamente y el cliente es libre de hacer otras cosas. Sin embargo, la forma en que la maquinaria de WCF funciona significa que el consumidor, de hecho, se bloquea, incluso si el mensaje es OneWay, hasta que el dispatcher entrega el mensaje a una instancia del servicio, en la forma de una llamada a un método del objeto.

Teniendo en cuenta que OneWay es un patrón “Fire-and-Forget”, no encaja mucho que el cliente tenga que mantenerse bloqueado hasta que el servidor entrega el mensaje a un objeto que implemente el contrato de servicio. Es importante recordar que alguna de las particularidades de OneWay no hacen sino resaltar esta naturaleza “Fire-and-Forget”; por ejemplo, las siguientes dos propiedades:

  • Un método OneWay no soporta retornar ningún resultado, siempre será un método void. Tampoco soporta el atributo FaultContract, pues no pueden definirse errores que vaya a devolver al no tener capacidad de devolver nada.
  • Un método OneWay no soporta fluir transacciones entre el cliente y el servidor, y viceversa.

No parece que lo afirmado por los autores del Training Kit encaje mucho con estas propiedades (y otras que se quedan en el tintero) de OneWay. Así que lo mejor es salir de dudas con un pequeño ejemplo, para ver la validez de dicho comentario.

La forma más sencilla de poder comprobarlo es aprovecharse de la forma en que está definido el pipeline de WCF. Son varios los puntos de extensibilidad que tiene este framework. Si colocáramos en uno de ellos una clase que retuviera la entrega del mensaje a un objeto servidor, podríamos comprobar si efectivamente el cliente sigue bloqueado. Para comprobarlo, vamos a usar un Message Inspector.

Inyectar un Message Inspector

Vamos a partir de un nuevo proyecto de librería WCF. La estructura que nos genera Visual Studio nos vale; dejaremos únicamente un método que no devuelva nada, como se ve a continuación.

/// <summary>
/// One Way Service.
/// </summary>
[ServiceContract]
public interface IService
{
    /// <summary>
    /// OneWay method.
    /// </summary>
    /// <param name="value">Value to send.</param>
    [OperationContract(IsOneWay=true)]
    void SendData(int value);
}

La implementación de este contrato de servicio no tiene demasiada importancia, con saber que deberíamos colocar un breakpoint al comienzo del método para saber en qué momento es realmente invocado, es suficiente.

Vamos ahora a empezar a crear el Message Inspector. Crearemos una clase que implemente la interfaz IDispatchMessageInspector e implementaremos uno de sus métodos.

/// <summary>
/// My message inspector class.
/// </summary>
public class MyMessageInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(
        ref System.ServiceModel.Channels.Message request, 
        System.ServiceModel.IClientChannel channel, 
        System.ServiceModel.InstanceContext instanceContext)
    {
        // Crear un "replicador" de mensajes y usarlo para obtener una copia del mismo
        MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue);
        string messageContent = buffer.CreateMessage().GetReaderAtBodyContents().ReadOuterXml();
        System.Diagnostics.Debug.WriteLine(messageContent);
        
        // Asignar una copia sin leer del mensaje en request, para que otros 
        // componentes del pipeline de WCF puedan leerlo sin fallar.
        request = buffer.CreateMessage();

        // Devolver null como resultado, que será lo que reciba el metodo BeforeSendReply
        // en el parámetro correlationState
        return null;
    }

    public void BeforeSendReply(
        ref System.ServiceModel.Channels.Message reply, 
        object correlationState)
    {
    }
}

En cuanto al método AfterReceiveRequest, se ejecuta en el camino de subida del mensaje desde la red hacia el objeto que va a servir esta petición. Queremos mostrarlo en la consola, para lo cual vamos a crear un buffer y generar una copia del mismo. Es importante usar el buffer en primer lugar, puesto que un mensaje no puede leerse dos veces. Si lo hiciéramos con el parámetro request, luego no podría leerse otra vez por otros componentes del pipeline de WC y cada petición fallaría irremediablemente.

En cuanto al método BeforeSendReply, no queremos hacer nada especial con él así que simplemente dejamos pasar el mensaje reply, sin leerlo para no tener el problema antes comentado de lecturas. Como curiosidad, decir que lo que devuelve el método AfterReceiveRequest lo recibe el método BeforeSendReply en su parámetro correlationState, para poder relacionar ambas llamadas entre sí.

Ahora que ya tenemos el Inspector, es el momento de modificar la configuración de WCF para usarlo. Para ello tenemos que crear una clase que modifique el comportamiento del endpoint sobre el que vamos a escuchar. Para ello, tendremos que implementar la interfaz IEndpointBehavior.

/// <summary>
/// Custom endpoint behavior.
/// </summary>
public class MyEndpointBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, 
        System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, 
        System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
        System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyMessageInspector());
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

De esta interfaz, como se puede observar, sólo nos interesa implementar el método ApplyDispatchBehavior, puesto que nuestro MessageInspector sólo va a actuar en el lado del servidor. Es importante no dejarnos ningún método con su implementación por defecto, puesto que tirar una NotImplementedException en cualquiera de ello tendría consecuencias indeseadas.

Ya sólo nos queda una última clase para tener todo el código listo. Este comportamiento personalizado para el endpoint necesitamos configurarlo de algún modo. La forma más limpia siempre es a través del fichero app.config, pero para ello necesitamos representar este comportamiento personalizable como un elemento de configuración. Esto podemos hacerlo creando una clase que herede de BehaviorExtensionElement, clase que por otra parte cargará nuestro comportamiento custom para el endpoint. El código sería algo así:

/// <summary>
/// Custom behavior extension element for the custom endpoint behavior.
/// </summary>
public class MyBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(MyEndpointBehavior); }
    }

    protected override object CreateBehavior()
    {
        return new MyEndpointBehavior();
    }
}

¿Sencillo, verdad? La clase simplemente representa al comportamiento personalizado que deseamos añadir como configuración. ¿Y ahora esto cómo se usaría? Primero se cargaría la clase que acabamos de definir como una extensión de los behaviors, y posteriormente se definiría como nueva configuración para un endpoint. Finalmente se cargaría en el endpoint correspondiente esta nueva configuración. Sin embargo, con las novedades que trae WCF4 en la configuración, este último paso podemos saltárnoslo, sabiendo que a partir de ese momento todos los endpoints van a tener esa extensión cargada.

  <system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="myCustomEndpointBehavior" 
             type="OneWayService.MyBehaviorExtensionElement, OneWayService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>      
    </extensions>
    <behaviors>
      <endpointBehaviors>
        <behavior>
          <myCustomEndpointBehavior/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Como se puede ver, en primer lugar se carga la extensión y, a continuación, se define como parte del comportamiento de los endpoints. Al no darle nombre al endpointBehavior, se cargará para todos los endpoints existentes. Por último, he activado la generación del fichero WSDL para poder generar un proxy.

Probando el inspector

Bien, suponiendo que ya tenemos el servicio arriba y un proyecto de cliente correctamente creado. Tras añadir la referencia al servicio, podemos escribir algo como lo siguiente para ver si realmente nuestro cliente se bloquea o no.

using (ServiceReference.ServiceClient proxy = new ServiceReference.ServiceClient())
{
    Console.WriteLine("Calling the remote server...");
    proxy.SendData(int.MaxValue);
    Console.WriteLine("Remote server called...");
    Console.ReadLine();
}

Con esto, es momento de lanzar el cliente. Deberemos tener breakpoints tanto en el método que hemos implementado del inspector, como en la clase que implementa el servicio. Si todo va bien veremos la siguiente secuencia de pasos:

  1. El cliente invoca al servicio usando el proxy.
  2. Salta el breakpoint en el MessageInspector. Si ejecutamos cualquiera de las instrucciones del inspector, con ejecución paso a paso, veremos que inmediatamente salta el breakpoint del cliente. Por lo tanto, no se está bloqueando.
  3. El servidor se queda esperando a que sigamos la ejecución, para llegar al objeto servidor, donde el otro breakpoint debería saltar.

He probado el mismo código con las mismas condiciones (el servicio desplegado en IIS 7.0), pero con el framework anterior y el resultado es el mismo. Parece, por tanto, que OneWay sí es realmente un patrón “Fire-and-Forget” y no bloquea a los clientes

 

Bibliografía:

Microsoft .NET Framework 3.5 – Windows Communication Foundation. Self-Paced Training Kit

Descargas: