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.

Observação

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 IInstanceContextProvider personalizado estiver a ser usado, o WCF invocará o método IsIdle da implementação do provedor antes de libertar 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 pode atrasar a libertação de InstanceContext com um tempo limite ocioso 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.

Finalmente, a interface IExtensionCollection<T> é uma coleção de implementações IExtension<T> que permite recuperar uma implementação de IExtension<T> de acordo com seus tipos.

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 classe InstanceContext. 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 na classe CustomLeaseExtension.

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

class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}

Quando o WCF invoca o método IsIdle na implementação IInstanceContextProvider, essa chamada é encaminhada para o método IsIdle do CustomLeaseExtension. Em seguida, verifica o seu CustomLeaseExtension estado privado para ver se o InstanceContext está inativo. Caso esteja ocioso, retorna true. Caso contrário, inicia um temporizador para um período especificado de tempo de vida útil prolongada.

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

No evento do Elapsed temporizador, a função callback 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 InstanceContext está fechado no momento em que a WCF verifica o método IsIdle. 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 CustomLifetimeLease para controlar o estado ocioso e é retornado pelo método IsIdle. Cada vez que o IsIdle método é chamado, o isIdle campo é retornado e redefinido para false. É essencial definir esse valor para false para 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 método IInstanceContextProvider.IsIdle retornar false, o Dispatcher registará uma função de retorno de chamada usando o método NotifyIdle. Este método recebe uma referência ao que está sendo InstanceContext liberado. Portanto, o código de exemplo pode consultar a extensão do tipo ICustomLease e verificar a propriedade ICustomLease.IsIdle 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 de verificar a propriedade ICustomLease.IsIdle, é necessário definir a propriedade Callback, pois isso é essencial para que CustomLeaseExtension notifique o Dispatcher quando ficar ocioso. Se ICustomLease.IsIdle retornar true, o isIdle membro privado é simplesmente definido em CustomLifetimeLease para true e chama o método de callback. Como o código contém um bloqueio, outros threads não podem alterar o valor desse membro privado. E na próxima vez que o Dispatcher chamar o IInstanceContextProvider.IsIdle, ele retorna true e permite ao Dispatcher libertar a instância.

Agora que o trabalho de base para a extensão personalizada está concluído, ela deve ser conectada ao modelo de serviço. Para ligar a implementação CustomLeaseExtension ao InstanceContext, o WCF fornece a interface IInstanceContextInitializer para executar o bootstrapping de 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. Verifique se você executou o procedimento de instalaçãoOne-Time 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.