Compartir a través de


Duración personalizada

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

Nota:

El procedimiento de instalación y las instrucciones de compilación de este ejemplo se encuentran al final de este artículo.

Creación de instancias compartidas

WCF proporciona varios modos de creación de instancias para las instancias de servicio. El modo de creación de instancias compartidas descrito en este artículo proporciona una manera de compartir una instancia de servicio entre varios canales. Los clientes pueden usar un método generador en el servicio y crear un nuevo canal para iniciar la comunicación. El siguiente fragmento de código muestra cómo una aplicación cliente crea un nuevo canal en una instancia de servicio existente:

// Create a header for the shared instance id
MessageHeader shareableInstanceContextHeader = MessageHeader.CreateHeader(
        CustomHeader.HeaderName,
        CustomHeader.HeaderNamespace,
        Guid.NewGuid().ToString());

// Create the channel factory
ChannelFactory<IEchoService> channelFactory =
    new ChannelFactory<IEchoService>("echoservice");

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

// Call an operation to create shared service instance
using (new OperationContextScope((IClientChannel)proxy))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy.Echo("Apple"));
}

((IChannel)proxy).Close();

// Create the second channel
IEchoService proxy2 = channelFactory.CreateChannel();

// Call an operation using the same header that will reuse the shared service instance
using (new OperationContextScope((IClientChannel)proxy2))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(shareableInstanceContextHeader);
    Console.WriteLine("Service returned: " + proxy2.Echo("Apple"));
}

A diferencia de otros modos de creación de instancias, el modo de creación de instancias compartidas tiene una manera única de liberar las instancias de servicio. De forma predeterminada, cuando todos los canales están cerrados para un InstanceContext, el tiempo de ejecución del servicio WCF comprueba si el servicio InstanceContextMode está configurado para PerCall o PerSession, y si es así, libera la instancia y reclama los recursos. Si se utiliza un IInstanceContextProvider personalizado, WCF invoca el método IsIdle de la implementación del proveedor antes de liberar la instancia. Si IsIdle devuelve true la instancia se libera, de lo contrario, la IInstanceContextProvider implementación es responsable de notificar al Dispatcher estado inactivo mediante un método de devolución de llamada. Para ello, se llama al NotifyIdle método del proveedor.

En este ejemplo se muestra cómo se puede retrasar la liberación de InstanceContext con un tiempo de espera de inactividad de 20 segundos.

Extensión de InstanceContext

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

Los objetos implementan la IExtensibleObject<T> interfaz para permitir extensiones que personalicen su funcionalidad.

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

Por último, la IExtensionCollection<T> interfaz es una colección de IExtension<T> implementaciones que permite recuperar una implementación de IExtension<T> por su tipo.

Por lo tanto, para ampliar el InstanceContext, debe implementar la interfaz IExtension<T>. En este proyecto de ejemplo, la CustomLeaseExtension clase contiene esta implementación.

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

La IExtension<T> interfaz tiene dos métodos Attach y Detach. Tal como sus nombres lo indican, se llama a estos dos métodos cuando el entorno de ejecución adjunta y separa la extensión a una instancia de la clase InstanceContext. En este ejemplo, el Attach método se usa para realizar un seguimiento del InstanceContext objeto 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 lo tanto, la ICustomLease interfaz se declara con los métodos deseados y se implementa en la CustomLeaseExtension clase .

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

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

Cuando WCF invoca el IsIdle método en la IInstanceContextProvider implementación, esta llamada se enruta al IsIdle método de CustomLeaseExtension. A continuación, CustomLeaseExtension comprueba su estado privado para determinar si InstanceContext está inactivo. Si está inactivo, 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 Dispatcher para iniciar otro ciclo de limpieza.

void idleTimer_Elapsed(object sender, ElapsedEventArgs args)
{
    lock (thisLock)
    {
        StopTimer();
        isIdle = true;
        Utility.WriteMessageToConsole(
            ResourceHelper.GetString("MsgLeaseExpired"));
        callback(owner);
    }
}

No hay forma de renovar el temporizador en ejecución cuando llega un nuevo mensaje para la instancia que se mueve al estado inactivo.

El ejemplo implementa IInstanceContextProvider para interceptar las llamadas al método IsIdle y enrutarlas al CustomLeaseExtension. La implementación IInstanceContextProvider está contenida en la clase CustomLifetimeLease. El IsIdle método se invoca cuando WCF está a punto de liberar la instancia de servicio. Sin embargo, solo hay una instancia de una implementación determinada ISharedSessionInstance en la colección serviceBehavior IInstanceContextProvider . Esto significa que no hay forma de saber si el InstanceContext está cerrado en el momento en que WCF comprueba el método IsIdle. Por lo tanto, en este ejemplo se usa el bloqueo de subprocesos para serializar las solicitudes al IsIdle método .

Importante

El uso del bloqueo de subprocesos no es un enfoque recomendado porque la serialización puede afectar gravemente al rendimiento de la aplicación.

Un campo miembro privado se usa en la CustomLifetimeLease clase para realizar un seguimiento del estado de inactividad y lo devuelve el IsIdle método . Cada vez que se llama al IsIdle método , el isIdle campo se devuelve y se restablece a false. Es esencial establecer este valor en false para garantizar que el Dispatcher llame al método NotifyIdle.

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

Si el método IInstanceContextProvider.IsIdle devuelve false, Dispatcher 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 lo tanto, el código de ejemplo puede consultar la ICustomLease extensión de tipo y comprobar la ICustomLease.IsIdle propiedad en 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 a Dispatcher 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 próxima vez que Dispatcher llame a IInstanceContextProvider.IsIdle, devuelve true y permite que Dispatcher libere la instancia.

Ahora que se ha completado la base de la extensión personalizada, debe integrarse al modelo de servicio. Para enlazar la CustomLeaseExtension implementación a InstanceContext, WCF proporciona la IInstanceContextInitializer interfaz para realizar el arranque de InstanceContext. En el ejemplo, la CustomLeaseInitializer clase implementa esta interfaz y agrega una instancia de CustomLeaseExtension a la Extensions colección desde la única inicialización del método. El Distribuidor llama a este método inicializando la instancia de InstanceContext.

public void InitializeInstanceContext(InstanceContext instanceContext,
    System.ServiceModel.Channels.Message message, IContextChannel channel)

    //...

    IExtension<InstanceContext> customLeaseExtension =
        new CustomLeaseExtension(timeout, headerId);
    instanceContext.Extensions.Add(customLeaseExtension);
}

Por último, la IInstanceContextProvider implementación se enlaza al modelo de servicio mediante la IServiceBehavior implementación. Esta implementación se coloca en la CustomLeaseTimeAttribute clase y también se deriva de la Attribute clase base para exponer este comportamiento como atributo.

public void ApplyDispatchBehavior(ServiceDescription description,
           ServiceHostBase serviceHostBase)
{
    CustomLifetimeLease customLease = new CustomLifetimeLease(timeout);

    foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
    {
        ChannelDispatcher cd = cdb as ChannelDispatcher;

        if (cd != null)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                ed.DispatchRuntime.InstanceContextProvider = customLease;
            }
        }
    }
}

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

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

Al ejecutar el ejemplo, las solicitudes de operación y las respuestas se muestran en las ventanas de servicio y consola de cliente. Presione ENTRAR en cada ventana de consola para apagar el servicio y el cliente.

Para configurar, compilar y ejecutar el ejemplo

  1. Asegúrese de que ha llevado a cabo el 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 Building the Windows Communication Foundation Samples.

  3. Para ejecutar el ejemplo en una configuración de una máquina única o entre máquinas, siga las instrucciones de Ejecución de los ejemplos de Windows Communication Foundation.