Introducción de un desarrollador a Windows Communication Foundation 4
Aaron Skonnard, Pluralsight
Original: noviembre de 2009
Actualizado a RTM: abril de 2010
Visión general
.NET 4 incluye algunas características nuevas atractivas y mejoras bienvenidas en el área de Windows Communication Foundation (WCF). Estas mejoras de WCF se centran principalmente en simplificar la experiencia del desarrollador, habilitar más escenarios de comunicación y proporcionar una integración enriquecida con Windows Workflow Foundation (WF) haciendo que "servicios de flujo de trabajo" un ciudadano de primera clase avance.
La buena noticia es que la mayoría de los cambios de WCF 4 se centran en facilitar los escenarios comunes actuales y facilitar nuevos escenarios de comunicación y estilos de desarrollo. Como resultado, mover las soluciones de WCF existentes a .NET 4 será bastante fluida en términos de migración. A continuación, simplemente decide qué características de WCF 4 desea aprovechar en sus soluciones en el futuro. El resto de este documento le presenta cada una de las nuevas áreas de características de WCF 4 y muestra cómo funcionan.
Novedades de WCF 4
WCF 4 incluye una amplia gama de características específicas, pero en la figura 1 se describen las principales áreas de características en las que nos centraremos en todo el documento siguiente. Estas áreas de características resumen la mayoría de las novedades de WCF 4 y resaltan las oportunidades de nivel superior que ofrece esta versión de .NET Framework.
Figura 1: Áreas de características de WCF 4
Área de características | Descripción |
---|---|
Configuración simplificada |
Simplificación de la sección de configuración de WCF a través de la compatibilidad con puntos de conexión predeterminados, configuraciones de enlace y comportamiento. Estos cambios permiten hospedar servicios sin configuración, lo que simplifica considerablemente la experiencia del desarrollador para los escenarios de WCF más comunes. |
Descubrimiento |
Nueva compatibilidad con marcos para comportamientos de detección de servicios administrados y ad hoc, que se ajustan al protocolo estándar WS-Discovery. |
Servicio de enrutamiento |
Nueva compatibilidad con el marco de trabajo para un servicio de enrutamiento configurable que puede usar en las soluciones wcF. Proporciona características para el enrutamiento basado en contenido, el protocolo de puente y el control de errores. |
Mejoras de REST |
Mejoras en los servicios WebHttp de WCF con algunas características y herramientas adicionales que simplifican el desarrollo del servicio REST. |
Servicios de flujo de trabajo |
Compatibilidad con el marco enriquecido para integrar WCF con WF para implementar servicios declarativos de flujo de trabajo de larga duración. Este nuevo modelo de programación ofrece los mejores marcos de trabajo (WCF & WF). |
Una vez que hayamos terminado de cubrir estas principales áreas de características, analizaremos brevemente algunas de las características de WCF de nivel inferior más avanzadas que vienen con .NET 4, lo que incluye cosas como funcionalidades de resolución de tipos mejoradas, compatibilidad con colas con consumidores competidores ("contexto de recepción"), compatibilidad con datos binarios desencapsulados a través de un codificador de secuencias de bytes y compatibilidad con el seguimiento basado en ETW de alto rendimiento.
Cuando hayamos terminado, verá que WCF 4 resulta más fácil de usar y proporciona compatibilidad más integrada con algunos de los escenarios y estilos de desarrollo más comunes de hoy en día.
Configuración simplificada
El modelo de programación unificado que ofrece WCF en 3.x es una bendición y una maldición: simplifica la escritura de la lógica del servicio para una variedad de escenarios de comunicación diferentes, pero también aumenta la complejidad en el lado de la configuración de las cosas porque proporciona tantas opciones de comunicaciones subyacentes diferentes que se ven obligados a comprender antes de empezar.
La realidad es que la configuración de WCF suele ser el área más costosa de usar WCF en la práctica en la actualidad y gran parte de esa complejidad llega al personal de TI/operaciones que no están preparados para tratarlo.
Dada esta realidad, cuando se considera la complejidad neta del uso de WCF 3.x, uno podría concluir razonablemente que es más difícil de usar que su predecesor ASP.NET servicios web (ASMX). Con ASMX, pudo definir una operación [WebMethod] y el tiempo de ejecución proporcionó automáticamente una configuración predeterminada para las comunicaciones subyacentes. Al pasar a WCF 3.x, por otro lado, los desarrolladores tienen que saber lo suficiente sobre las distintas opciones de configuración de WCF para definir al menos un punto de conexión. Y el número abrumador de opciones de configuración a menudo asusta a algunos desarrolladores.
En un esfuerzo por hacer que la experiencia general de WCF sea tan fácil como ASMX, WCF 4 viene con un nuevo modelo de "configuración predeterminada" que elimina completamente la necesidad de cualquier configuración de WCF. Si no proporciona ninguna configuración de WCF para un servicio determinado, el entorno de ejecución de WCF 4 configura automáticamente el servicio con algunos puntos de conexión estándar y configuraciones de enlace y comportamiento predeterminados. Esto facilita mucho la puesta en marcha de un servicio WCF, especialmente para aquellos que no están familiarizados con las distintas opciones de configuración de WCF y están encantados de aceptar los valores predeterminados, al menos para empezar.
Puntos de conexión predeterminados
Con WCF 3.x, si intenta hospedar un servicio sin ningún punto de conexión configurado, la instancia de ServiceHost producirá una excepción que le informará de que necesita configurar al menos un punto de conexión. Con WCF 4, este ya no es el caso porque el tiempo de ejecución agrega automáticamente uno o varios "puntos de conexión predeterminados" para usted, lo que hace que el servicio se pueda usar sin ninguna configuración.
Así es como funciona. Cuando la aplicación host llama a Open en la instancia de ServiceHost, compila la descripción del servicio interno desde el archivo de configuración de la aplicación junto con todo lo que la aplicación host puede haber configurado explícitamente y si el número de puntos de conexión configurados sigue siendo cero, llama a AddDefaultEndpoints, un nuevo método público encontrado en la clase ServiceHost. Este método agrega uno o varios puntos de conexión a la descripción del servicio en función de las direcciones base del servicio (en escenarios de IIS, esta es la dirección .svc). Dado que el método es público, también puede llamarlo directamente en escenarios de hospedaje personalizados.
Para ser precisos, la implementación de AddDefaultEndpoints agrega un punto de conexión predeterminado por dirección base para cada contrato de servicio implementado por el servicio. Por ejemplo, si el servicio implementa dos contratos de servicio y configura el host con una sola dirección base, AddDefaultEndpoints configurará el servicio con dos puntos de conexión predeterminados (uno para cada contrato de servicio). Sin embargo, si el servicio implementa dos contratos de servicio y el host está configurado con dos direcciones base (una para HTTP y otra para TCP), AddDefaultEndpoints configurará el servicio con cuatro puntos de conexión predeterminados.
Echemos un vistazo a un ejemplo completo para ilustrar el funcionamiento de ho'w. Supongamos que tiene los siguientes contratos de servicio wcF y la siguiente implementación de servicio:
[ServiceContract]
public interface IHello
{
[OperationContract]
void SayHello(nombre de cadena);
}
[ServiceContract]
interfaz pública IGoodbye
{
[OperationContract]
void SayGoodbye(nombre de cadena);
}
public class GreetingService : IHello, IGoodbye // service implementa ambos contratos
{
public void SayHello(string name)
{
Console.WriteLine("Hello {0}", name);
}
public void SayGoodbye(string name)
{
Console.WriteLine("Goodbye {0}", name);
}
}
Con WCF 4, ahora puede usar ServiceHost para hospedar el servicio GreetingService sin ninguna configuración de aplicación. Al usar ServiceHost en escenarios de hospedaje personalizados, deberá especificar una o varias direcciones base que se van a usar. A continuación se muestra cómo hospedar GreetingService en una aplicación de consola y, de nuevo, puede suponer que no hay ningún archivo app.config asociado con este programa:
Clase Program
{
static void Main(string[] args)
{
El host está configurado con dos direcciones base, una para HTTP y otra para TCP.
Host de ServiceHost = nuevo ServiceHost(typeof(GreetingService),
new Uri("https://localhost:8080/greeting"),
new Uri("net.tcp://localhost:8081/greeting"));
anfitrión. Open();
foreach (ServiceEndpoint se in host). Description.Endpoints)
Console.WriteLine("A: {0}, B: {1}, C: {2}",
se. Dirección, se.Binding.Name, se.Contract.Name);
Console.WriteLine("Presione <Entrar> para detener el servicio".);
Console.ReadLine();
anfitrión. Close();
}
}
En este ejemplo se configura ServiceHost con dos direcciones base: una para HTTP y otra para TCP. Al ejecutar este programa, verá cuatro puntos de conexión impresos en la ventana de consola, como se muestra en la figura 2. Obtendrá dos para la dirección base HTTP, una por contrato y dos para la dirección base TCP, de nuevo una por contrato. Esto se proporciona en segundo plano por la instancia de ServiceHost.
Figura 2: Puntos de conexión predeterminados mostrados en la ventana de consola
Observe cómo WCF elige usar BasicHttpBinding para los puntos de conexión HTTP predeterminados y NetTcpBinding para los puntos de conexión TCP predeterminados. Te mostraré cómo cambiar estos valores predeterminados en breve.
Recuerde que este comportamiento de punto de conexión predeterminado solo se inicia cuando el servicio no se ha configurado con ningún punto de conexión. Si cambio la aplicación de consola para configurar el servicio con al menos un punto de conexión, ya no verá ninguno de estos puntos de conexión predeterminados en la salida. Para ilustrar esto, simplemente agregaré la siguiente línea de código que llama a AddServiceEndpoint después de construir la instancia de ServiceHost:
...
Host de ServiceHost = nuevo ServiceHost(typeof(GreetingService),
new Uri("https://localhost:8080/greeting"),
new Uri("net.tcp://localhost:8081/greeting"));
anfitrión. AddServiceEndpoint(typeof(IHello), new WSHttpBinding(), "myendpoint");
...
Si ejecuta la aplicación de consola con esta línea de código insertada, observará que solo aparece un único punto de conexión en la salida: el que hemos configurado manualmente en el código anterior (vea la figura 3).
Figura 3: Salida de la consola después de configurar el host con un único punto de conexión
Sin embargo, siempre puede llamar a AddDefaultEndpoints usted mismo si desea agregar el conjunto de puntos de conexión predeterminados junto con sus propios. En el ejemplo de código siguiente se muestra cómo hacerlo:
...
Host de ServiceHost = nuevo ServiceHost(typeof(GreetingService),
new Uri("https://localhost:8080/greeting"),
new Uri("net.tcp://localhost:8081/greeting"));
anfitrión. AddServiceEndpoint(typeof(IHello), new WSHttpBinding(), "myendpoint");
anfitrión. AddDefaultEndpoints();
...
Si vuelve a ejecutar la aplicación de consola con este cambio, verá cinco puntos de conexión mostrados en la ventana de la consola: el que configuré manualmente junto con los cuatro puntos de conexión predeterminados (consulte la figura 4).
Figura 4: Salida de la consola después de llamar manualmente a AddDefaultEndpoints
Ahora que entendemos el algoritmo y la mecánica para agregar puntos de conexión predeterminados a los servicios en tiempo de ejecución, la siguiente pregunta es cómo decide WCF qué enlace usar para una dirección basada en particular?
Asignación de protocolo predeterminada
La respuesta a esta pregunta es sencilla. WCF define una asignación de protocolo predeterminada entre esquemas de protocolo de transporte (por ejemplo, http, net.tcp, net.pipe, etc.) y los enlaces WCF integrados. La asignación de protocolo predeterminada se encuentra en el archivo .comments de .NET 4 machine.configy tiene este aspecto:
<system.serviceModel>
<protocolMapping>
<add scheme="http" binding="basicHttpBinding" bindingConfiguration="" />
<add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration=""/>
<add scheme="net.pipe" binding="netNamedPipeBinding" bindingConfiguration=""/>
<add scheme="net.msmq" binding="netMsmqBinding" bindingConfiguration=""/>
</protocolMapping>
...
Puede invalidar estas asignaciones en el nivel de máquina agregando esta sección a machine.config y modificando la asignación de cada esquema de protocolo. O bien, si solo desea invalidarlo dentro del ámbito de una aplicación, puede invalidar esta sección en el archivo de configuración de la aplicación o web.
Por ejemplo, si su organización se centra principalmente en la creación de servicios RESTful con WCF, podría tener sentido cambiar el enlace predeterminado del esquema de protocolo "http" a WebHttpBinding. En el ejemplo siguiente se muestra cómo hacerlo dentro de un archivo de configuración de la aplicación:
> de configuración de <
<system.serviceModel>
<protocolMapping>
<add scheme="http" binding="webHttpBinding"/>
</protocolMapping>
</system.serviceModel>
</configuration>
Ahora, si volver a ejecutar la aplicación de consola que se mostró anteriormente con este app.config en su lugar, los dos puntos de conexión basados en HTTP predeterminados ahora mostrarán que usan WebHttpBinding (consulte la figura 5).
Figura 5: Salida de la consola después de invalidar la asignación de protocolo HTTP predeterminada
Una vez que WCF determina qué enlace se va a usar a través de la tabla de asignación de protocolos, usa la configuración de enlace predeterminada al configurar el punto de conexión predeterminado. Si no está satisfecho con los valores predeterminados de enlace integrados, también puede invalidar la configuración predeterminada de un enlace determinado.
Configuraciones de enlace predeterminadas
Cada enlace WCF incluye una configuración predeterminada que se usa a menos que la aplicación host invalide explícitamente para un punto de conexión determinado. Cada instancia de enlace que use siempre incluye los valores predeterminados integrados a menos que decida invalidar aplicando una configuración de enlace explícita.
En WCF 3.x, debe definir una configuración de enlace con nombre que se puede aplicar a las definiciones de punto de conexión mediante el atributo bindingConfiguration. La mecánica de hacerlo correctamente es complicada y propensa a errores. El siguiente archivo de configuración muestra un ejemplo típico:
> de configuración de <
<system.serviceModel>
<enlaces>
< > basicHttpBinding
<binding name="BasicWithMtom" messageEncoding="Mtom"/>
</basicHttpBinding>
</bindings>
> servicios de <
<service name="GreetingService">
<endpoint address="mtom" binding="basicHttpBinding"
bindingConfiguration="BasicWithMtom"
contract="IHello"/>
</service>
</services>
</system.serviceModel>
</configuration>
En el ejemplo anterior, la configuración de enlace "BasicWithMtom" invalida los valores predeterminados de BasicHttpBinding cambiando la codificación de mensajes a MTOM. Sin embargo, esta configuración de enlace solo surte efecto cuando se aplica a un punto de conexión específico a través del atributo "bindingConfiguration", este es el paso que a menudo elude al personal de operaciones y desarrolladores, lo que provoca problemas de configuración.
Con WCF 4, ahora puede definir configuraciones de enlace predeterminadas si simplemente omite el nombre de configuración de enlace al definir la nueva configuración. A continuación, WCF usará esa configuración predeterminada para los puntos de conexión que usen ese enlace que no tenga una configuración de enlace explícita establecida en ellos.
Por ejemplo, si agregamos el siguiente archivo app.config a la aplicación de consola que se mostró anteriormente, los dos puntos de conexión HTTP predeterminados seleccionarán esta configuración Predeterminada BasicHttpBinding, que habilita MTOM:
> de configuración de <
<system.serviceModel>
<enlaces>
< > basicHttpBinding
<mensaje de enlaceEncoding="Mtom"/><!-- observe que no hay ningún atributo name:>
</basicHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
Por supuesto, también puede agregar estas configuraciones de enlace predeterminadas a machine.config si quiere que surtan efecto en todos los servicios que se ejecutan en la máquina o puede definirlas en una aplicación por aplicación agregando las configuraciones de enlace predeterminadas dentro del archivo de configuración de la aplicación.
Esta característica proporciona un mecanismo sencillo para definir un conjunto estándar de valores predeterminados de enlace que puede usar en todos los servicios sin imponer las complejidades de las configuraciones de enlace a otros desarrolladores o al personal de TI/operaciones. Simplemente pueden elegir el enlace adecuado y asegurarse de que el entorno de hospedaje proporcionará la configuración predeterminada adecuada.
Además de las configuraciones de enlace predeterminadas, lo otro aspecto que se debe tener en cuenta para los servicios y los puntos de conexión es cuál debe ser su configuración de comportamiento predeterminada.
Configuraciones de comportamiento predeterminadas
WCF 4 también permite definir configuraciones de comportamiento predeterminadas para servicios y puntos de conexión, lo que puede simplificar las cosas cuando desea compartir una configuración de comportamiento predeterminada estándar en todos los servicios o puntos de conexión que se ejecutan en una máquina o dentro de una solución.
En WCF 3.x, debe definir configuraciones de comportamiento con nombre que se aplican explícitamente a los servicios y puntos de conexión mediante el atributo "behaviorConfiguration". Con WCF 4, puede definir configuraciones de comportamiento predeterminadas si omite el nombre en la definición de configuración. Si agrega estos comportamientos predeterminados a machine.config, se aplicarán a todos los servicios o puntos de conexión hospedados en la máquina. Si los agrega a app.config, solo surtirán efecto dentro del ámbito de la aplicación host. Este es un ejemplo:
> de configuración de <
<system.serviceModel>
<comportamientos>
< > serviceBehaviors
<comportamiento><!-- observe que no hay ningún atributo de nombre:>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
< > /serviceBehaviors
</behaviors>
</system.serviceModel>
</configuration>
En este ejemplo se activan los metadatos del servicio para cualquier servicio que no incluya una configuración de comportamiento explícita. Si agregamos esta configuración de comportamiento predeterminada al archivo app.config de la aplicación de consola se muestra anteriormente y ejecutamos la aplicación de nuevo, podemos ir a la dirección HTTP base para recuperar la página de ayuda del servicio y la definición de WSDL del servicio (vea la figura 6).
Figura 6: Exploración a los metadatos del servicio habilitados por la configuración de comportamiento predeterminada
Otra nueva característica de WCF 4 es que las configuraciones de comportamiento ahora admiten un modelo de herencia. Si una aplicación define una configuración de comportamiento con el mismo nombre que uno ya definido en machine.config, la configuración de comportamiento específica de la aplicación se combinará con la configuración de toda la máquina, agregando cualquier comportamiento adicional a la configuración del comportamiento compuesto derivado.
Puntos de conexión estándar
Relacionado con los puntos de conexión predeterminados es otra nueva característica wcF 4 conocida como "puntos de conexión estándar". Puede considerar un punto de conexión estándar como una definición de punto de conexión preconfigurada común integrada en el marco WCF 4 que simplemente puede usar. Los puntos de conexión estándar definen una configuración de punto de conexión "estándar" que normalmente no cambia, aunque puede si necesita como verá en breve.
En la figura 7 se describen los puntos de conexión estándar que se incluyen con WCF 4. Proporcionan definiciones de punto de conexión estándar para algunas de las características y escenarios de comunicación de WCF 4 más comunes. Por ejemplo, en el caso de un punto de conexión MEX, siempre tendrá que especificar IMetadataExchange para el contrato de servicio y es más probable que elija HTTP. Por lo tanto, en lugar de forzarlo siempre a hacerlo manualmente, WCF proporciona una definición de punto de conexión estándar para el intercambio de metdata denominado "mexEndpoint" que es fácil de usar.
Figura 7: Puntos de conexión estándar en WCF 4
Nombre del punto de conexión estándar | Descripción |
---|---|
mexEndpoint |
Define un punto de conexión estándar para MEX configurado con IMetadataExchange para el contrato de servicio, mexHttpBinding como enlace predeterminado (puede cambiarlo) y una dirección vacía. |
dynamicEndpoint |
Define un punto de conexión estándar configurado para usar la detección de WCF dentro de una aplicación cliente WCF. Cuando se usa este punto de conexión estándar, no se requiere una dirección porque durante la primera llamada, el cliente consultará un punto de conexión de servicio que coincida con el contrato especificado y se conectará automáticamente a él. De forma predeterminada, la consulta de detección se envía a través de UDP de multidifusión, pero puede especificar el enlace de detección y los criterios de búsqueda que se usarán cuando sea necesario. |
discoveryEndpoint |
Define un punto de conexión estándar preconfigurado para las operaciones de detección dentro de una aplicación cliente. El usuario debe especificar la dirección y el enlace al usar este punto de conexión estándar. |
udpDiscoveryEndpoint |
Define un punto de conexión estándar preconfigurado para las operaciones de detección dentro de una aplicación cliente mediante el enlace UDP en una dirección de multidifusión. Deriva de DiscoveryEndpoint. |
announcementEndpoint |
Define un punto de conexión estándar preconfigurado para la funcionalidad de anuncio de detección. El usuario debe especificar la dirección y el enlace al usar este punto de conexión estándar. |
udpAnnouncementEndpoint |
Define un punto de conexión estándar preconfigurado para la funcionalidad de anuncio a través de un enlace UDP en una dirección de multidifusión. Este punto de conexión se deriva de announcementEndpoint. |
workflowControlEndpoint |
Define un punto de conexión estándar para controlar la ejecución de instancias de flujo de trabajo (crear, ejecutar, suspender, finalizar, etc.). |
webHttpEndpoint |
Define un punto de conexión estándar configurado con WebHttpBinding y WebHttpBehavior. Use para exponer servicios REST. |
webScriptEndpoint |
Define un punto de conexión estándar configurado con WebHttpBinding y WebScriptEnablingBehavior. Use para exponer los servicios de Ajax. |
Puede aprovechar cualquiera de estos puntos de conexión estándar en sus propias configuraciones de servicio simplemente haciendo referencia a ellos por nombre. El <punto de conexión> ahora incluye un atributo "kind" que puede usar para especificar el nombre de un punto de conexión estándar. Por ejemplo, en el ejemplo siguiente se configura GreetingService con un punto de conexión MEX aprovechando la definición estándar "mexEndpoint":
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="GreetingService">
<endpoint kind="basicHttpBinding" contract="IHello"/>
<endpoint kind="mexEndpoint" address="mex" />
</service>
</services>
</system.serviceModel>
</configuration>
Aunque los puntos de conexión estándar le protegen de la mayoría de los detalles de configuración (por ejemplo, con el mexEndpoint que no tenía que especificar el enlace o contrato), puede haber ocasiones en las que quiera usarlas, pero es necesario configurar las definiciones de punto de conexión estándar de forma un poco diferente.
Cuando tenga que hacerlo, puede usar la sección <standardEndpoints> e invalidar la configuración del punto de conexión para el punto de conexión estándar. A continuación, puede hacer referencia a esa configuración al definir un nuevo punto de conexión de <> a través del atributo endpointConfiguration, como se muestra aquí:
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="GreetingService">
<endpoint binding="basicHttpBinding" contract="IHello"/>
<endpoint kind="udpDiscoveryEndpoint" endpointConfiguration="D11"/>
</service>
</services>
> standardEndpoints de <
<udpDiscoveryEndpoint>
<standardEndpoint name="D11" discoveryVersion="WSDiscovery11"/>
</udpDiscoveryEndpoint>
</standardEndpoints>
<comportamientos>
< > serviceBehaviors
< > de comportamiento
<serviceDiscovery/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
< > /serviceBehaviors
</behaviors>
</system.serviceModel>
</configuration>
En este ejemplo se cambia la versión predeterminada de WS-Discovery para el punto de conexión estándar denominado "udpDiscoveryEndpoint" (hablaremos más sobre la detección de servicios en breve).
Simplificación del hospedaje de IIS/ASP.NET
Dadas estas nuevas características para los puntos de conexión predeterminados, las configuraciones de enlace predeterminadas y las configuraciones de comportamiento predeterminadas, el hospedaje en IIS/ASP.NET resulta mucho más fácil en WCF 4. ASP.NET desarrolladores que se usan para trabajar con servicios ASMX ahora pueden definir los servicios WCF que son tan simples de la naturaleza.
De hecho, consulte lo sencillo que es la siguiente definición de servicio WCF:
<!-- HelloWorld.svc:>
<%@ ServiceHost Language="C#" Debug="true" Service="HelloWorldService
CodeBehind="~/App_Code/HelloWorldService.cs" %>
[ServiceContract]
clase pública HelloWorldService
{
[OperationContract]
public string HelloWorld()
{
devuelve "hello, world";
}
}
Esta es la forma más sencilla de definición de servicio WCF porque no usamos una definición de interfaz independiente para definir el contrato de servicio y todo se define en un archivo, HelloWorld.svc (nota: No recomiendo este enfoque, simplemente teniendo en cuenta que es posible dibujar una comparación con ASMX). Esto debería parecerse mucho a los servicios ASMX típicos, la diferencia principal es que los nombres de atributo que se usan en la clase de servicio (por ejemplo, [WebService] y [WebMethod]). Definitivamente hay menos piezas móviles.
Con las nuevas características de WCF 4 descritas en la sección anterior, ahora puede ir a HelloWorld.svc sin ninguna configuración de WCF adicional y la lógica de activación de WCF creará la instancia de ServiceHost en segundo plano y la configurará con un único punto de conexión HTTP predeterminado. Y si ha agregado un comportamiento de servicio predeterminado al archivo de machine.config habilitación de los metadatos del servicio, verá la página de ayuda de WCF y el vínculo a la definición de WSDL cuando vaya a HelloWorld.svc (consulte la figura 8).
Figura 8: página de ayuda helloWorldService
Si no ha habilitado los metadatos de servicio en toda la máquina, puede habilitarlos en la aplicación web agregando la siguiente configuración de comportamiento predeterminada al archivo de web.config:
...
<system.serviceModel>
<comportamientos>
< > serviceBehaviors
<comportamiento><!-- observe que no hay ningún atributo de nombre:>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
< > /serviceBehaviors
</behaviors>
</system.serviceModel>
...
También puede cambiar otras opciones predeterminadas siguiendo los procedimientos descritos en las secciones anteriores. Por ejemplo, puede cambiar la asignación de protocolo predeterminada, agregar configuraciones de enlace predeterminadas o configuraciones de comportamiento predeterminadas adicionales. Si el servicio implementa más de un contrato de servicio, la instancia de ServiceHost resultante se configurará con un punto de conexión HTTP por contrato.
Por ejemplo, supongamos que hospedamos greetingService (anterior) a través del archivo .svc que se muestra aquí:
<!-- GreetingService.svc:>
<%@ServiceHost Service="GreetingService"%>
Dada nuestra definición para GreetingService, la primera vez que vaya a GreetingService.svc, la lógica de activación de WCF creará la instancia de ServiceHost y agregará dos puntos de conexión HTTP predeterminados para el tipo GreetingService (uno para cada contrato de servicio). Para comprobarlo, vaya a la definición de WSDL y encontrará dos elementos <puerto> dentro del elemento> del servicio <.
En general, estas simplificaciones de configuración de WCF deberían facilitar mucho a los desarrolladores de ASP.NET poner en marcha los servicios WCF dentro de sus aplicaciones web, y aporta el caso más sencillo a la experiencia que los desarrolladores se usaron con ASP.NET servicios web.
Activación sin archivos
Aunque los archivos .svc facilitan la exposición de servicios WCF, un enfoque aún más sencillo sería definir puntos de conexión de activación virtual dentro de Web.config, lo que elimina la necesidad de archivos .svc por completo.
En WCF 4, puede definir puntos de conexión de activación de servicio virtual que se asignan a los tipos de servicio en Web.config. Esto permite activar los servicios WCF sin tener que mantener archivos .svc físicos (a.k.a. "activación sin archivo"). En el ejemplo siguiente se muestra cómo configurar un punto de conexión de activación:
> de configuración de <
<system.serviceModel>
< > serviceHostingEnvironment
<serviceActivations>
<agregar relativeAddress="Greeting.svc" service="GreetingService"/>
</serviceActivations>
< > /serviceHostingEnvironment
</system.serviceModel>
</configuration>
Con esto en su lugar, ahora es posible activar GreetingService mediante una ruta de acceso relativa de "Greeting.svc" (relativa a la dirección base de la aplicación web). Para ilustrar esto, he creado una aplicación IIS en mi máquina denominada "GreetingSite", que he asignado al grupo de aplicaciones "ASP.NET v4.0" y la he asignado al directorio del proyecto GreetingService que contiene el web.config mostrado anteriormente. Ahora puedo ir simplemente a https://localhost/GreetingSite/Greeting.svc sin tener realmente un archivo .svc físico en el disco. En la figura 9 se muestra el aspecto de esto en el explorador.
Figura 9: Ejemplo de activación sin archivos
Descubrimiento
La siguiente característica principal de WCF 4 que analizaremos es la detección de servicios. En algunos entornos especializados orientados a servicios, hay servicios cuya ubicación en tiempo de ejecución es dinámica y cambia constantemente. Por ejemplo, considere los entornos en los que diferentes tipos de dispositivos habilitados para el servicio se unen constantemente y dejan la red como parte de la solución empresarial general. Tratar con esta realidad requiere que los clientes detecten dinámicamente la ubicación en tiempo de ejecución de los puntos de conexión de servicio.
WS-Discovery es una especificación OASIS que define un protocolo basado en SOAP para detectar dinámicamente la ubicación de los puntos de conexión de servicio en tiempo de ejecución. El protocolo permite a los clientes sondear los puntos de conexión de servicio que coinciden con determinados criterios para recuperar una lista de candidatos adecuados. A continuación, un cliente puede elegir un punto de conexión específico de la lista detectada y usar su dirección actual del punto de conexión en tiempo de ejecución.
WS-Discovery define dos modos principales de operación: modo ad hoc y modo administrado. En modo ad hoc, los clientes sondea los servicios mediante el envío de mensajes de multidifusión. El marco proporciona un mecanismo de multidifusión UDP para este modo ad hoc. Los servicios que coinciden con el sondeo responden directamente al cliente. Para minimizar la necesidad de sondeo de clientes, los servicios también pueden "anunciarse" al unirse o salir de la red enviando un mensaje de multidifusión a los clientes que pueden estar "escuchando". La detección ad hoc está limitada por el protocolo usado para la multidifusión de mensajes, en el caso de UDP solo los servicios que escuchan en la subred local podrán recibir los mensajes.
Con la detección de servicios administrados, se proporciona un proxy de detección en la red que "administra" los puntos de conexión de servicio detectables. Los clientes hablan directamente con el proxy de detección para buscar servicios en función de los criterios de sondeo. El proxy de detección necesita un repositorio de servicios que pueda coincidir con la consulta. Cómo se rellena el proxy con esta información es un detalle de implementación. Los servidores proxy de detección pueden conectarse fácilmente a un repositorio de servicios exisiting, se pueden configurar previamente con una lista de puntos de conexión o un proxy de detección incluso puede escuchar anuncios para actualizar su caché. En modo administrado, los anuncios se pueden unidifusión directamente a un destinatario, posiblemente mediante un proxy de detección.
El marco de .NET 4.0 proporciona las clases base que necesita para implementar su propio proxy de detección. Las clases base abstraen los detalles del protocolo de detección para que pueda centrarse simplemente en la lógica que desea que contenga el proxy de detección. Por ejemplo, solo tiene que definir lo que hará el proxy de detección en respuesta a un mensaje de sondeo, mensajes de anuncio y resolver mensajes.
WCF 4 proporciona una implementación completa del protocolo WS-Discovery y proporciona compatibilidad con los modos de detección ad hoc y administrados. Echaremos un vistazo breve a cada uno de estos siguientes.
Detección de servicios simples
La manera más fácil de habilitar la detección de servicios es a través del modo ad hoc. WCF facilita la habilitación de la detección de servicios dentro de las aplicaciones host de servicio proporcionando algunos puntos de conexión de detección estándar y un comportamiento de detección de servicios. Para configurar el servicio para la detección, simplemente agregue el punto de conexión "udpDiscoveryEndpoint" estándar y, a continuación, habilite el comportamiento de <serviceDiscovery> en el servicio.
Este es un ejemplo completo que ilustra cómo hacerlo:
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="CalculatorService">
<endpoint binding="wsHttpBinding" contract="ICalculatorService" />
<!-- agregar un punto de conexión de detección de UDP estándar:>
<endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
</service>
</services>
<comportamientos>
< > serviceBehaviors
< > de comportamiento
<serviceDiscovery/><!-- habilitar el comportamiento de detección de servicios:>
</behavior>
< > /serviceBehaviors
</behaviors>
</system.serviceModel>
</configuration>
Al hacerlo, el servicio se puede detectar a través de UDP en la subred local. A continuación, los clientes pueden aprovechar WS-Discovery en tiempo de ejecución para "detectar" la dirección real del servicio en ejecución. WCF 4 facilita la realización de los clientes a través del punto de conexión estándar dynamicEndpoint.
Simplemente tome el punto de conexión de cliente existente que estaba usando para conectarse al servicio, quite la dirección y agregue una etiqueta kind="dynamicEndpoint".
> de configuración de <
<system.serviceModel>
> de cliente de <
punto de conexión de <
name="calculatorEndpoint"
kind="dynamicEndpoint"
binding="wsHttpBinding"
contract="ICalculatorService">
</endpoint>
</client>
</system.serviceModel>
</configuration>
Cuando se realiza la primera llamada de servicio, el cliente enviará una consulta de multidifusión que busca servicios que coincidan con el contrato ICalculatorService e intentará conectarse a uno. Varias configuraciones permiten ajustar el serach, ajustar los enlaces de detección y controlar el proceso de detección. También puede hacer todo esto mediante programación mediante la clase DiscoveryClient.
El ejemplo siguiente va un paso más allá mostrando cómo usar UdpDiscoveryEndpoint mediante programación para detectar un punto de conexión ICalculatorService y, a continuación, invocarlo:
Creación de DiscoveryClient
DiscoveryClient discoveryClient =
new DiscoveryClient(new UdpDiscoveryEndpoint());
Búsqueda de puntos de conexión de ICalculatorService en el ámbito especificado
FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));
FindResponse findResponse = discoveryClient.Find(findCriteria);
Solo tiene que elegir el primer punto de conexión detectado.
EndpointAddress address = findResponse.Endpoints[0]. Dirección;
Creación del cliente de servicio de destino
CalculatorServiceClient client =
new CalculatorServiceClient("calculatorEndpoint");
Conexión al punto de conexión de servicio detectado
cliente. Endpoint.Address = address;
Console.WriteLine("Invocar CalculatorService en {0}", dirección);
Llame a la operación Agregar servicio.
double result = client. Add(100, 15.99);
Console.WriteLine("Add({0},{1}) = {2}", 100, 15.99, result);
Una vez que el programa cliente ha recuperado la colección de puntos de conexión detectados, puede usar uno de ellos para invocar realmente el servicio de destino. En la figura 10 se muestra la salida de ejecutar el código de cliente mostrado anteriormente, suponiendo que el servicio también se esté ejecutando al mismo tiempo. Nota: en este ejemplo, la operación Buscar en el cliente de detección es sincrónica; discovery también proporciona compatibilidad con operaciones de búsqueda asincrónicas.
Figura 10: Salida de la ejecución del código de cliente de detección
Uso de ámbitos al detectar puntos de conexión
En el ejemplo anterior, el cliente simplemente sondeó para los servicios en función del tipo de contrato de servicio. Los clientes pueden restringir los resultados de la detección proporcionando información de ámbito adicional al enviar los sondeos de detección. Echemos un vistazo a un ejemplo sencillo para ver cómo se pueden usar los "ámbitos" durante la detección.
En primer lugar, el servicio debe asociar uno o varios ámbitos a cada punto de conexión que va a publicar para la detección. WCF 4 incluye un comportamiento de <endpointDiscovery> que puede usar para definir un conjunto de ámbitos que puede asociar a una definición de punto de conexión. En el ejemplo siguiente se muestra cómo asociar dos ámbitos con el único punto de conexión definido en el servicio:
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="CalculatorService"
behaviorConfiguration="calculatorServiceBehavior">
<endpoint binding="wsHttpBinding"
contract="ICalculatorService"
behaviorConfiguration="ep1Behavior" />
<endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
</service>
</services>
<comportamientos>
< > serviceBehaviors
<behavior name="calculatorServiceBehavior">
<serviceDiscovery/>
</behavior>
< > /serviceBehaviors
<endpointBehaviors>
<behavior name="ep1Behavior">
<endpointDiscovery>
<!-- ámbitos asociados a este comportamiento de punto de conexión:>
<ámbitos>
<agregar scope="http://www.example.org/calculator"/>
<agregar scope="ldap:///ou=engineering,o=exampleorg,c=us"/>
</scopes>
</endpointDiscovery>
</behavior>
< > /endpointBehaviors
</behaviors>
</system.serviceModel>
</configuration>
Los clientes pueden sondear los puntos de conexión de servicio en función de ámbitos específicos en tiempo de ejecución. Para ello, agregue una lista de ámbitos de destino a la instancia de FindCriteria que proporcione a la operación Find. En el código siguiente se muestra cómo detectar puntos de conexión ICalculatorService que coinciden con un ámbito LDAP específico:
...
Creación de DiscoveryClient
DiscoveryClient discoveryClient = new DiscoveryClient("udpDiscoveryEndpoint");
Búsqueda de puntos de conexión de ICalculatorService en el ámbito especificado
Ámbito de URI = nuevo Uri("ldap:///ou=engineering,o=exampleorg,c=us");
FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));
findCriteria.Scopes.Add(scope);
FindResponse findResponse = discoveryClient.Find(findCriteria);
...
El aprovechamiento de ámbitos permite ajustar la implementación de detección para que los clientes puedan detectar con más facilidad los puntos de conexión de servicio específicos de interés para ellos. La detección también permite una personalización adicional. Por ejemplo, los servicios pueden agregar metadatos XML personalizados a un punto de conexión. Esta información se envía al cliente en respuesta a la consulta del cliente.
Anuncios de servicio
WCF 4 también facilita la configuración de servicios para "anunciar" sus puntos de conexión cuando se inician. Esto permite a los clientes que están "escuchando" obtener información sobre los nuevos puntos de conexión de servicio directamente a medida que se unen a la red, lo que reduce la cantidad de sondeos (y mensajería de multidifusión) realizada por los clientes.
Puede configurar un servicio con un punto de conexión de anuncio mediante el comportamiento de> serviceDiscovery de <. El comportamiento de> serviceDiscovery <permite definir una colección de puntos de conexión de anuncio que el servicio expondrá. Puede usar el estándar "udpAnnouncementEndpoint" para la mayoría de los casos.
También deberá configurar el servicio con un estándar "udpDiscoveryEndpoint" si quiere que responda a los sondeos de detección iniciados por los clientes. En el ejemplo siguiente se muestra una configuración típica:
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="CalculatorService">
<endpoint binding="wsHttpBinding" contract="ICalculatorService"/>
<endpoint kind="udpDiscoveryEndpoint"/>
</service>
</services>
<comportamientos>
< > serviceBehaviors
< > de comportamiento
<serviceDiscovery>
<announcementEndpoints>
<endpoint kind="udpAnnouncementEndpoint"/>
</announcementEndpoints>
</serviceDiscovery>
</behavior>
< > /serviceBehaviors
</behaviors>
</system.serviceModel>
</configuration>
Con esta configuración en su lugar, el servicio se anunciará cuando se ponga en línea y también anunciará cuándo se queda sin conexión. Para aprovechar estos anuncios, tendrá que diseñar específicamente a los clientes para que los escuchen en tiempo de ejecución. Para ello, hospede un servicio de anuncio dentro de la aplicación cliente que implemente el protocolo de anuncio WS-Discovery.
WCF 4 incluye una clase denominada AnnouncementService diseñada específicamente para este propósito. AnnouncementService proporciona dos controladores de eventos: OnlineAnnouncementReceived y OfflineAnnouncementReceived. Las aplicaciones cliente simplemente pueden hospedar una instancia de AnnouncementService mediante ServiceHost y registrar controladores de eventos para estos dos eventos.
Cada vez que un servicio se conecte y se anuncie, el AnnouncementService hospedado por el cliente recibirá el anuncio "en línea" y OnlineAnnouncementReceived se activará en el cliente. Cuando el servicio se desconecta, enviará un anuncio "sin conexión" y OfflineAnnouncementReceived se activará en el cliente. A continuación se muestra una aplicación cliente de ejemplo que hospeda AnnouncementService e implementa controladores para los dos eventos de anuncio:
clase Client
{
public static void Main()
{
Creación de una instancia de AnnouncementService
AnnouncementService announcementService = new AnnouncementService();
Suscripción a los eventos de anuncio
announcementService.OnlineAnnouncementReceived += OnOnlineEvent;
announcementService.OfflineAnnouncementReceived += OnOfflineEvent;
Creación de ServiceHost para AnnouncementService
using (ServiceHost announcementServiceHost =
new ServiceHost(announcementService))
{
Escucha de los anuncios enviados a través de la multidifusión UDP
announcementServiceHost.AddServiceEndpoint(
new UdpAnnouncementEndpoint());
announcementServiceHost.Open();
Console.WriteLine("Listening for service announcements.");
Console.WriteLine();
Console.WriteLine("Presione <ENTRAR> para finalizar".);
Console.ReadLine();
}
}
static void OnOnlineEvent(object sender, AnnouncementEventArgs e)
{
Console.WriteLine();
Console.WriteLine("Recibió un anuncio en línea de {0}:",
e.EndpointDiscoveryMetadata.Address);
PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata);
}
static void OnOfflineEvent(object sender, AnnouncementEventArgs e)
{
Console.WriteLine();
Console.WriteLine("Se recibió un anuncio sin conexión de {0}:",
e.EndpointDiscoveryMetadata.Address);
PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata);
}
...
}
Figura 11: Escucha de mensajes de anuncio de detección
Ahora supongamos que ejecuto este programa cliente y lo dejo en funcionamiento durante un tiempo. Después, ejecuté algunas instancias de la aplicación host de servicio. Cuando se inicie cada uno, veremos que aparece un mensaje de anuncio "en línea" en la ventana de la consola del cliente. Cuando cierre cada una de las aplicaciones host de servicio, veremos que aparece un mensaje de anuncio "sin conexión" en la ventana de la consola del cliente. En la figura 11 se muestra la ventana de consola del cliente resultante después de hacer lo que acabo de describir.
Recuerde que el modo de detección ad hoc solo funciona en una subred local. Si desea usar WS-Discovery más allá de los límites de la red local, deberá volver al modo de detección administrado. WCF 4 también proporciona compatibilidad para crear los componentes de detección administrados necesarios.
Detección de servicios administrados
La implementación del modo de detección administrada es un poco más implicada que el modo ad hoc porque requiere que implemente un servicio de proxy de detección. El servicio proxy de detección es el componente que realizará un seguimiento de todos los puntos de conexión de servicio disponibles. En este ejemplo se usa la funcionalidad de anuncio para actualizar el proxy de detección. Hay muchas otras maneras de proporcionar un proxy de detección con la información de detección pertinente, por ejemplo, puede conectar una base de datos existente de puntos de conexión y capturar datos desde allí. ¿Cómo se implementa un servicio de proxy de detección?
WCF 4 incluye una clase base denominada DiscoveryProxy de la que puede derivar para implementar un servicio de proxy de detección. En la figura 12 se muestra el inicio de una implementación de servicio proxy de detección personalizada. Los ejemplos del SDK de .NET 4 contienen una implementación de ejemplo completa para la referencia. Una vez que haya terminado de implementar el servicio proxy de detección, debe hospedarlo en algún lugar.
Figura 12: Implementación de un servicio de proxy de detección personalizado
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyDiscoveryProxy : DiscoveryProxyBase
{
Repositorio para almacenar EndpointDiscoveryMetadata.
También se puede usar una base de datos o un archivo plano.
Dictionary<EndpointAddress, EndpointDiscoveryMetadata> onlineServices;
public MyDiscoveryProxy()
{
this.onlineServices =
new Dictionary<EndpointAddress, EndpointDiscoveryMetadata>();
}
Se llama a OnBeginOnlineAnnouncement cuando proxy recibe un mensaje Hello.
invalidación protegida de IAsyncResult OnBeginOnlineAnnouncement(
DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata
endpointDiscoveryMetadata, devolución de llamada AsyncCallback, estado de objeto)
{
éste. AddOnlineService(endpointDiscoveryMetadata);
devuelve el nuevo OnOnlineAnnouncementAsyncResult(callback, state);
}
protected override void OnEndOnlineAnnouncement(IAsyncResult result)
{
OnOnlineAnnouncementAsyncResult.End(result);
}
Se llama a OnBeginOfflineAnnouncement cuando proxy recibe un mensaje bye.
invalidación protegida IAsyncResult OnBeginOfflineAnnouncement(
DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata
endpointDiscoveryMetadata, devolución de llamada AsyncCallback, estado de objeto)
{
éste. RemoveOnlineService(endpointDiscoveryMetadata);
devuelve el nuevo OnOfflineAnnouncementAsyncResult(callback, state);
}
protected override void OnEndOfflineAnnouncement(IAsyncResult result)
{
OnOfflineAnnouncementAsyncResult.End(result);
}
Se llama a OnBeginFind cuando el proxy recibe un mensaje de solicitud de sondeo.
invalidación protegida IAsyncResult OnBeginFind(
FindRequestContext findRequestContext, devolución de llamada AsyncCallback, estado de objeto)
{
éste. MatchFromOnlineService(findRequestContext);
devuelve el nuevo OnFindAsyncResult(callback, state);
}
invalidación protegida void OnEndFind(IAsyncResult result)
{
OnFindAsyncResult.End(result);
}
...
En este ejemplo, simplemente hospedaré el servicio MyDiscoveryProxy en una aplicación de consola. Configuraré el host con dos puntos de conexión: un punto de conexión de detección y un punto de conexión de anuncio. En el ejemplo siguiente se muestra cómo hospedar correctamente el servicio MyDiscoveryProxy con ambos puntos de conexión:
Clase Program
{
public static void Main()
{
Uri probeEndpointAddress = new Uri("net.tcp://localhost:8001/Probe");
Anuncio de URIEndpointAddress =
new Uri("net.tcp://localhost:9021/Announcement");
ServiceHost proxyServiceHost = new ServiceHost(new MyDiscoveryProxy());
DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(
new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));
discoveryEndpoint.IsSystemEndpoint = false;
AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(
new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));
proxyServiceHost.AddServiceEndpoint(discoveryEndpoint);
proxyServiceHost.AddServiceEndpoint(announcementEndpoint);
proxyServiceHost.Open();
Console.WriteLine("Servicio proxy iniciado.");
Console.WriteLine();
Console.WriteLine("Presione <ENTRAR> para finalizar el servicio".);
Console.WriteLine();
Console.ReadLine();
proxyServiceHost.Close();
}
}
Una vez que tenga un servicio de proxy de detección en funcionamiento, puede configurar los servicios para anunciarse directamente en el servicio de proxy de detección. Del mismo modo, puede configurar las aplicaciones cliente para sondear el servicio proxy de detección directamente (no más mensajería de multidifusión).
Configure el servicio para anunciarse directamente en el servicio proxy de detección especificando la dirección de anuncio del proxy de detección al crear el AnnouncementEndpoint dentro de la aplicación host de servicio. En el ejemplo siguiente se muestra cómo hacerlo:
...
Uri baseAddress = new Uri("net.tcp://localhost:9002/CalculatorService/" +
Guid.NewGuid(). ToString());
Uri announcementEndpointAddress = new Uri("net.tcp://localhost:9021/Announcement");
ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService), baseAddress);
ServiceEndpoint netTcpEndpoint = serviceHost.AddServiceEndpoint(
typeof(ICalculatorService), new NetTcpBinding(), string. Vacío);
Creación de un punto de conexión de anuncio que apunte al servicio de proxy hospedado
AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(
new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));
ServiceDiscoveryBehavior serviceDiscoveryBehavior = new ServiceDiscoveryBehavior();
serviceDiscoveryBehavior.AnnouncementEndpoints.Add(announcementEndpoint);
serviceHost.Description.Behaviors.Add(serviceDiscoveryBehavior);
serviceHost.Open();
...
A continuación, puede configurar las aplicaciones cliente para que se comuniquen directamente con el servicio proxy de detección especificando la dirección de sondeo del proxy de detección al crear discoveryEndpoint dentro de la aplicación cliente. En el ejemplo siguiente se muestra una manera de hacerlo:
...
Cree un punto de conexión de detección que apunte al servicio proxy.
Uri probeEndpointAddress = new Uri("net.tcp://localhost:8001/Probe");
DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(
new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));
Creación de DiscoveryClient con discoveryEndpoint creado anteriormente
DiscoveryClient discoveryClient = new DiscoveryClient(discoveryEndpoint);
Búsqueda de puntos de conexión de ICalculatorService
FindResponse findResponse = discoveryClient.Find(
new FindCriteria(typeof(ICalculatorService)));
...
Ahora vamos a ver un ejemplo completo. En primer lugar, ejecutaré la aplicación proxy de detección para que el servicio de proxy de detección esté disponible para su uso. A continuación, ejecutaré una instancia de la aplicación host de servicio, que se anunciará con el proxy de detección. Una vez que esto suceda, veremos un mensaje impreso en la ventana de consola de la aplicación proxy de detección (consulte la figura 13). Esto muestra que el servicio se anunció correctamente en el proxy de detección y el proxy de detección guardó la información sobre el nuevo punto de conexión de servicio "en línea". Ahora, si ejecutamos el código de cliente mostrado anteriormente, sondeará el proxy de detección directamente y recuperará la dirección del punto de conexión del servicio de destino que se está ejecutando actualmente.
Figura 13: Salida del servicio proxy de detección en tiempo de ejecución
La belleza de la detección de servicios administrados es que funciona a través de los límites de red (se basa en llamadas de servicio tradicionales) y reduce la necesidad de mensajería de multidifusión dentro de la solución de detección. Además, dado que los clientes pasan por un proxy de detección para buscar servicios, los propios servicios no necesitan estar en funcionamiento todo el tiempo para poder detectarse.
Uso avanzado del proxy de detección
El modelo de programación WCF ofrece mucha flexibilidad en la implementación de un proxy de detección. Recibir anuncios es una manera de rellenar la lista de servicios; sin embargo, no es el único método. Por ejemplo, si el entorno ya contiene un repositorio de servicios, puede crear fácilmente una fachada de proxy de detección sobre ese almacén para que el repositorio se pueda detectar en tiempo de ejecución.
Un proxy de detección se puede configurar en modo ad hoc o de administración. Cuando se trabaja en modo administrado, los clientes se comunican directamente con el proxy de forma unidifusión mediante anuncios, sondeos y se resuelven. El proxy también transmite la respuesta de forma unidifusión al remitente.
Si funciona en modo ad hoc, un proxy puede escuchar mensajes de detección de multidifusión y responder directamente al remitente. En este modo ad hoc, un proxy también se puede configurar específicamente para suprimir mensajes de multidifusión. Es decir, si un proxy recibe un mensaje de multidifusión, informa al remitente de su presencia e informa al remitente para dirigir más consultas en el proxy, evitando así más mensajes de multidifusión.
Para obtener más información sobre estos escenarios de detección avanzados, consulte el WS-Discovery Primer en http://www.oasis-open.org/committees/download.php/32184/WS-D-primer-wd-04.docx.
Servicio de enrutamiento
En algunos entornos orientados a servicios, a menudo resulta útil aprovechar los servicios centralizados de "enrutamiento" que actúan como agentes o puertas de enlace a los servicios empresariales reales dispersos alrededor de la organización. Esto desacopla a los consumidores de los servicios empresariales reales y permite realizar una variedad de tipos diferentes de procesamiento intermedio dentro del nodo de enrutamiento.
Por ejemplo, algunos entornos usan el enrutamiento para implementar un límite de seguridad centralizado que todos los mensajes entrantes deben pasar. Algunas usan técnicas de enrutamiento basadas en contenido para determinar qué servicio de destino se va a usar en función del contenido de un mensaje entrante determinado. Otros usan el enrutamiento para implementar el protocolo de puente, lo que permite a los consumidores usar un conjunto de protocolos para comunicarse mientras el enrutador usa un conjunto diferente de protocolos para comunicarse con el servicio de destino. Tampoco es raro usar el enrutamiento para varios equilibrio de carga o incluso técnicas de control de versiones de servicio.
Sea cual sea el motivo, el patrón de "enrutamiento intermedio" es un requisito común al compilar soluciones SOA a gran escala en la actualidad. En WCF 3.x, no había compatibilidad oficial con el enrutamiento. Aunque el marco proporcionó las API necesarias para implementar sus propios servicios de enrutamiento, era mucho trabajo hacerlo correctamente. Se han publicado varios artículos en MSDN Magazine que muestran cómo hacerlo.
Dado que el enrutamiento es un requisito tan común en estos días, WCF 4 ahora viene con un "servicio de enrutamiento" oficial en el marco que puede hospedar y configurar simplemente en sus propias soluciones.
Descripción de RoutingService
WCF 4 incluye una nueva clase denominada RoutingService, que proporciona una implementación genérica de enrutamiento wcF para su uso en las aplicaciones. RoutingService puede controlar el enrutamiento de mensajes a través de cualquier protocolo compatible con WCF mediante una variedad de patrones de mensajería diferentes, como la mensajería unidireccional, la solicitud-respuesta y la mensajería dúplex). A continuación se muestra la definición de clase RoutingService:
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any,
InstanceContextMode = InstanceContextMode.PerSession,
UseSynchronizationContext = false, ValidateMustUnderstand = false),
AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public sealed class RoutingService: // contracts allow different communication patterns
ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter,
IDuplexSessionRouter, IDisposable
{
... // implementación omitida
}
Como puede ver, la clase RoutingService deriva de varios contratos de servicio para admitir varios patrones de mensajería. Cada contrato de servicio proporciona compatibilidad con un patrón de mensajería diferente, incluida la compatibilidad con las comunicaciones basadas en sesión cuando corresponda.
Todo el propósito de RoutingService es recibir mensajes entrantes de los consumidores y "enrutarlos" a un servicio de bajada adecuado. RouterService determina qué servicio de destino se va a usar mediante la evaluación de cada mensaje entrante en un conjunto de filtros de mensajes. Por lo tanto, como desarrollador, controla el comportamiento de enrutamiento definiendo los filtros de mensaje, normalmente en un archivo de configuración. Los servicios de destino pueden residir en la misma máquina que RouterService, pero no tienen que hacerlo, sino que también se pueden distribuir a través de la red y pueden requerir una variedad de protocolos diferentes.
Hospedaje de RoutingService
Puede hospedar RoutingService en la aplicación como lo haría con cualquier otro servicio WCF. Simplemente cree una instancia de ServiceHost y especifique RoutingService para el tipo de servicio. Una vez que llame a Open en la instancia de ServiceHost, el servicio de enrutamiento estará listo para "enrutar" los mensajes como se muestra aquí:
using System;
uso de System.ServiceModel;
uso de System.ServiceModel.Routing;
public static void Main()
{
Cree un ServiceHost para el tipo RoutingService.
using (ServiceHost serviceHost =
new ServiceHost(typeof(RoutingService)))
{
probar
{
serviceHost.Open();
Console.WriteLine("The Routing Service is now running.");
Console.WriteLine("Presione <ENTRAR> para finalizar el enrutador".);
Ahora se puede acceder al servicio.
Console.ReadLine();
serviceHost.Close();
}
catch (CommunicationException)
{
serviceHost.Abort();
}
}
}
También configura RoutingService como cualquier otro servicio y es donde se definen los filtros de enrutamiento. En primer lugar, debe configurarlo con uno o varios puntos de conexión. Al definir un punto de conexión de enrutamiento, elija un enlace WCF y uno de los contratos de servicio de enrutamiento implementados por el routingService mostrado anteriormente (por ejemplo, ISimplexDatagramRouter, IRequestReplyRouter, etc.). Puede exponer más de un punto de conexión en routingService si desea admitir varios patrones de mensajería o enlaces WCF.
En el ejemplo siguiente se muestra cómo configurar RoutingService con cuatro puntos de conexión de enrutamiento: dos que usan BasicHttpBinding (unidireccional y request-reply) y dos que usan WSHttpBinding (unidireccional y request-reply). Observe cómo es igual que configurar cualquier otro servicio WCF:
> de configuración de <
<system.serviceModel>
> servicios de <
<!--ROUTING SERVICE:>
<comportamiento del servicioConfiguration="routingData"
name="System.ServiceModel.Routing.RoutingService">
< > de host
<baseAddresses>
<agregar baseAddress="https://localhost:8000/routingservice/router"/>
</baseAddresses>
</host>
<!--
Definir y configurar el punto de conexión en el que queremos que el enrutador escuche y
Contrato que queremos que use. Los contratos proporcionados por enrutador son:
ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter y
IDuplexSessionRouter.
-->
<endpoint address="oneway-basic"
binding="basicHttpBinding"
name="onewayEndpointBasic"
contract="System.ServiceModel.Routing.ISimplexDatagramRouter" />
<endpoint address="oneway-ws"
binding="wsHttpBinding"
name="onewayEndpointWS"
contract="System.ServiceModel.Routing.ISimplexDatagramRouter" />
<endpoint address="twoway-basic"
binding="basicHttpBinding"
name="reqReplyEndpointBasic"
contract="System.ServiceModel.Routing.IRequestReplyRouter" />
<endpoint address="twoway-ws"
binding="wsHttpBinding"
name="reqReplyEndpointWS"
contract="System.ServiceModel.Routing.IRequestReplyRouter" />
</service>
</services>
...
Las interfaces ISimplexDatagramRouter e IRequestReplyRouter definen definiciones genéricas de contrato de servicio unidireccional y de solicitud-respuesta que se pueden usar junto con contratos de servicio específicos de la empresa. A continuación se muestra cómo se definieron estas interfaces en WCF:
[ServiceContract(Namespace="https://schemas.microsoft.com/netfx/2009/05/routing",
SessionMode = SessionMode.Allowed)]
interfaz pública ISimplexDatagramRouter
{
[OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
IAsyncResult BeginProcessMessage(Message, AsyncCallback callback,
estado del objeto);
void EndProcessMessage(IAsyncResult result);
}
[ServiceContract(Namespace="https://schemas.microsoft.com/netfx/2009/05/routing",
SessionMode = SessionMode.Allowed)]
interfaz pública IRequestReplyRouter
{
[OperationContract(AsyncPattern = true, IsOneWay= false, Action = "*",
ReplyAction = "*")]
[GenericTransactionFlow(TransactionFlowOption.Allowed)]
IAsyncResult BeginProcessRequest(Message, AsyncCallback callback,
estado del objeto);
Message EndProcessRequest(IAsyncResult result);
}
La configuración del punto de conexión anterior expone los puntos de conexión de enrutamiento que los consumidores usarán. Las aplicaciones cliente elegirán uno de estos puntos de conexión para usarlos en su código de cliente y dirigirán todas las invocaciones de servicio directamente a RoutingService. Cuando RoutingService recibe un mensaje a través de uno de estos puntos de conexión, evalúa los filtros de mensaje de enrutamiento para determinar dónde reenviar el mensaje.
Configuración de RoutingService con filtros de mensajes
Puede configurar RoutingService con filtros de mensaje a través de código o configuración (como todo lo demás en WCF). WCF 4 proporciona un RoutingBehavior para administrar los filtros de mensajes de enrutamiento. Primero debe habilitar RoutingBehavior en RouterService y, a continuación, especificar el nombre de la tabla de filtros que desea usar con esta instancia concreta de RoutingService:
> de configuración de <
<system.serviceModel>
...
<comportamientos>
< > serviceBehaviors
<behavior name="routingData">
<serviceMetadata httpGetEnabled="True"/>
<!-- Definir el comportamiento de enrutamiento y especificar el nombre de la tabla de filtro:>
<filterTableName="filterTable1" />
</behavior>
< > /serviceBehaviors
</behaviors>
...
Si observa en el ejemplo anterior donde configuramos RoutingService con puntos de conexión, verá que hemos aplicado el comportamiento "routingData" al servicio a través del atributo behaviorConfiguration. A continuación, es necesario definir una tabla de filtro denominada "filterTable1".
Sin embargo, para poder definir una tabla de filtro, necesitamos definiciones de punto de conexión para los servicios de destino a los que pretendemos enrutar. Estos puntos de conexión de destino se definen en la sección de configuración> cliente de WCF <> porque RoutingService es esencialmente el "cliente" cuando reenvía mensajes a un servicio de destino. En el ejemplo siguiente se muestra cómo definir dos puntos de conexión de destino a los que podemos enrutar:
> de configuración de <
...
<!-- Definir los puntos de conexión de cliente con los que queremos que el enrutador se comunique.
Estos son los destinos a los que el enrutador enviará mensajes. -->
> de cliente de <
<nombre del punto de conexión="CalculatorService1"
address="https://localhost:8000/servicemodelsamples/calcservice1"
binding="wsHttpBinding" contract="*" />
<nombre del punto de conexión="CalculatorService2"
address="https://localhost:8001/servicemodelsamples/calcservice2"
binding="wsHttpBinding" contract="*" />
</client>
...
Ahora podemos definir la tabla de filtros real, que determinará la lógica de enrutamiento en tiempo de ejecución. Defina las entradas de la tabla de filtro en el elemento <filterTables>. Cada entrada de la <filterTable> define una asignación entre un "filtro" de enrutamiento y un punto de conexión de destino. Defina los "filtros" que desea usar en el elemento <filtros>: cada <filtro> entrada especifica qué tipo de filtro desea usar junto con los datos específicos del filtro (como un valor de acción, una expresión XPath, etc.).
En el ejemplo siguiente se muestra cómo configurar una tabla de filtro con un único filtro que se asigna al punto de conexión CalculatorService1. En este caso, el filtro "MatchAll" coincide con todos los mensajes entrantes:
> de configuración de <
...
SECCIÓN <!--ROUTING:>
< > de enrutamiento
<!-- Definir los filtros que queremos que use el enrutador. -->
<filtros>
<filter name="MatchAllFilter1" filterType="MatchAll" />
</filters>
<!-- Definir la tabla de filtros que contiene el filtro matchAll (>
<filterTables>
<filterTable name="filterTable1">
<!-- Asignar el filtro a un punto de conexión de cliente que se definió anteriormente.
Los mensajes que coinciden con este filtro se enviarán a este destino. -->
<agregar filterName="MatchAllFilter1" endpointName="CalculatorService1" />
</filterTable>
< > /filterTables
</routing>
</system.serviceModel>
</configuration>
Podemos comprobar que el enrutamiento funciona correctamente ejecutando la aplicación host del servicio de enrutamiento, la aplicación host CalculatorService1 y un cliente diseñado para enviar mensajes a uno de los puntos de conexión del enrutador. Cuando ejecutamos el cliente, deberíamos ver que los mensajes llegan a CalculatorService 1 después de que se "enrutan" mediante routingService intermedio (vea la figura 14, la figura 15 y la figura 16).
Figura 14: La aplicación host RoutingService
Figura 15: Cliente dirigido a RoutingService en https://localhost:8000/routingservice/router
Figura 16: El servicio de destino (CalculatorService1)
Filtros de mensajes y enrutamiento basado en contenido
WCF incluye varias clases MessageFilter integradas que puede usar junto con los filtros de mensaje de enrutamiento para inspeccionar el contenido de los mensajes entrantes.
Por ejemplo, WCF proporciona un ActionMessageFilter que le permite coincidir con valores específicos de WS-Addressing "acción". WCF también proporciona EndpointAddressMessageFilter, EndpointNameMessageFilter y PrefixEndpointAddressMessageFilter que le permiten hacer coincidir detalles específicos del punto de conexión. Uno de los más flexibles es XPathMessageFilter, que permite evaluar expresiones XPath en los mensajes entrantes. Todos estos filtros permiten realizar el enrutamiento basado en contenido dentro de la solución.
Además de estos tipos de MessageFilter integrados, WCF 4 también permite definir filtros de mensajes personalizados, lo que sucede como uno de los puntos de extensibilidad principales para RoutingService.
Veamos un ejemplo de cómo realizar el enrutamiento basado en contenido en función de los valores de acción. Supongamos que queremos enrutar la mitad de las operaciones CalculatorService a CalculatorService1 y la otra mitad a CalculatorService2. Para ello, puede definir filtros para cada uno de los distintos valores de acción CalculatorService y asignar la mitad de ellos a cada punto de conexión de servicio de destino, como se muestra aquí:
> de configuración de <
...
SECCIÓN <!--ROUTING:>
< > de enrutamiento
<!-- Definir los filtros que queremos que use el enrutador. -->
<filtros>
<filter name="addFilter" filterType="Action"
filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Add"/>
<filter name="subFilter" filterType="Action"
filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Subtract"/>
<filter name="mulFilter" filterType="Action"
filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Multiply"/>
<filter name="divFilter" filterType="Action"
filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Divide"/>
</filters>
<filterTables>
<filterTable name="filterTable1">
<agregar filterName="addFilter" endpointName="CalculatorService1"/>
<agregar filterName="subFilter" endpointName="CalculatorService2"/>
<add filterName="mulFilter" endpointName="CalculatorService1"/>
<add filterName="divFilter" endpointName="CalculatorService2"/>
</filterTable>
< > /filterTables
</routing>
</system.serviceModel>
</configuration>
Ahora, cuando ejecutemos la solución y ejecutemos el cliente que invoca las cuatro operaciones, veremos que la mitad de las operaciones aparecen en cada ventana de consola de servicio (vea la figura 17 y la figura 18).
Figura 17: Salida de calculatorService1
Figura 18: Salida de calculatorService2
XPathMessageFilter proporciona aún más flexibilidad porque puede proporcionar una variedad de expresiones XPath diferentes para evaluar los mensajes entrantes. Las expresiones XPath pueden evaluar cualquier parte del mensaje entrante, incluidos los encabezados SOAP o el cuerpo SOAP. Esto le proporciona una gran flexibilidad al crear filtros de mensajes basados en contenido. Para comprender la mecánica de XPathMessageFilter, a continuación se muestra cómo reescribir el último ejemplo mediante expresiones XPath:
> de configuración de <
...
SECCIÓN <!--ROUTING:>
< > de enrutamiento
<!-- Definir los filtros que queremos que use el enrutador. -->
<filtros>
<filter name="addFilter" filterType="XPath"
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Add'"/>
<filter name="subFilter" filterType="XPath"
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Subtract'"/>
<filter name="mulFilter" filterType="XPath"
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Multiply'"/>
<filter name="divFilter" filterType="XPath"
filterData="/s:Envelope/s:Header/wsa:Action =
'http://Microsoft.Samples.ServiceModel/ICalculator/Divide'"/>
</filters>
<namespaceTable>
<agregar prefix="s" namespace="http://www.w3.org/2003/05/soap-envelope" />
<agregar prefix="wsa" namespace="http://www.w3.org/2005/08/addressing" />
</namespaceTable>
<filterTables>
<filterTable name="filterTable1">
<agregar filterName="addFilter" endpointName="CalculatorService1"/>
<agregar filterName="subFilter" endpointName="CalculatorService2"/>
<add filterName="mulFilter" endpointName="CalculatorService1"/>
<add filterName="divFilter" endpointName="CalculatorService2"/>
</filterTable>
< > /filterTables
</routing>
</system.serviceModel>
</configuration>
Observe que el atributo filterData contiene una expresión XPath que se evaluará en el mensaje entrante (las expresiones simplemente comprueban los valores de acción de este ejemplo). Observe cómo también he definido un conjunto de enlaces de prefijos de espacio de nombres mediante el elemento <namespaceTable>. Esto es necesario si desea usar prefijos de espacio de nombres dentro de las expresiones XPath como he hecho anteriormente. Volver a ejecutar la solución con esta configuración genera los mismos resultados que antes (vea la figura 17 y la figura 18).
Tendrá que usar esta técnica de filtro XPath cada vez que necesite enrutar mensajes basados en encabezados SOAP personalizados o en función del contenido encontrado en el cuerpo del mensaje SOAP.
Protocolo de puente
En los ejemplos anteriores, usamos el mismo enlace WCF (WSHttpBinding) para hablar entre el cliente y el enrutador, y entre el enrutador y los servicios de destino. RoutingService es capaz de puenter la comunicación entre la mayoría de los enlaces WCF. Por ejemplo, es posible que quiera configurar el enrutador para que los clientes se comuniquen con él a través del WSHttpBinding, pero el enrutador se comunica con los servicios de destino de bajada mediante NetTcpBinding o NetNamedPipeBinding.
Veamos cómo configurar RoutingService para controlar este escenario. Dejaremos la configuración del punto de conexión routingService igual que antes, lo que permite a los consumidores comunicarse con RoutingService a través de BasicHttpBinding o WSHttpBinding. Pero ahora cambiaremos las definiciones de punto de conexión de cliente para que los servicios de destino usen NetTcpBinding y NetNamedPipeBinding como se muestra aquí:
> de configuración de <
...
<!-- Definir los puntos de conexión de cliente con los que queremos que el enrutador se comunique.
Estos son los destinos a los que el enrutador enviará mensajes. -->
> de cliente de <
<nombre del punto de conexión="CalculatorService1"
address="net.tcp://localhost:8001/servicemodelsamples/calcservice1"
binding="netTcpBinding" contract="*" />
<nombre del punto de conexión="CalculatorService2"
address="net.pipe://localhost/servicemodelsamples/calcservice2"
binding="netNamedPipeBinding" contract="*" />
</client>
...
Y, por supuesto, necesitaremos actualizar las aplicaciones CalculatorService1 y CalculatorService2 para admitir puntos de conexión compatibles con NetTcpBinding y NetNamedPipeBinding, respectivamente. Con esta configuración en su lugar, los consumidores pueden comunicarse con RoutingService mediante BasicHttpBinding/WSHttpBinding y el enrutador se comunicará con los servicios de destino mediante NetTcpBinding o NetNamedPipeBinding en función del servicio al que se enrute el mensaje.
Tolerancia a errores y entrega de errores
RoutingService también proporciona un mecanismo integrado para tratar los errores de comunicación en tiempo de ejecución y para admitir un nivel básico de tolerancia a errores. Al definir la tabla de filtros, puede definir diferentes listas de puntos de conexión alternativos que usará RoutingService si se comunica con el punto de conexión de destino inicial produce un error. Esto básicamente permite tener listas de puntos de conexión de "copia de seguridad".
En el ejemplo siguiente se muestra cómo definir una lista de puntos de conexión de copia de seguridad dentro del <elemento backupLists> que podemos asociar a nuestras entradas de tabla de filtro:
> de configuración de <
...
SECCIÓN <!--ROUTING:>
< > de enrutamiento
... <!-- Definir los filtros que queremos que use el enrutador. -->
<filterTables>
<filterTable name="filterTable1">
<agregar filterName="addFilter" endpointName="CalculatorService1"
alternateEndpoints="backupEndpoints"/>
<add filterName="subFilter" endpointName="CalculatorService1"
alternateEndpoints="backupEndpoints"/>
<add filterName="mulFilter" endpointName="CalculatorService1"
alternateEndpoints="backupEndpoints"/>
<add filterName="divFilter" endpointName="CalculatorService1"
alternateEndpoints="backupEndpoints"/>
</filterTable>
< > /filterTables
<backupLists>
<backupList name="backupEndpoints">
<agregar endpointName="CalculatorService2"/>
</backupList>
</backupLists>
</routing>
</system.serviceModel>
</configuration>
Observe cómo configuramos todas las entradas de la tabla de filtro para reenviar a CalculatorService1 en este ejemplo, ya que CalculatorService2 es ahora nuestro punto de conexión de "copia de seguridad" que solo se usará cuando CalculatorService1 produzca una excepción TimeoutException, communicationException o un tipo de excepción derivado. Por ejemplo, si ejecuto la solución de nuevo y cierre CalculatorService1 y, a continuación, ejecute el programa cliente, veremos que todos los mensajes terminan en CalculatorService2. Es importante tener en cuenta, una vez más, que toda esta configuración de enrutamiento se puede realizar dinámicamente en el código de la aplicación host.
Comportamiento de enrutamiento de multidifusión
RoutingService también admite el enrutamiento automático de un mensaje entrante determinado a varios destinos de forma "multidifusión". Cuando el mensaje entrante coincide con varios filtros encontrados en la tabla de filtros configuradas, RoutingService enrutará automáticamente el mensaje a cada uno de los puntos de conexión de destino asociados a los filtros "coincidentes".
En el ejemplo siguiente se muestran dos entradas de enrutamiento configuradas con el mismo filtro comodín, que coincide con todos los mensajes entrantes:
> de configuración de <
...
SECCIÓN <!--ROUTING:>
< > de enrutamiento
<!-- Definir los filtros que queremos que use el enrutador. -->
<filtros>
<filter name="wildcardFilter" filterType="MatchAll" />
</filters>
<filterTables>
<filterTable name="filterTable1">
<agregar filterName="wildcardFilter" endpointName="CalculatorService1"/>
<agregar filterName="wildcardFilter" endpointName="CalculatorService2"/>
<agregar filterName="wildcardFilter" endpointName="CalculatorService3"/>
</filterTable>
< > /filterTables
</routing>
</system.serviceModel>
</configuration>
Con esta configuración, cada mensaje SOAP entrante se enrutará automáticamente a todos los puntos de conexión de destino, independientemente de lo que se encuentre en los mensajes.
Este comportamiento de multidifusión se compone de las funcionalidades de protocolo de puente y control de errores que se describen en las secciones anteriores. El único problema es que la multidifusión solo funciona para la comunicación unidireccional o dúplex, pero no para la comunicación de solicitud-respuesta, ya que el sistema subyacente necesita mantener una relación uno a uno entre las solicitudes salientes y las respuestas entrantes.
Compatibilidad mejorada con REST
WCF 4 incluye varias características nuevas que son útiles al compilar servicios RESTful mediante WCF. Este conjunto de características se conoce ahora como Servicios WebHttp de WCF. Incluyen compatibilidad con una página de ayuda automática que describe el servicio RESTful a los consumidores, almacenamiento en caché HTTP simplificado, selección de formato de mensaje, excepciones compatibles con REST, ASP.NET integración de enrutamiento, algunas nuevas plantillas de proyecto de Visual Studio, etc. No tendremos espacio para cubrir todas estas características aquí en detalle, pero le daré una introducción rápida a algunos de mis favoritos a continuación, junto con vínculos a más información sobre el resto.
Muchas de estas características fueron introducidas por primera vez por el Kit de inicio rest de WCF el año pasado y ahora lo están convirtiendo en el marco oficial. Es posible que vea más características del Kit de inicio rest de WCF en el futuro.
Página de ayuda automática
Al usar la clase WebServiceHost en WCF 4, los servicios RESTful disfrutarán automáticamente de las ventajas de la funcionalidad de la página de ayuda automática. Se trata de una adición muy necesaria al usar REST, dada la falta de metadatos de WSDL y generación de código del lado cliente, lo que facilita mucho a los consumidores averiguar cómo empezar a trabajar con el servicio, por lo que suele ser una buena idea habilitar esta nueva característica.
Cuando se usa la clase WebServiceHost para hospedar el servicio, se configura automáticamente el servicio con WebHttpBehavior y se agrega un punto de conexión HTTP predeterminado configurado con WebHttpBinding (en la dirección HTTP base). A partir de WCF 4, la clase WebHttpBehavior incluye una propiedad HelpEnabled que controla si la nueva página de ayuda está habilitada o no en el host. En el ejemplo de configuración siguiente se muestra cómo habilitar la característica de página de ayuda automática para un punto de conexión REST específico:
> de configuración de <
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<comportamientos>
<endpointBehaviors>
<behavior name="HelpBehavior">
<webHttp helpEnabled="true" />
</behavior>
< > /endpointBehaviors
</behaviors>
> servicios de <
<service name="CounterResource">
<comportamiento del punto de conexiónConfiguration="HelpBehavior"
binding="webHttpBinding"
contract="CounterResource" />
</service>
</services>
</system.serviceModel>
</configuration>
Para ver la página de ayuda, vaya a la dirección base del servicio con la "ayuda" anexada al final de la dirección URL (consulte la figura 19).
Figura 19: Página de ayuda automática para servicios RESTFul
La página de ayuda proporciona una descripción legible de cada operación anotada con [WebGet] o [WebInvoke], y para cada una describe la plantilla de URI, la operación HTTP admitida y los formatos de mensaje admitidos, básicamente todo lo que un consumidor necesita saber (vea la figura 20). También puede proporcionar una descripción más fácil de usar mediante la aplicación de un atributo [Description] a cada operación.
Para cada solicitud o respuesta, la página de ayuda también proporciona un esquema XML y una instancia XML de ejemplo correspondiente que los consumidores pueden usar para integrarse con el servicio. Los consumidores pueden usar el esquema para generar tipos serializables del lado cliente adecuados o simplemente pueden inspeccionar el documento XML de ejemplo para determinar manualmente cómo escribir el código de procesamiento XML adecuado. Ambos enfoques son útiles.
Figura 20: Página de ayuda automática para una operación específica
Esta nueva característica de página de ayuda hace que los servicios DE RESTful sean más reconocibles, lo que, en última instancia, hace que otros usuarios consuman más fácilmente. Los consumidores pueden detectar el diseño del URI del servicio, las operaciones HTTP admitidas y los formatos de solicitud y respuesta, y la descripción siempre permanecerá sincronizada con el código WCF, similar a cómo funcionan las cosas con ASP.NET servicios web.
Compatibilidad con el almacenamiento en caché HTTP
Una de las principales ventajas potenciales de REST es el almacenamiento en caché HTTP. Sin embargo, para lograr esa ventaja, debe aprovechar los distintos encabezados de almacenamiento en caché HTTP en los mensajes de solicitud y respuesta. Puede hacerlo dentro de las operaciones del servicio WCF accediendo manualmente a los encabezados de solicitud y respuesta a través de la instancia de WebOperationContext, pero no es trivial hacerlo correctamente.
Por lo tanto, WCF 4 incluye un modelo más sencillo para controlar el almacenamiento en caché a través del atributo [AspNetCacheProfile] que puede aplicar mediante declaración a las operaciones GET. Este atributo permite especificar un nombre de perfil de almacenamiento en caché de ASP.NET para cada operación y en segundo plano hay un inspector de almacenamiento en caché (CachingParameterInspector) que se encarga de controlar todos los detalles de almacenamiento en caché HTTP subyacentes.
La implementación de [AspNetCacheProfile] se basa en el mecanismo de almacenamiento en caché de salida ASP.NET estándar. En el ejemplo siguiente se muestra cómo aplicar el atributo [AspNetCacheProfile] a una operación [WebGet]:
...
[AspNetCacheProfile("CacheFor60Seconds")]
[WebGet(UriTemplate=XmlItemTemplate)]
[OperationContract]
public Counter GetItemInXml()
{
return HandleGet();
}
...
Con esto en su lugar, deberá definir un perfil de almacenamiento en caché de salida ASP.NET denominado "CacheFor60Seconds" dentro del archivo web.config. En el web.config siguiente se muestra cómo se puede hacer esto:
> de configuración de <
<system.web>
< > de almacenamiento en caché
<outputCacheSettings>
< > outputCacheProfiles
<add name="CacheFor60Seconds" duration="60" varyByParam="format" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
</configuration>
Observe cómo el perfil de almacenamiento en caché de ASP.NET está establecido para almacenar en caché la salida durante 60 segundos y está configurado para variar la memoria caché por la variable de cadena de consulta "format". Esto es importante porque el servicio en cuestión admite XML y JSON, que se controla a través de la variable de formato (por ejemplo, "?format=json"). El servicio debe almacenar en caché las respuestas XML y JSON independientemente entre sí.
Este archivo de configuración también muestra cómo habilitar el modo de compatibilidad de ASP.NET, que es necesario cuando desea aprovechar varias características de ASP.NET, como el almacenamiento en caché de salida.
El atributo [AspNetCacheProfile] facilita mucho el uso del almacenamiento en caché HTTP sin necesidad de trabajar directamente con los encabezados de almacenamiento en caché HTTP. El comportamiento de WCF subyacente se encarga de insertar los encabezados HTTP Cache-Control, Date, Expires y Vary HTTP en la respuesta, que los clientes también pueden aprovechar para determinar la semántica de almacenamiento en caché adecuada en torno a los futuros recorridos de ida y vuelta.
Selección de formato de mensaje
Si examina la figura 19, observará que el servicio CounterResource admite formatos XML y JSON para las operaciones GET, POST y PUT. Esto ha sido posible desde WCF 3.5 a través de las propiedades RequestFormat y ResponseFormat que se encuentran en los atributos [WebGet] y [WebInvoke].
Lo he logrado en el servicio CounterResource mediante la definición de un contrato de operación independiente para cada versión ( una para la versión XML y otra para la versión JSON), como se muestra aquí:
...
[WebGet(UriTemplate="")]
[OperationContract]
public Counter GetItemInXml()
{
return HandleGet();
}
[WebGet(UriTemplate = "?format=json", ResponseFormat=WebMessageFormat.Json)]
[OperationContract]
public Counter GetItemInJson()
{
return HandleGet();
}
...
Por desgracia, esto significa que, para cada operación lógica, necesita dos contratos de operación si desea admitir formatos XML y JSON. En el caso del servicio CounterResource, se necesitaba tener seis contratos de operación para admitir XML y JSON para las operaciones GET, POST y PUT.
WCF 4 facilita mucho este escenario al proporcionar compatibilidad con la selección de formato automática basada en encabezados HTTP "Accept", que es una mejor manera de hacerlo. Déjame explicar cómo funciona esto.
En primer lugar, solo necesitamos un contrato de operación único para cada operación lógica:
[WebGet(UriTemplate="")]
[OperationContract]
public Counter GetItem()
{
return HandleGet();
}
A continuación, la selección de formato automático en el webHttpEndpoint estándar como se muestra aquí:
> de configuración de <
<system.serviceModel>
> standardEndpoints de <
<webHttpEndpoint>
<!-- el punto de conexión estándar "" se usa para crear automáticamente un punto de conexión web. -->
<standardEndpoint name="" helpEnabled="true"
automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</configuration>
Con esto en su lugar, el servicio ahora es capaz de controlar y devolver mensajes XML o JSON. Determina el formato que se va a usar inspeccionando primero el encabezado HTTP Accept que se encuentra en el mensaje de solicitud. Si esto no funciona, usará el mismo formato de mensaje que el mensaje de solicitud, suponiendo que sea XML o JSON; de lo contrario, usará el formato predeterminado para la operación específica.
Ahora es el cliente para determinar qué formato usar a través de los encabezados HTTP Content-Type y Accept. El encabezado Content-Type especifica el formato en el mensaje de solicitud mientras que el encabezado Accept indica el formato que el cliente "acepta" del servicio. Por ejemplo, si el cliente quiere recibir JSON de nuevo del servicio, debe especificar el siguiente encabezado HTTP Accept en la solicitud:
Aceptar: application/json
Esto es fácil de lograr en cualquier modelo de programación de cliente HTTP y también es fácil de hacer mediante herramientas como Fiddler. En la figura 21 se muestra cómo usar Fiddler para recuperar JSON de nuestro nuevo servicio y mejorar.
Figura 21: Usar el encabezado ACCEPT HTTP para recuperar JSON
WCF 4 también proporciona nuevas API que facilitan la especificación explícita del formato de mensaje en tiempo de ejecución. El código siguiente muestra cómo podemos extender la operación GetItem escribiendo código que busca primero un parámetro de cadena de consulta "format" y establece explícitamente el formato de respuesta en consecuencia. Si no encuentra un parámetro de "formato", simplemente se basará en el encabezado Accept como antes.
[WebGet(UriTemplate="")]
[OperationContract]
public Counter GetItem()
{
si se ha especificado un parámetro de cadena de consulta de formato,
establezca el formato de respuesta en ese. Si no es así
el parámetro de cadena de consulta existe, se usará el encabezado Accept.
string formatQueryStringValue =
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters[
"format"];
if (!string. IsNullOrEmpty(formatQueryStringValue))
{
if (formatQueryStringValue.Equals("xml",
System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
}
else if (formatQueryStringValue.Equals("json",
System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
más
{
inicie una nueva cadena webFaultException<>(cadena. Format("Formato no admitido '{0}'",
formatQueryStringValue), HttpStatusCode.BadRequest);
}
}
return HandleGet();
}
Al final, estas nuevas características hacen que ya no tenga que codificar de forma rígida un tipo de formato de mensaje en los contratos de operación [WebGet] y [WebInvoke], como hizo en WCF 3.5.
Control de errores RESTful
Dado que los servicios RESTful no usan SOAP, ya no tiene el mecanismo de error SOAP estándar a su disposición, lo que permite asignar automáticamente entre excepciones de .NET y mensajes de error SOAP. Al compilar servicios REST en WCF 3.5, tenía que construir manualmente el mensaje de respuesta HTTP siempre que quisiera personalizar el mensaje de error HTTP enviado de vuelta al cliente.
En WCF 4, encontrará una nueva clase denominada WebFaultException<T> que facilita la transmisión de "errores web" (por ejemplo, errores HTTP) a los consumidores. Funciona muy como FaultException<T> en WCF, pero puede especificar un código de estado HTTP y un tipo de detalle para proporcionar más detalles.
En el ejemplo siguiente se muestra cómo devolver un error HTTP 400 (solicitud incorrecta) cuando el usuario especifica un tipo de formato no admitido: simplemente uso la cadena para el tipo de detalle:
if (!string. IsNullOrEmpty(format))
{
if (formato. Equals("xml", System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
}
else if (format. Equals("json", System.StringComparison.OrdinalIgnoreCase))
{
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
}
más
{
inicie una nueva cadena webFaultException<>(cadena. Format("Formato no admitido '{0}'",
format), HttpStatusCode.BadRequest);
}
}
Esta nueva característica hace que sea mucho más natural devolver mensajes de error HTTP estándar simplemente iniciando excepciones como normalmente en los servicios basados en SOAP.
Integración de WCF con rutas de ASP.NET
Hay muchas similitudes entre las funcionalidades de enrutamiento basadas en url que se encuentran en WCF 4 y ASP.NET 4. Ambos le permiten hacer lo mismo: básicamente, asignar plantillas de dirección URL a métodos en clases. Sin embargo, hay una diferencia significativa entre los dos modelos.
El enfoque WCF 4 vincula el diseño de la plantilla de dirección URL a una sola clase a través de los atributos [WebGet] y [WebInvoke] aplicados a la definición de clase durante el desarrollo. A medida que el servicio crece y evoluciona con el tiempo, esto puede dar lugar a un diseño monolítico grande que no se puede factorizar en fragmentos más pequeños. El enfoque ASP.NET 4, por otro lado, desacopla la lógica de enrutamiento de la definición de clase de destino, lo que le permite asignar las rutas de servicio en numerosas definiciones de clase cuando lo desee.
WCF 4 ahora proporciona la capacidad de integrar el motor de enrutamiento de ASP.NET con los servicios WCF 4, lo que permite beneficiarse del modelo de enrutamiento de ASP.NET sobre los servicios WCF.
Esto se logra en el método Global.asax RegisterRoutes aprovechando la nueva clase ServiceRoute que le permite asignar una ruta de ASP.NET a una clase de servicio WCF:
private void RegisterRoutes()
{
Factoría de WebServiceHostFactory = new WebServiceHostFactory();
RouteTable.Routes.Add(new ServiceRoute("Bookmarks", factory,
typeof(BookmarkService)));
RouteTable.Routes.Add(new ServiceRoute("Users", factory,
typeof(UserService)));
}
Estoy llamando a RouteTable.Routes.Add dos veces para agregar nuevas rutas para dos clases de servicio WCF diferentes. La primera ruta asigna "/Bookmarks" a la clase BookmarkService mientras que la segunda ruta asigna "/Users" a la clase UserService. Observe cómo se configuran ambas rutas para usar WebServiceHostFactory.
Ahora, cuando usamos los atributos [WebGet] y [WebInvoke] en las definiciones de clase de servicio WCF, usaremos rutas de acceso relativas y serán relativas a la ruta de ASP.NET especificada aquí.
Plantillas de proyecto REST
Una de las características ordenadas de Visual Studio 2010 es el Administrador de extensiones, al que puede acceder desde Herramientas | Administrador de extensiones. El Administrador de extensiones permite que Microsoft y otros terceros integren nuevas plantillas de proyecto, controles y herramientas en la experiencia de Visual Studio 2010 con un simple clic de un botón. Para los equipos de productos de Microsoft, esto es excelente porque permite enviar cosas después de RTM y seguir haciéndolos extensiones oficiales proporcionadas por Microsoft.
Si abre el Administrador de extensiones y expande el nodo "Plantillas", encontrará un nodo secundario denominado "WCF". Haga clic en "WCF" y debería ver cuatro nuevas plantillas de servicio REST de WCF: una para cada versión de .NET (3.5 frente a 4.0) y una por lenguaje (C# frente a VB.NET) como se muestra en la figura 22.
Figura 22: Nuevas plantillas de proyecto REST de WCF en el administrador de extensiones
Puede seleccionar estas nuevas plantillas de proyecto y descargarlas en la instalación local de Visual Studio 2010 y usarlas como cualquier otro tipo de proyecto. Encontrará el nuevo tipo de proyecto REST en Visual C# | Web | Aplicación de servicio REST de WCF (suponiendo que instaló la versión de C#).
Cuando lo use para crear un nuevo proyecto, observará que la aplicación de servicio REST de WCF que genera usa la mayoría de las nuevas características de WCF 4 que he resaltado en esta sección.
Más información
Además de lo que hemos analizado aquí, WCF 4 incluye varias funcionalidades rest más avanzadas y nuevas extensiones de API de WCF que simplifican cosas como controlar formatos de mensaje personalizados (no XML o JSON), devolver "vistas" basadas en plantillas con la nueva funcionalidad T4, implementar compatibilidad condicional GET y ETag, implementar la simultaneidad optimista y generar vínculos salientes.
Para obtener más información sobre todas estas nuevas características de WCF 4, incluidas las que no teníamos espacio para cubrir aquí, vaya al blog del punto de conexión de .NET y busque la entrada en Introducción a wcF WebHttp Services en .NET 4. El equipo ha proporcionado una serie exhaustiva de blog de 12 partes que recorre cada nueva entrada de blog de una característica cada vez, junto con una gran cantidad de código de ejemplo y instrucciones paso a paso.
Servicios de flujo de trabajo
Una de las áreas de características que recibió la mayor atención en .NET 4 era la de "servicios de flujo de trabajo". Microsoft ha invertido mucho en mejorar la integración entre WCF y WF con el fin de proporcionar un modelo de programación enriquecido y declarativo para crear una amplia variedad de aplicaciones.
Descripción de los servicios de flujo de trabajo
WF proporciona un modelo de programación declarativo que eleva el nivel de abstracción para aquellos que escriben la lógica. Lo que WF le ofrece en última instancia es un marco para escribir sus propios lenguajes definiendo su propia biblioteca de actividades de dominio empresarial. A continuación, proporciona un diseñador visual para redactar esas actividades en programas y un entorno de ejecución que sabe cómo administrar la ejecución de esos programas.
WF proporciona un modelo especialmente bueno para implementar aplicaciones de larga duración que necesitan esperar a que se produzcan distintos tipos de eventos externos, como recibir un mensaje de otro sistema. Su modelo de persistencia permite hacer un uso eficaz de los recursos del sistema manteniendo el programa en memoria solo cuando se está ejecutando realmente. Cuando el programa espera a que se produzca un evento externo, se puede conservar en la base de datos liberando así los recursos del sistema que estaba usando.
Figura 23: servicios de flujo de trabajo
Lo que hace que WF sea diferente de otros marcos de desarrollo es que proporciona estas características mientras sigue permitiendo programar mediante una lógica de programación de control de flujo secuencial, que suele ser mucho más fácil para los desarrolladores leer y escribir el código para comprender.
Si va a crear servicios WCF que son de larga duración por naturaleza o podría beneficiarse de algunas de las otras ventajas que acabo de describir, puede implementar los servicios WCF mediante un flujo de trabajo de WF. Y puede usar WF para coordinar la lógica de consumo de otros servicios externos.
En la figura 23 se muestran estos conceptos.
Aunque esto ha sido posible desde .NET 3.5, .NET 4 ha avanzado considerablemente en mejorar la experiencia del desarrollador y proporcionar algunas de las características necesarias que faltaban en .NET 3.5.
Al combinar los mundos de WCF y WF, obtiene lo mejor de ambos mundos. Obtiene un modelo de programación declarativo, una experiencia fácil de desarrolladores gracias a los diseñadores de WF, un entorno de ejecución eficaz para administrar servicios de larga duración y la amplia flexibilidad de comunicación que ofrece WCF.
Creación del primer servicio de flujo de trabajo
Para darle una idea de la nueva experiencia de desarrollador de servicios de flujo de trabajo, le guiaré a través de un ejemplo completo de creación de uno con .NET 4 y el nuevo diseñador de Visual Studio 2010.
Si abre Visual Studio 2010 y selecciona Archivo | Nuevo proyecto, encontrará un nuevo proyecto de flujo de trabajo en las plantillas de WCF: se denomina "Aplicación de servicio de flujo de trabajo WCF". Seleccionaré esta plantilla de proyecto y le asignaré el nombre "HelloWorldWorkflowService" y crearemos el nuevo proyecto.
Figura 24: Plantillas de proyecto de Servicios de flujo de trabajo de Visual Studio 2010
Una vez creado, el nuevo proyecto simplemente contendrá dos archivos: un archivo Service1.xamlx que contiene la definición del servicio de flujo de trabajo declarativo y un archivo Web.config que contiene la configuración del servicio.
Una de las cosas más atractivas sobre el nuevo modelo de servicios de flujo de trabajo en .NET 4 es que toda la definición de servicio se puede definir en XAML. En nuestro nuevo proyecto, el archivo Service1.xamlx contiene la definición del servicio y la implementación es simplemente un flujo de trabajo basado en XAML. El archivo Web.config contiene la configuración del servicio WCF: aquí es donde definirá los puntos de conexión y los comportamientos del servicio de flujo de trabajo.
En la figura 25 se muestra el aspecto del diseñador de Visual Studio 2010 para el archivo Service1.xamlx. Tenga en cuenta que es solo el diseñador de flujo de trabajo estándar, solo en este caso concreto el flujo de trabajo que estamos diseñando servirá como una implementación del servicio WCF. La clave para integrar esta definición de flujo de trabajo con WCF es el nuevo WorkflowServiceHost y el conjunto de actividades de WCF "Messaging" (vea el Cuadro de herramientas en la figura 25).
Figura 25: Diseño de un servicio de flujo de trabajo en Visual Studio 2010
El servicio de flujo de trabajo esqueleto proporcionado con esta plantilla de proyecto contiene una actividad Receive seguida de una actividad Send. Observe que la actividad Receive contiene una propiedad OperationName y está establecida actualmente en "GetData". También tiene una propiedad Content para enlazar el mensaje entrante a una variable local definida dentro de la actividad Sequence( en este caso, la variable se denomina "datos" y es de tipo Int32 (vea la ventana Variables debajo del servicio de diseño de flujo de trabajo en la figura 25).
La actividad Receive también está configurada para activar el servicio, lo que significa que en el mensaje entrante puede hacer que se cree una nueva instancia del flujo de trabajo. Observe que la propiedad CanCreateInstance está activada dentro de la ventana Propiedades de la derecha de la figura 25).
Vamos a personalizar este servicio de flujo de trabajo un poco. En primer lugar, cambiaré el nombre de archivo del servicio de Service1.xamlx a HelloWorld.xamlx. A continuación, cambiaré el nombre del servicio a "HelloWorldService" en la ventana adecuada. A continuación, cambiaré la propiedad OperationName de la actividad receive a "SayHello". Y, por último, definiré una nueva variable denominada "personName" de tipo String dentro de la secuencia.
A continuación, enlazaré la propiedad Content de la actividad Receive a la variable "personName", como se muestra en la figura 26. A continuación, enlazaré la propiedad Content de la actividad Send a una cadena con el formato "hello {0}!" insertar la variable personName en el marcador de posición. A continuación, guardaré el archivo Service1.xamlx resultante.
Figura 26: Definición de la propiedad Content para la actividad Receive
En este momento, continúe y abra el archivo HelloWorld.xamlx e inspeccione su contenido. Para ello, haga clic con el botón derecho en el archivo en el Explorador de soluciones y seleccione "Abrir con..." seguido de "Editor XML". Esto te permite ver la definición XAML sin formato para el servicio. Es importante tener en cuenta que la definición xaml representa la implementación completa del servicio. No hay ningún código de C# en absoluto.
En este ejemplo, nos basaremos en el punto de conexión HTTP predeterminado y se supone que tenemos un comportamiento de servicio estándar que habilita automáticamente los metadatos del servicio, por lo que no necesitamos ninguna configuración de WCF.
Ahora estamos listos para probar nuestro servicio de flujo de trabajo basado en XAML. Esto es tan fácil como presionar F5 en Visual Studio 2010. Al presionar F5, se cargará el servicio de flujo de trabajo en el servidor de desarrollo de ASP.NET y se mostrará el cliente de prueba de WCF. El cliente de prueba de WCF se conecta automáticamente al servicio de flujo de trabajo, descarga los metadatos de WSDL y permite probar la lógica del servicio de flujo de trabajo (consulte la figura 27).
Figura 27: Probar el servicio de flujo de trabajo
Esto muestra que la infraestructura de servicios de flujo de trabajo puede generar dinámicamente una definición de WSDL para el servicio inspeccionando las actividades de envío y recepción que se usan en la definición del flujo de trabajo y los tipos de mensajes que envían y reciben.
Esto completa el sencillo tutorial de creación del primer servicio de flujo de trabajo declarativo en .NET 4. Hemos visto que la implementación del servicio se define completamente en XAML y que usas las actividades De envío y recepción para modelar las interacciones de mensajería dentro del flujo de trabajo. También vimos que .NET 4 incluye compatibilidad de hospedaje con archivos .xamlx, lo que elimina la necesidad de archivos .svc adicionales. Continuaremos profundizando en cada una de estas áreas con más detalle en las secciones siguientes.
Hospedaje de servicios de flujo de trabajo
.NET 4 incluye una infraestructura de hospedaje nueva y mejorada para los servicios de flujo de trabajo. Puede hospedar servicios de flujo de trabajo en IIS/WAS o en sus propias aplicaciones mediante WorkflowServiceHost. La infraestructura de hospedaje de .NET 4 proporciona los elementos necesarios para administrar instancias de flujo de trabajo de larga duración y para correlacionar mensajes cuando se ejecutan varias instancias de servicio simultáneamente. .NET 4 también proporciona un punto de conexión de control de flujo de trabajo estándar para administrar instancias de flujo de trabajo de forma remota.
Hospedaje en IIS/ASP.NET
En el ejemplo sencillo HelloWorldWorkflowService que hemos descrito en la sección anterior se muestra cómo hospedar servicios de flujo de trabajo en IIS/ASP.NET. Aunque probamos el servicio mediante el servidor de desarrollo de ASP.NET, habría funcionado igual en IIS mediante un grupo de aplicaciones de ASP.NET 4. .NET 4 instala el controlador necesario para las solicitudes .xamlx, que controla la creación de WorkflowServiceHost en segundo plano y la activación de las instancias de flujo de trabajo individuales.
Aunque hemos usado el enfoque de "configuración cero" en nuestro primer ejemplo, puede configurar los servicios de flujo de trabajo dentro de Web.config como cualquier otro servicio WCF. Aquí es donde configuraría los puntos de conexión y comportamientos de WCF que le gustaría usar. Puede exponer tantos puntos de conexión como quiera en los servicios de flujo de trabajo, pero debe considerar el uso de uno de los nuevos enlaces de "contexto" (por ejemplo, BasicHttpContextBinding, WSHttpContextBinding o NetTcpContextBinding) que administran la comunicación específica de la instancia.
En el ejemplo siguiente se muestra cómo configurar un servicio de flujo de trabajo con dos puntos de conexión HTTP:
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="HelloWorldWorkflowService">
<endpoint binding="basicHttpContextBinding" contract="IHelloWorld"/>
<endpoint address="ws" binding="wsHttpContextBinding"
contract="IHelloWorld"/>
</service>
...
También puede configurar el servicio de flujo de trabajo con comportamientos de WCF. En el ejemplo siguiente se muestra cómo configurar este servicio de flujo de trabajo con algunos de los comportamientos de WCF estándar:
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="HelloWorldWorkflowService"
behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior" >
<endpoint binding="basicHttpContextBinding" contract="IHelloWorld"/>
<endpoint address="ws" binding="wsHttpContextBinding"
contract="IHelloWorld"/>
</service>
</services>
<comportamientos>
< > serviceBehaviors
<behavior name="HelloWorldWorkflowService.Service1Behavior">
<serviceDebug includeExceptionDetailInFaults="False" />
<serviceMetadata httpGetEnabled="True"/>
</behavior>
< > /serviceBehaviors
...
Además de estos comportamientos estándar de WCF, .NET 4 incluye algunos nuevos comportamientos específicos de WF para controlar la persistencia del flujo de trabajo, el seguimiento de flujos de trabajo y otros comportamientos en tiempo de ejecución de flujo de trabajo junto con los servicios de flujo de trabajo. Consulte los ejemplos del SDK de .NET 4 para obtener más información.
Autohospedaje con WorkflowServiceHost
Aunque .NET 3.5 llegó con una clase WorkflowServiceHost para hospedar servicios de flujo de trabajo, era necesario rediseñar dados todos los cambios en el entorno de ejecución y el modelo de programación de WF en .NET 4. Por lo tanto, .NET 4 incluye una nueva clase WorkflowServiceHost que se encuentra en el ensamblado System.ServiceModel.Activities.
La clase WorkflowServiceHost facilita el hospedaje de servicios de flujo de trabajo en su propia aplicación, ya sea una aplicación de consola, una aplicación WPF o un servicio de Windows. En el fragmento de código siguiente se muestra cómo usar WorkflowServiceHost en su propio código de aplicación:
...
WorkflowServiceHost host = new WorkflowServiceHost("HelloWorld.xamlx",
new Uri("https://localhost:8080/helloworld"));
anfitrión. AddDefaultEndpoints();
anfitrión. Description.Behaviors.Add(
new ServiceMetadataBehavior { HttpGetEnabled = true });
anfitrión. Open();
Console.WriteLine("Host is open");
Console.ReadLine();
...
Como puede ver, tanto si decide hospedar los servicios de flujo de trabajo en IIS/ASP.NET como en sus propias aplicaciones, son tan fáciles de hospedar como cualquier servicio WCF.
WorkflowControlEndpoint
Hospedar un servicio de flujo de trabajo se encarga de inicializar el entorno de ejecución de WF y activar nuevas instancias de flujo de trabajo al activar mensajes que llegan a través de uno de los puntos de conexión expuestos. Además de esta funcionalidad básica de hospedaje, también es importante poder administrar la configuración y ejecución de estas instancias de flujo de trabajo en ejecución de forma remota. .NET 4 facilita este proceso proporcionando un workflowControlEndpoint estándar que puede exponer en los servicios de flujo de trabajo.
En el ejemplo siguiente se muestra cómo agregar el punto de conexión de control de flujo de trabajo estándar al servicio en un escenario de hospedaje personalizado:
...
WorkflowServiceHost host = new WorkflowServiceHost("HelloWorld.xamlx",
new Uri("https://localhost:8080/helloworld"));
anfitrión. AddDefaultEndpoints();
WorkflowControlEndpoint wce = new WorkflowControlEndpoint(
new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/helloworld/WCE"));
anfitrión. AddServiceEndpoint(wce);
anfitrión. Open();
...
Si va a hospedar en IIS/ASP.NET, puede agregar el workflowControlEndpoint estándar como se indica a continuación:
> de configuración de <
<system.serviceModel>
> servicios de <
<service name="HelloWorldWorkflowService"
behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior" >
<endpoint address="" binding="basicHttpContextBinding" contract="IHelloWorld"/>
<endpoint address="ws" binding="wsHttpContextBinding" contract="IHelloWorld"/>
<endpoint address="wce" binding="wsHttpBinding" kind="workflowControlEndpoint"/>
</service>
...
WorkflowControlEndpoint permitirá que el servicio de flujo de trabajo se administre dentro de diferentes entornos de hospedaje. Uno de estos entornos es posible mediante AppFabric de Windows Server, que estará disponible en una versión futura de Windows Server.
Actividades de envío y recepción
.NET 3.5 llegó con dos actividades de mensajería : Enviar y recibir, para enviar y recibir mensajes mediante WCF, pero estaban bastante limitados en términos de su funcionalidad. .NET 4 mejora las actividades de envío y recepción con algunas características adicionales (por ejemplo, correlación) y agrega algunas actividades de mensajería más: SendReply y ReceiveReply, que simplifican el modelado de operaciones de solicitud-respuesta.
Cuando se usan las actividades de envío y recepción dentro de un servicio de flujo de trabajo, básicamente se usan para modelar el contrato de servicio que se expondrá a los clientes a través de la definición de WSDL. Cuando se usa la actividad Receive, se le asigna un nombre de operación y se asigna el mensaje entrante a un tipo de .NET. Aunque hemos estado usando cadenas simples hasta ahora, también puede usar contratos de datos complejos definidos por el usuario. Vamos a actualizar HelloWorldWorkflowService para usar el siguiente tipo de contrato de datos:
[DataContract(Namespace="")]
public class Person
{
[DataMember]
public string Id;
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
}
Si agregamos esta definición de clase al proyecto web y regresamos a HelloWorld.xamlx, podemos definir una nueva variable denominada "personMsg" de tipo Person (el contrato de datos definido anteriormente). A continuación, podemos asignar las actividades ReceiveRequest y SendResponse a la variable personMsg a través de la propiedad "Content". Para ello, basta con seleccionar cada actividad y presionar el botón "Contenido" para abrir la ventana "Definición de contenido". A continuación, escriba "personMsg" en el cuadro de texto "Datos del mensaje" y "Persona" en el cuadro de texto "Tipo de mensaje". Tendrá que hacerlo para ambas actividades.
Las actividades De envío y recepción también permiten configurar otros aspectos del servicio WCF, como el nombre de la operación, el nombre del contrato de servicio, la acción, la colección de tipos conocidos, el nivel de protección y el serializador que se va a usar en tiempo de ejecución (por ejemplo, DataContractSerializer frente a XmlSerializer). En la actividad Enviar, también se especifican los detalles del punto de conexión de destino. Todas estas opciones de configuración se pueden configurar a través de la ventana Propiedades cuando se selecciona la actividad Enviar o recibir.
Con estos cambios implementados, puedes volver a probar HelloWorld.xamlx para ver que la operación SayHello ahora está definida para recibir y devolver mensajes de tipo Person.
Definición de operaciones de Request-Reply
Para facilitar el modelado de las operaciones de solicitud-respuesta, .NET 4 presenta algunas actividades nuevas para modelar estos tipos de interacciones. Una es la actividad SendReply que puede combinar con una actividad Receive para implementar una operación de solicitud-respuesta dentro del servicio. También hay una actividad ReceiveReply que puede combinar con una actividad Send al invocar un servicio externo dentro de un flujo de trabajo.
.NET 4 también incluye algunas actividades de nivel superior denominadas ReceiveAndSendReply y SendAndReceiveReply, que verá en el cuadro de herramientas de Visual Studio 2010. Estas actividades simplemente componen Receive/Send con SendReply/ReceiveReply respectivamente y facilitan su uso. Al arrastrarlos a la superficie de diseño del flujo de trabajo, verá que se expanden a una secuencia que contiene una actividad de envío o recepción seguida de la actividad "respuesta" adecuada.
Agregar referencia de servicio
Para facilitar aún más el consumo de servicios externos, Visual Studio 2010 también proporciona una característica de "Agregar referencia de servicio" que funciona como lo esperaría, excepto en lugar de generar una definición de clase de proxy de cliente que genera un conjunto de actividades del lado cliente. Después de seleccionar "Agregar referencia de servicio" y especificar la dirección de la definición de WSDL, Visual Studio descarga el WSDL y genera una actividad personalizada para cada operación que se encuentra en la definición de WSDL.
Veamos un ejemplo sencillo. Supongamos que hay una calculatorService con las operaciones Agregar, Restar, Multiplicar y Dividir que queremos usar desde nuestro servicio de flujo de trabajo. Simplemente podemos seleccionar "Agregar referencia de servicio" y especificar la ubicación de la definición de WSDL CalculatorService, como se muestra en la figura 28.
Figura 28: Agregar de referencia de servicio
Una vez que presionamos Aceptar para agregar la referencia de servicio, Visual Studio descarga la definición de WSDL y genera cuatro actividades personalizadas que aparecerán en el cuadro de herramientas. Ahora tiene actividades Agregar, Restar, Multiplicar y Dividir que son fáciles de usar dentro de los flujos de trabajo para invocar el servicio externo.
Correlación
Al compilar sistemas que constan de servicios de flujo de trabajo de larga duración, es habitual tener numerosas instancias de un único servicio de flujo de trabajo que se ejecuta simultáneamente esperando que se produzca el mismo evento (por ejemplo, esperar a que llegue un mensaje específico antes de continuar). El desafío con este escenario es que necesita una manera de correlacionar el mensaje entrante con la instancia de flujo de trabajo correcta. Normalmente, la forma de determinar la instancia de flujo de trabajo correcta es inspeccionando el contenido del mensaje entrante.
.NET 4 incluye una sofisticada característica de correlación de mensajes basada en contenido que puede usar junto con las actividades enviar y recibir que acabamos de analizar. La implementación de esta característica gira en torno a lo que se conoce como "identificadores de correlación", otro nuevo concepto en .NET 4.
Para empezar a usar la correlación, primero debe definir una variable de flujo de trabajo de tipo CorrelationHandle. Puede considerar esta variable como punto de encuentro para conectar un fragmento de datos de (potencialmente) dos mensajes diferentes (procesados por dos actividades de mensajería diferentes). Las actividades Enviar y Recibir proporcionan una propiedad CorrelationsWith para especificar una variable CorrelationHandle.
Para correlacionar los mensajes que envían varias actividades de envío o recepción, debe especificar el mismo identificador de correlación en todas las actividades que desean participar en la correlación. A continuación, en cada actividad, configure la clave de correlación a la que se asignará en el mensaje que procesa esa actividad concreta (por ejemplo, el elemento SSN de un mensaje podría correlacionarse con el elemento CustomerId en otro mensaje). Las claves de correlación se definen mediante expresiones XPath que se evalúan en los mensajes.
Vamos a ampliar helloWorldWorkflowService para ver un ejemplo de cómo funciona esto. Actualmente, el ejemplo con el que hemos estado trabajando recibe un mensaje person y lo devuelve al cliente. Vamos a agregar otra actividad ReceiveAndSendReply justo debajo de la actividad SendResponse en la parte inferior del flujo de trabajo. Al hacerlo, se agrega Sequence que contiene otra recepción seguida de otro SendReply. Daré a la actividad Receive un nombre de operación de "Finish" y vamos a requerir al cliente que proporcione un saludo en el mensaje Finish que usaremos para construir la respuesta final de saludo.
En otras palabras, los clientes llamarán primero a SayHello proporcionando su nombre completo para iniciar una nueva instancia de servicio de flujo de trabajo. A continuación, la instancia de flujo de trabajo esperará hasta que el cliente llame a Finalizar la entrega de un saludo (y esto podría tardar minutos, horas o días, durante el cual el flujo de trabajo podría persistir). Una vez que el cliente llama a Finalizar el saludo, el flujo de trabajo crea un saludo con el saludo y el nombre original y lo devuelve. En este escenario, podemos tener fácilmente varias instancias de flujo de trabajo en ejecución esperando a que se llame a la operación Finish, por lo que definitivamente es necesario correlacionar el mensaje Finish con el mensaje SayHello anterior. Como este es el caso, necesito asociar la actividad Receive para "Finish" con el mismo identificador de correlación que se especificó en la actividad "SayHello" (nameHandle).
Ahora echemos un vistazo al contenido del mensaje que vamos a correlacionar entre estas dos actividades. En este ejemplo, voy a usar los dos tipos de contrato de datos siguientes para modelar los mensajes:
[DataContract(Namespace="")]
public class Person
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
[DataContract(Namespace = "")]
public class Greeting
{
[DataMember]
public string Salutation { get; set; }
[DataMember]
public string NameKey { get; set; }
}
La actividad Receive para "SayHello" está configurada para usar el mensaje Person y la actividad Receive para "Finish" está configurada para usar el mensaje Greeting. Vamos a suponer que el valor lastName siempre es único para poder usarlo como valor único para la correlación. Por lo tanto, cuando el cliente envía el mensaje "Finish", debe proporcionar el mismo valor LastName usado en el mensaje anterior "SayHello".
Ahora vamos a ver cómo configurar el identificador de correlación para configurar esto. Ya he definido una variable CorrelationHandle dentro de la secuencia denominada "handle". Lo primero que debemos hacer es "inicializar" el identificador de correlación dentro de la actividad receive "SayHello". Por lo tanto, seleccione la actividad "SayHello" Receive y presione el botón situado junto al cuadro de texto "CorrelationInitializers".
Aquí debe seleccionar "Inicializador de correlación de consultas" en el cuadro desplegable y, a continuación, debería poder elegir la propiedad "LastName" para el campo de consulta (esto debería generar una expresión XPath de "sm:body()/xg0:Person/xg0:LastName", como se muestra en la figura 29).
A continuación, es necesario especificar que la actividad de recepción "Finalizar" con correlacione en el mismo identificador. Seleccione la actividad "Finalizar" Receive y presione el botón "Correlacionar". A continuación, especifique "handle" para el identificador "CorrelatesWith" y, a continuación, seleccione "NameKey" para el campo de consulta (vea la figura 30).
Figura 29: Definición de una clave de correlación para la "SayHello"
Figura 30: Definición de una clave de correlación para el "Finish"
Esto significa, en última instancia, que el elemento LastName del mensaje Person debe coincidir con el elemento NameKey en el mensaje Greeting en las dos solicitudes independientes. Con esto en su lugar, la infraestructura de flujo de trabajo podrá correlacionar automáticamente los mensajes de nosotros y enrutar los mensajes entrantes "Finish" a la instancia de flujo de trabajo correcta (basada en "LastName/NameKey").
La actividad final SendReply devuelve la siguiente cadena con formato al autor de la llamada, incluida la información de "personMsg" original y el nuevo "greetingMsg":
String.Format("{0}{1}{2}!",
greetingMsg.Salutation, personMsg.FirstName, personMsg.LastName)
Voy a probar la característica de correlación mediante el cliente de prueba de WCF para iniciar varias instancias de flujo de trabajo llamando a SayHello varias veces con diferentes valores FirstName y LastName. Después de hacerlo, deberíamos tener varias instancias en ejecución del servicio de flujo de trabajo. Ahora, podemos llamar a la operación Finalizar especificando uno de los mismos valores lastName usados en uno de los mensajes SayHello y deberíamos ver el valor firstName correspondiente usado en el saludo final devuelto al cliente (vea la figura 31).
Esto muestra la correlación basada en contenido en acción, una característica atractiva de servicios de flujo de trabajo que viene con .NET 4. Es importante tener en cuenta que también puede realizar la correlación en función de los datos de nivel de canal y protocolo, como puede haber hecho en .NET 3.5 (por ejemplo, mediante el identificador de instancia de canal o flujo de trabajo). La correlación basada en contenido es un enfoque más flexible y sofisticado para hacer lo mismo.
Figura 31: Prueba del ejemplo de correlación basado en contenido
Eso nos lleva al final de nuestra cobertura de servicios de flujo de trabajo. Solo hemos podido arañar la superficie de los servicios de flujo de trabajo en este documento, pero podrá encontrar información adicional en los ejemplos del SDK de .NET 4 y en la creciente documentación de MSDN que se encuentra en línea. Como puede ver, la combinación de WCF y WF 4 abre las puertas a un modelo de desarrollo completamente nuevo para crear servicios declarativos, de larga duración y asincrónicos que pueden disfrutar de las mejores características que ambos marcos tienen que ofrecer.
Características avanzadas varias
Además de todo lo demás que hemos hablado en este documento, WCF 4 incluye algunas características más avanzadas que puede resultarle útil. Estas características avanzadas incluyen compatibilidad mejorada con la resolución de tipos a través de DataContractResolver, la capacidad de controlar los consumidores de colas competidores mediante ReceiveContext, un nuevo codificador de flujos de bytes y un seguimiento basado en ETW de alto rendimiento.
Resolución de tipos con DataContractResolver
En WCF 3.x, hay una característica de resolución de tipos denominada "tipos conocidos". Durante la deserialización, cuando el serializador encuentra una instancia que no tiene el mismo tipo que el tipo declarado, inspecciona la lista de "tipos conocidos" declarados para averiguar qué tipo usar. Como autor del servicio, puede anotar los tipos o métodos con los atributos [KnownType] o [ServiceKnownType] para definir la lista de posibles sustituciones. Esta característica se usa normalmente para admitir la herencia y el polimorfismo.
Desafortunadamente, WCF 3.x no proporciona una manera fácil de invalidar el algoritmo de asignación de tipos usado por DataContractSerializer al realizar este tipo de resolución de tipos dinámicos en tiempo de ejecución. Para solucionar ese problema, WCF 4 proporciona la clase DataContractResolver abstracta de la que puede derivar para implementar su propio algoritmo de resolución de tipos personalizado. A continuación se muestra cómo empezar:
Clase MyDataContractResolver : DataContractResolver
{
Ensamblado;
public MyDataContractResolver(Assembly assembly)
{
this.assembly = assembly;
}
Se usa en la deserialización
Permite a los usuarios asignar el nombre xsi:type a cualquier tipo
public override Type ResolveName(string typeName, string typeNamespace,
Type declareType, DataContractResolver knownTypeResolver)
{
... // implementar la asignación
}
Se usa en la serialización
Asigna cualquier tipo a una nueva representación xsi:type
public override bool TryResolveType(Type, Type declareType,
DataContractResolver knownTypeResolver, out XmlDictionaryString typeName,
out XmlDictionaryString typeNamespace)
{
... // implementar la asignación
}
}
Una vez que haya implementado dataContractResolver personalizado, puede proporcionarlo a DataContractSerializer como se muestra aquí:
DataContractSerializer dcs = new DataContractSerializer(
typeof(Object), null, int. MaxValue, false, true, null,
new MyDataContractResolver(assembly));
Ahora, cuando se usa esta instancia de DataContractSerializer para serializar o deserializar objetos, se llamará a dataContractResolver personalizado para realizar la resolución de tipos personalizados.
Para insertar dataContractResolver personalizado en el tiempo de ejecución de WCF en segundo plano, deberá escribir un comportamiento de contrato WCF que se conecte a DataContractSerializerOperationBehavior e invalide la resolución predeterminada. El SDK de .NET 4 incluye un ejemplo completo que muestra cómo hacerlo a través de un comportamiento de contrato y un atributo personalizado denominado [KnownAssembly].
La resolución de tipos personalizados puede ser útil cuando desea invalidar los valores predeterminados de DataContractSerializer, controlar exactamente qué tipos se usan para la serialización o administrar dinámicamente los tipos conocidos.
Procesamiento de mensajes en cola con ReceiveContext
Con WCF 3.x, puede garantizar la entrega exactamente una vez de los mensajes cuando se usa NetMsmqBinding mediante colas transaccionales y la inscripción de la operación de servicio WCF en la transacción MSMQ. Cuando se producen excepciones durante el procesamiento de un mensaje, WCF garantizará que el mensaje no se pierda devolviendo el mensaje a una cola (podría devolverse a la cola de origen, una cola de mensajes dudosos o una cola de mensajes fallidos en función de la configuración).
Aunque esta funcionalidad es importante, hay algunos problemas en los que los usuarios suelen encontrarse. Una es que las transacciones son costosas y genera una gran cantidad de lectura y escritura con las colas, lo que introduce más sobrecarga y complejidad. Otro problema es que no hay ninguna manera de asegurarse de que el mismo servicio procesa el mensaje la próxima vez que se extraiga de la cola (por ejemplo, no hay ninguna manera de que un servicio determinado "bloquee" un mensaje), que es una garantía que puede querer realizar en determinados escenarios.
Para solucionar estos problemas, WCF 4 presenta una nueva API denominada ReceiveContext para procesar mensajes en cola. Con ReceiveContext, un servicio puede "ver" un mensaje en la cola para empezar a procesarlo y si algo va mal y se produce una excepción, permanece en la cola. Los servicios también pueden "bloquear" mensajes para reintentar el procesamiento en un momento posterior. ReceiveContext proporciona un mecanismo para "completar" el mensaje una vez procesado para que se pueda quitar de la cola.
Este enfoque simplifica las cosas en varios frentes porque los mensajes ya no se leen y se vuelven a escribir en colas a través de la red, y los mensajes individuales no se rebotan en distintas instancias de servicio durante el procesamiento. Echemos un vistazo a un ejemplo sencillo para ilustrar cómo funciona.
En el ejemplo siguiente se muestra cómo usar ReceiveContext para procesar mensajes que llegan a una cola:
...
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
[ReceiveContextEnabled(ManualControl = true)]
public void CalculateProduct(int firstNumber, int secondNumber)
{
ReceiveContext receiveContext;
si (! ReceiveContext.TryGet(OperationContext.Current.IncomingMessageProperties,
out receiveContext))
{
Console.WriteLine("ReceiveContext not installed/found on this machine.");
devolución;
}
if ((firstNumber * secondNumber) % 2 == (receiveCount % 2))
{
receiveContext.Complete(TimeSpan.MaxValue);
Console.WriteLine("{0} x {1} = {2}", firstNumber, secondNumber,
firstNumber * secondNumber);
}
más
{
receiveContext.Abandon(TimeSpan.MaxValue);
Console.WriteLine("{0} & {1} no procesado", firstNumber, secondNumber);
}
receiveCount++;
}
...
Suponiendo que se haya superado correctamente, llamamos a ReceiveContext.Complete para finalizar el procesamiento del mensaje, lo que hace que se quite de la cola subyacente. De lo contrario, llamamos a Abandon, que deja el mensaje en la cola para reintentos futuros. El SDK de .NET 4 incluye un ejemplo completo que puede usar para explorar esta nueva característica con más profundidad.
Optimización de la codificación con ByteStreamMessageEncodingBindingElement
En algunos escenarios de mensajería, simplemente desea transmitir "blobs" de datos binarios sin ningún ajuste ni procesamiento adicional. Para simplificar este escenario, WCF 4 incluye un nuevo ByteStreamMessageEncodingBindingElement que hace exactamente eso. Desafortunadamente, .NET 4 no incluye un nuevo enlace para este codificador, por lo que tendrá que crear un enlace personalizado para poder usarlo.
El SDK de .NET 4 incluye un ejemplo completo que muestra cómo definir un enlace personalizado que usa ByteStreamMessageEncodingBindingElement a través de una clase de enlace personalizada. Una vez que tenga la nueva clase de enlace en su lugar, el codificador de secuencia de bytes se vuelve muy fácil de usar con los servicios.
Seguimiento basado en ETW de alto rendimiento
WCF 4 también realiza algunas mejoras en el front-end de seguimiento y diagnóstico. WCF 4 ahora usa ETW para el seguimiento, lo que mejora considerablemente el rendimiento del seguimiento y proporciona una mejor integración con otras tecnologías relacionadas, como Windows Workflow Foundation, Windows Server AppFabric y las diversas tecnologías de administración que se encuentran en Windows Server, ofreciendo un mejor modelo para la plataforma.
Con ETW, los proveedores de eventos escriben eventos en sesiones y sesiones, opcionalmente, pueden conservar esos eventos en un registro. Los consumidores pueden escuchar eventos de sesión en tiempo real o leer esos eventos de un registro después del hecho. Los controladores ETW son responsables de habilitar o deshabilitar sesiones y asociar proveedores a sesiones.
El SDK de .NET 4 proporciona un ejemplo sencillo que muestra cómo aprovechar esta nueva arquitectura de seguimiento de alto rendimiento junto con los servicios WCF.
Conclusión
WCF 4 ofrece numerosas mejoras y varias características completamente nuevas que abordan algunos de los escenarios de comunicación más comunes de hoy. En primer lugar, WCF 4 resulta más fácil de usar a través del modelo de configuración simplificado y mejor compatibilidad con los valores predeterminados comunes.
Además, WCF 4 proporciona compatibilidad de primera clase con la detección y el enrutamiento de servicios, que son requisitos comunes en la mayoría de los entornos empresariales y las iniciativas de SOA grandes: estas características solo establecen WCF aparte de muchos de los marcos de trabajo de la competencia. WCF 4 también simplifica el desarrollo del servicio basado en REST y proporciona otras características de WCF más avanzadas que abordan algunos puntos débiles específicos en la actualidad.
Además de todo esto, WCF 4 proporciona una integración sofisticada con WF para proporcionar un nuevo modelo para desarrollar servicios de flujo de trabajo declarativos. Los servicios de flujo de trabajo permiten desarrollar servicios asincrónicos y de larga duración que se benefician del modelo de programación wf y del entorno de ejecución subyacente. Y gracias a las nuevas actividades basadas en WCF y la compatibilidad del diseñador que se encuentra en Visual Studio 2010, este nuevo modelo de programación se está convirtiendo en una opción de primera clase para los servicios de creación.
En .NET 4, los mundos de WCF y WF se combinan para ofrecer un modelo de programación cohesivo que le ofrece los mejores mundos que tienen que ofrecer. Para obtener más información sobre las novedades de WF 4, consulte Introducción de un desarrollador a Windows Workflow Foundation en .NET 4.
Acerca del autor
Aaron Skonnard es un cofundador de Pluralsight, un proveedor de aprendizaje de Microsoft que ofrece cursos dirigidos por instructores y desarrolladores de .NET a petición. Estos días Aaron pasa la mayor parte de su tiempo grabando Pluralsight On-Demand! cursos centrados en informática en la nube, Windows Azure, WCF y REST. Aaron ha pasado años escribiendo, hablando y enseñando desarrolladores profesionales de todo el mundo. Puede llegar a él en http://pluralsight.com/aaron y http://twitter.com/skonnard.