Con la llegada de Silverlight 3, la implementación de comunicación dúplex a través de WCF experimentó mejoras significativas en estabilidad y rendimiento. A diferencia de versiones anteriores donde la serialización y la abstracción de métodos presentaban retos complejos, Polling Duplex ofrece ahora un modelo de programación mucho más cercano al estándar de WCF convencional, permitiendo que el serivdor envíe datos de forma proactiva a los clientes conectados.
1. Definición del Contrato de Retorno (Callback Contract)
El primer paso consiste en definir una interfaz que represente las operaciones que el servidor podrá invocar en el cliente. Es fundamental marcar los métodos con IsOneWay = true para asegurar una comunicación asíncrona fluida.
[ServiceContract]
public interface IReceptorEventos
{
[OperationContract(IsOneWay = true)]
void ProcesarMensaje(string contenido);
}
2. Configuración del Contrato del Servicio
Para habilitar el modo dúplex, debemos vincular el contrato de servicio con la interfaz de retorno definida anteriormente mediante la propiedad CallbackContract.
[ServiceContract(Namespace = "http://tech-examples.org/duplex", CallbackContract = typeof(IReceptorEventos))]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ServicioNotificaciones
{
private static List<IReceptorEventos> _suscriptores = new List<IReceptorEventos>();
[OperationContract]
public void RegistrarCliente()
{
IReceptorEventos callback = OperationContext.Current.GetCallbackChannel<IReceptorEventos>();
if (!_suscriptores.Contains(callback))
{
_suscriptores.Add(callback);
DifundirEstado("Nuevo usuario en línea");
}
}
private void DifundirEstado(string msg)
{
foreach (var cliente in _suscriptores)
{
try
{
cliente.ProcesarMensaje(msg);
}
catch { /* Manejo de clientes desconectados */ }
}
}
}
3. Creación de un ServiceHostFactory personalizado
Dado que en ciertos escenarios de Silverlight 3 la configuración mediante web.config no es suficeinte para el enlace de tipo dúplex, se recomienda crear una factoría personalizada para definir los puntos de conexión (endpoints) de forma programática.
public class FactoriaServicioDuplex : ServiceHostFactoryBase
{
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
return new HostNotificaciones(baseAddresses);
}
}
class HostNotificaciones : ServiceHost
{
public HostNotificaciones(params Uri[] direcciones)
{
base.InitializeDescription(typeof(ServicioNotificaciones), new UriSchemeKeyedCollection(direcciones));
base.Description.Behaviors.Add(new ServiceMetadataBehavior());
}
protected override void InitializeRuntime()
{
var enlace = new CustomBinding(
new PollingDuplexBindingElement(),
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement()
);
this.AddServiceEndpoint(typeof(ServicioNotificaciones), enlace, "");
this.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
base.InitializeRuntime();
}
}
Para aplicar esta factoría, modifique la directiva del archivo .svc del proyecto web:
<%@ ServiceHost Language="C#" Debug="true" Service="Proyecto.Web.ServicioNotificaciones" Factory="Proyecto.Web.FactoriaServicioDuplex" %>
4. Integración en el cliente Silverlight
Al agregar la referencia de servicio en Silverlight, es posible que el archivo de configuración no genere automáticamente todos los parámetros necesarios. Por ello, es una práctica común instanciar el cliente manualmente definiendo el enlace y la dirección del punto de conexión.
// Configuración del enlace en el lado del cliente
var bindingDúplex = new CustomBinding(
new PollingDuplexBindingElement(),
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement()
);
EndpointAddress direccionLocal = new EndpointAddress("http://localhost:1234/ServicioNotificaciones.svc");
// Inicialización del cliente
ServicioNotificacionesClient proxy = new ServicioNotificacionesClient(bindingDúplex, direccionLocal);
// Suscripción al evento de recepción de datos
proxy.ProcesarMensajeReceived += (s, e) =>
{
MessageBox.Show("Notificación recibida: " + e.contenido);
};
// Llamada al servidor para iniciar la sesión
proxy.RegistrarClienteAsync();
Este enfoque permite establecer un canal de comunicación persistente donde el servidor puede notificar cambios de estado o mensajes a múltiples clientes sin que estos tengan que realizar peticiones repetitivas, optimizando así el uso de recursos en aplicaciones enriquecidas de Silverlight.