Compartir a través de


Vigencia personalizada

En este ejemplo se muestra cómo escribir una extensión de Windows Communication Foundation (WCF) para proporcionar servicios de duración personalizados para las instancias de servicio de WCF compartidas.

Ee960156.note(es-es,VS.100).gifNota:
El procedimiento de instalación y las instrucciones de compilación de este ejemplo se encuentran al final de este tema.

Creación de instancias compartidas

WCF proporciona varios modos de creación de instancias para las instancias de servicio. El modo Creación de instancias compartidas que se explica en este tema proporciona una manera de compartir una instancia de servicio entre varios canales. Los clientes pueden resolver la dirección del extremo de la instancia de forma local o usar un método generador en el servicio para obtener la dirección del extremo de una instancia en ejecución. Cuando tenga la dirección del extremo, puede crear un nuevo canal e iniciar la comunicación. El siguiente fragmento de código muestra el modo en que una aplicación cliente crea un canal nuevo para una instancia de servicio existente.

// Create the first channel.
IEchoService proxy = channelFactory.CreateChannel();

// Resolve the instance.
EndpointAddress epa = ((IClientChannel)proxy).ResolveInstance();

// Create new channel factory with the endpoint address resolved by 
// previous statement.
ChannelFactory<IEchoService> channelFactory2 =
                new ChannelFactory<IEchoService>("echoservice",
                epa);

// Create the second channel to the same instance.
IEchoService proxy2 = channelFactory2.CreateChannel(); 

A diferencia de los demás modos de creación de instancias, el modo de creación de instancias compartido tiene una sola manera de liberar las instancias de servicio. Cuando todos los canales se cierran para una instancia, el tiempo de ejecución de WCF del servicio inicia un temporizador. Si nadie realiza una conexión antes de que el tiempo de espera expire, WCF libera la instancia y reclama los recursos. Como parte del procedimiento de destrucción, WCF invoca el método IsIdle de todas las implementaciones de IShareableInstanceContextLifetime antes de liberar la instancia. Si todas ellas devuelven true, la instancia se libera. De lo contrario, la implementación de IShareableInstanceContextLifetime es responsable de notificar al Dispatcher el estado de inactividad utilizando un método de devolución de llamada.

De forma predeterminada, el valor de tiempo de espera de inactividad de InstanceContext es de un minuto. Sin embargo, este ejemplo muestra cómo puede ampliarlo utilizando una extensión personalizada.

Ampliar InstanceContext

En WCF, InstanceContext es el vínculo entre la instancia de servicio y Dispatcher. WCF le permite ampliar este componente de tiempo de ejecución agregando un nuevo estado o comportamiento mediante su modelo de objetos extensible. El modelo de objeto extensible se utiliza en WCF para extender las clases de tiempo de ejecución existentes con una nueva funcionalidad o agregar nuevas características del estado a un objeto. Hay tres interfaces en el modelo de objetos extensible: IExtensibleObject<T>, IExtension<T> y IExtensionCollection<T>.

La interfaz IExtensibleObject<T> se implementa mediante objetos para permitir las extensiones que pueden personalizar su funcionalidad.

La interfaz IExtension<T> se implementa mediante objetos que pueden ser extensiones de clases de tipo T.

Y finalmente, la interfaz IExtensionCollection<T> es una colección de interfaces IExtensions que permite recuperar las interfaces IExtensions por su tipo.

Por consiguiente, para extender el InstanceContext debe implementar la interfaz IExtension. En este proyecto de ejemplo, la clase CustomLeaseExtension contiene esta implementación.

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

La interfaz IExtension tiene dos métodos: Attach y Detach. Como sus nombres indican, se llama a estos dos métodos cuando el tiempo de ejecución asocia y desasocia la extensión a una instancia de la clase InstanceContext. En este ejemplo, el método Attach se usa para realizar un seguimiento del objeto InstanceContext que pertenece a la instancia actual de la extensión.

InstanceContext owner;

public void Attach(InstanceContext owner)
{
  this.owner = owner; 
}

Además, debe agregar la implementación necesaria a la extensión para proporcionar compatibilidad con la duración extendida. Por consiguiente, la interfaz ICustomLease se declara con los métodos deseados y se implementa en la clase CustomLeaseExtension.

interface ICustomLease
{
    bool IsIdle { get; }        
    InstanceContextIdleCallback Callback { get; set; }
}

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

Cuando WCF invoca el método IsIdle en la implementación de IShareableInstanceContextLifetime, esta llamada se enruta al método IsIdle de CustomLeaseExtension. A continuación, CustomLeaseExtension comprueba su estado privado para ver si el InstanceContext está inactivo. Si lo está, devuelve true. De lo contrario, inicia un temporizador con la duración extendida especificada.

public bool IsIdle
{
  get
  {
    lock (thisLock)
    {
      if (isIdle)
      {
        return true;
      }
      else
      {
        StartTimer();
        return false;
      }
    }
  }
}

En el evento Elapsed del temporizador, se llama a la función de devolución de llamada en el Distribuidor para iniciar otro ciclo de limpieza.

void idleTimer_Elapsed(object sender, ElapsedEventArgs args)
{
    idleTimer.Stop();
    isIdle = true;  
    callback(owner);
}

No hay ninguna manera de renovar el temporizador en ejecución cuando llega un mensaje nuevo para la instancia que se va a pasar al estado inactivo.

En el ejemplo se implementa IShareableInstanceContextLifetime para interceptar las llamadas al método IsIdle y enrutarlas a CustomLeaseExtension. La implementación de IShareableInstanceContextLifetime está contenida en la clase CustomLifetimeLease. Se invoca al método IsIdle cuando WCF está a punto de liberar la instancia de servicio. Sin embargo, solo hay una instancia de una implementación de ISharedSessionInstance determinada en la colección de propiedades InstanceContextLifetimes de ServiceBehavior. Esto significa que no hay ninguna manera de saber que el InstanceContext se está cerrando en el momento en que WCF comprueba el método IsIdle. Por consiguiente, este ejemplo utiliza el bloqueo de subprocesos para serializar las solicitudes al método IsIdle.

Ee960156.Important(es-es,VS.100).gif Nota:
El uso del bloqueo de subprocesos no es una solución recomendada porque la serialización puede afectar de forma grave al rendimiento de la aplicación.

Una variable de miembro privado se utiliza en la clase CustomLeaseExtension para realizar el seguimiento del valor IsIdle. Cada vez que se recupera el valor de IShareableInstanceContextLifetime, se devuelve el miembro privado IsIdle y se restablece en false. Es esencial establecer este valor en false para asegurarse de que el Distribuidor llama al método NotifyIdle.

public bool IsIdle
{
    get 
    {
       lock (thisLock)
       {
           bool idleCopy = isIdle;
           isIdle = false;
           return idleCopy;
       }
    }
}

Si la propiedad ISharedSessionLifetime.IsIdle devuelve false, el Distribuidor registra una función de devolución de llamada utilizando el método NotifyIdle. Este método recibe una referencia al InstanceContext que se va a liberar. Por consiguiente, el código de muestra puede consultar la extensión de tipo de ICustomLease y comprobar la propiedad ICustomLease.IsIdle en el estado extendido.

public void NotifyIdle(InstanceContextIdleCallback callback, 
            InstanceContext instanceContext)
{
    lock (thisLock)
    {
       ICustomLease customLease =
           instanceContext.Extensions.Find<ICustomLease>();
       customLease.Callback = callback; 
       isIdle = customLease.IsIdle;
       if (isIdle)
       {
             callback(instanceContext);
       }
    } 
}

Antes de que se compruebe la propiedad ICustomLease.IsIdle, tiene que establecerse la propiedad Callback porque es esencial para que CustomLeaseExtension notifique al Distribuidor cuando pase a estar inactivo. Si ICustomLease.IsIdle devuelve true, el miembro privado isIdle simplemente se establece en CustomLifetimeLease como true y llama al método de devolución de llamada. Dado que el código contiene un bloqueo, otros subprocesos no pueden cambiar el valor de este miembro privado. Y la siguiente vez que el Distribuidor compruebe la interfaz ISharedSessionLifetime.IsIdle, devuelve true y permite que el Distribuidor libere la instancia.

Ahora que el fundamento de la extensión personalizada está completado, tiene que enlazarse con el modelo de servicio. Para enlazar la implementación de CustomLeaseExtension en el InstanceContext, WCF proporciona la interfaz de IInstanceContextInitializer para realizar la secuencia de arranque de InstanceContext. En el ejemplo, la clase CustomLeaseInitializer implementa esta interfaz y agrega una instancia de CustomLeaseExtension a la colección de la propiedad Extensions desde la única inicialización del método. El Distribuidor llama a este método inicializando la instancia de InstanceContext.

public void Initialize(InstanceContext instanceContext, Message message)
{
  IExtension<InstanceContext> customLeaseExtension =
    new CustomLeaseExtension(timeout);
  instanceContext.Extensions.Add(customLeaseExtension);
}

Finalmente, las implementaciones de IInstanceContextInitializer y IShareableInstanceContextLifetime se enlazan con el modelo de servicio utilizando la implementación de IServiceBehavior. Esta implementación se coloca en la clase CustomLeaseTimeAttribute y también deriva de la clase base Attribute para exponer este comportamiento como un atributo. En el método IServiceBehavior.ApplyBehavior, las instancias de las implementaciones de IInstanceContextInitializer y IShareableInstanceContextLifetime se agregan respectivamente a las colecciones de propiedades InstanceContextLifetimes y InstanceContextInitializers del espacio de nombres System.ServiceModel.Dispatcher.

public void ApplyBehavior(ServiceDescription description, 
           ServiceHostBase serviceHostBase, 
           Collection<DispatchBehavior> behaviors,
           Collection<BindingParameterCollection> parameters)
{
    CustomLifetimeLease customLease = new CustomLifetimeLease();
    CustomLeaseInitializer initializer = 
                new CustomLeaseInitializer(timeout);

    foreach (DispatchBehavior dispatchBehavior in behaviors)
    {
        dispatchBehavior.InstanceContextLifetimes.Add(customLease);
        dispatchBehavior.InstanceContextInitializers.Add(initializer);
    }
}

Este comportamiento se puede agregar a una clase de servicio de ejemplo agregándolo con el atributo CustomLeaseTime.

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Shareable)]
[CustomLeaseTime(Timeout = 20000)]
public class EchoService : IEchoService
{
  //…
}

Al ejecutar el ejemplo, las solicitudes y las respuestas de operación se muestran tanto en la ventanas de la consola del cliente como del servicio. Presione Entrar en cada ventana de la consola para cerrar el servicio y el cliente.

Para configurar, compilar y ejecutar el ejemplo

  1. Asegúrese de realizar los Procedimiento de instalación única para los ejemplos de Windows Communication Foundation.

  2. Para compilar el código C# o Visual Basic .NET Edition de la solución, siga las instrucciones de Compilación de los ejemplos de Windows Communication Foundation.

  3. Para ejecutar el ejemplo en una configuración con un único equipo o con varios, siga las instrucciones de Running the Windows Communication Foundation Samples.

Ee960156.Important(es-es,VS.100).gif Nota:
Puede que los ejemplos ya estén instalados en su equipo. Compruebe el siguiente directorio (valor predeterminado) antes de continuar.

<InstallDrive>:\WF_WCF_Samples

Si no existe este directorio, vaya a la página de ejemplos de Windows Communication Foundation (WCF) y Windows Workflow Foundation (WF) Samples para .NET Framework 4 para descargar todos los ejemplos de WF y Windows Communication Foundation (WCF). Este ejemplo se encuentra en el siguiente directorio.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Instancing\Lifetime