Partilhar via


Vida útil personalizada

O exemplo Lifetime demonstra como escrever uma extensão do Windows Communication Foundation (WCF) para fornecer serviços de tempo de vida personalizados para instâncias de serviço WCF compartilhadas.

Nota

O procedimento de instalação e as instruções de compilação para este exemplo estão localizados no final deste artigo.

Instanciação compartilhada

O WCF oferece vários modos de instanciação para suas instâncias de serviço. O modo de instanciação compartilhada abordado neste artigo fornece uma maneira de compartilhar uma instância de serviço entre vários canais. Os clientes podem entrar em contato com um método de fábrica no serviço e criar um novo canal para iniciar a comunicação. O trecho de código a seguir mostra como um aplicativo cliente cria um novo canal para uma instância de serviço 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"));
}

Ao contrário de outros modos de instanciação, o modo de instanciação compartilhada tem uma maneira exclusiva de liberar as instâncias de serviço. Por padrão, quando todos os canais são fechados para um InstanceContext, o tempo de execução do serviço WCF verifica se o serviço InstanceContextMode está configurado para PerCall ou PerSessione, em caso afirmativo, libera a instância e reivindica os recursos. Se um costume IInstanceContextProvider estiver sendo usado, o WCF invocará o IsIdle método da implementação do provedor antes de liberar a instância. Se IsIdle retornar true a instância for liberada, caso contrário, a IInstanceContextProvider implementação será responsável por notificar o estado ocioso Dispatcher usando um método de retorno de chamada. Isso é feito chamando o NotifyIdle método do provedor.

Este exemplo demonstra como você pode atrasar a liberação do com um tempo limite ocioso InstanceContext de 20 segundos.

Estendendo o InstanceContext

No WCF, InstanceContext é o link entre a instância de serviço e o Dispatcher. O WCF permite que você estenda esse componente de tempo de execução adicionando novo estado ou comportamento usando seu padrão de objeto extensível. O padrão de objeto extensível é usado no WCF para estender classes de tempo de execução existentes com nova funcionalidade ou para adicionar novos recursos de estado a um objeto. Há três interfaces no padrão de objeto extensível: IExtensibleObject<T>, IExtension<T>e IExtensionCollection<T>.

A IExtensibleObject<T> interface é implementada por objetos para permitir extensões que personalizam sua funcionalidade.

A IExtension<T> interface é implementada por objetos que podem ser extensões de classes do tipo T.

E, finalmente, a IExtensionCollection<T> interface é uma coleção de IExtension<T> implementações que permite recuperar uma implementação de IExtension<T> acordo com seu tipo.

Portanto, para estender o InstanceContext, você deve implementar a IExtension<T> interface. Neste projeto de exemplo, a CustomLeaseExtension classe contém essa implementação.

class CustomLeaseExtension : IExtension<InstanceContext>
{
}

A IExtension<T> interface tem dois métodos Attach e Detach. Como seus nomes indicam, esses dois métodos são chamados quando o tempo de execução anexa e desanexa a extensão a uma instância da InstanceContext classe. Neste exemplo, o Attach método é usado para controlar o InstanceContext objeto que pertence à instância atual da extensão.

InstanceContext owner;

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

Além disso, você deve adicionar a implementação necessária à extensão para fornecer o suporte vitalício estendido. Portanto, a ICustomLease interface é declarada com os métodos desejados e é implementada CustomLeaseExtension na classe.

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

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

Quando o IInstanceContextProvider WCF invoca o IsIdle método na implementação, essa chamada é roteada para o IsIdle método do CustomLeaseExtension. Em seguida, o verifica seu CustomLeaseExtension estado privado para ver se o InstanceContext está ocioso. Se estiver ocioso, ele retorna true. Caso contrário, ele inicia um temporizador para uma quantidade especificada de vida útil estendida.

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

No caso do Elapsed temporizador, a função de retorno de chamada no Dispatcher é chamada para iniciar outro ciclo de limpeza.

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

Não há como renovar o temporizador de execução quando uma nova mensagem chega para a instância que está sendo movida para o estado ocioso.

O exemplo implementa IInstanceContextProvider para intercetar as chamadas para o IsIdle método e roteá-las para o CustomLeaseExtension. A IInstanceContextProvider implementação está contida na CustomLifetimeLease classe. O IsIdle método é invocado quando o WCF está prestes a liberar a instância de serviço. No entanto, há apenas uma instância de uma implementação específica ISharedSessionInstance na coleção ServiceBehavior IInstanceContextProvider . Isso significa que não há como saber se o está fechado no momento em que o InstanceContext WCF verifica o IsIdle método. Portanto, este exemplo usa bloqueio de thread para serializar solicitações para o IsIdle método.

Importante

Usar o bloqueio de thread não é uma abordagem recomendada porque a serialização pode afetar gravemente o desempenho do seu aplicativo.

Um campo de membro privado é usado na classe para controlar o CustomLifetimeLease estado ocioso e é retornado pelo IsIdle método. Cada vez que o IsIdle método é chamado, o isIdle campo é retornado e redefinido para false. É essencial definir esse valor para false garantir que o Dispatcher chame o NotifyIdle método.

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

Se o IInstanceContextProvider.IsIdle método retornar false, o Dispatcher registrará uma função de retorno de chamada usando o NotifyIdle método. Este método recebe uma referência ao que está sendo InstanceContext liberado. Portanto, o código de exemplo pode consultar a extensão de ICustomLease tipo e verificar a ICustomLease.IsIdle propriedade no estado estendido.

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 que a ICustomLease.IsIdle propriedade seja verificada, a propriedade Callback precisa ser definida, pois isso é essencial para CustomLeaseExtension notificar o Dispatcher quando ela ficar ociosa. Se ICustomLease.IsIdle retornar true, o isIdle membro privado é simplesmente definido e CustomLifetimeLeasetrue chama o método de retorno de chamada. Como o código contém um bloqueio, outros threads não podem alterar o valor desse membro privado. E da próxima vez que o Dispatcher chamar o IInstanceContextProvider.IsIdle, ele retornará true e permitirá que o Dispatcher libere a instância.

Agora que o trabalho de base para a extensão personalizada está concluído, ele deve ser conectado ao modelo de serviço. Para conectar a CustomLeaseExtension implementação ao , o InstanceContextWCF fornece a IInstanceContextInitializer interface para executar o bootstrapping do InstanceContext. No exemplo, a CustomLeaseInitializer classe implementa essa interface e adiciona uma instância de CustomLeaseExtension à Extensions coleção a partir da única inicialização do método. Esse método é chamado pelo Dispatcher ao inicializar o InstanceContext.

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

    //...

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

Finalmente, a IInstanceContextProvider implementação é conectada ao modelo de serviço usando a IServiceBehavior implementação. Essa implementação é colocada na CustomLeaseTimeAttribute classe e também deriva da Attribute classe base para expor esse comportamento como um 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;
            }
        }
    }
}

Esse comportamento pode ser adicionado a uma classe de serviço de exemplo anotando-a com o CustomLeaseTime atributo.

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

Quando você executa o exemplo, as solicitações de operação e as respostas são exibidas nas janelas do console de serviço e do cliente. Pressione ENTER em cada janela do console para desligar o serviço e o cliente.

Para configurar, compilar e executar o exemplo

  1. Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.

  2. Para criar a edição C# ou Visual Basic .NET da solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.

  3. Para executar o exemplo em uma configuração de máquina única ou cruzada, siga as instruções em Executando os exemplos do Windows Communication Foundation.