Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O exemplo Lifetime demonstra como escrever uma extensão do WCF (Windows Communication Foundation) para fornecer serviços de lifetime personalizados para instâncias compartilhadas de serviço WCF.
Observação
O procedimento de instalação e as instruções de build 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 compartilhado 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 snippet 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 compartilhado tem uma maneira exclusiva de liberar as instâncias de serviço. Por padrão, quando todos os canais são fechados para InstanceContext, o runtime do serviço WCF verifica se o serviço InstanceContextMode está configurado para PerCall ou PerSessione, se assim for, libera a instância e reivindica os recursos. Se um IInstanceContextProvider personalizado estiver sendo usado, o WCF invocará o método IsIdle 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 InstanceContext com um tempo limite de inatividade de 20 segundos.
Estendendo o InstanceContext
No WCF, InstanceContext é o link entre a instância de serviço e a Dispatcher. O WCF permite que você estenda esse componente de runtime adicionando um 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 runtime existentes com novas funcionalidades 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 suas funcionalidades.
A IExtension<T> interface é implementada por objetos que podem ser extensões de classes do tipo T.
Por fim, a interface IExtensionCollection<T> é uma coleção de implementações IExtension<T> que permite recuperar uma implementação de IExtension<T> pelo seu tipo.
Portanto, para estender o InstanceContext, você deve implementar a interface IExtension<T>. 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 implicam, esses dois métodos são chamados quando o runtime 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 de tempo de vida estendido. Portanto, a ICustomLease interface é declarada com os métodos desejados e é implementada na CustomLeaseExtension classe.
interface ICustomLease
{
bool IsIdle { get; }
InstanceContextIdleCallback Callback { get; set; }
}
class CustomLeaseExtension : IExtension<InstanceContext>, ICustomLease
{
}
Quando o WCF invoca o IsIdle método na IInstanceContextProvider implementação, essa chamada é roteada para o IsIdle método do CustomLeaseExtension. Em seguida, o CustomLeaseExtension verifica seu estado privado para ver se o InstanceContext está ocioso. Se estiver ocioso, retornará true. Caso contrário, inicia um temporizador para uma quantidade específica de tempo de vida estendido.
public bool IsIdle
{
get
{
lock (thisLock)
{
if (isIdle)
{
return true;
}
else
{
StartTimer();
return false;
}
}
}
}
No evento Elapsed do 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 interceptar 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 IInstanceContextProvider de ServiceBehavior. Isso significa que não há como saber se o InstanceContext está fechado no momento em que o WCF verifica o método IsIdle. Portanto, este exemplo usa o bloqueio de thread para serializar solicitações para o IsIdle método.
Importante
O uso do bloqueio de thread não é uma abordagem recomendada porque a serialização pode afetar severamente o desempenho do aplicativo.
Um campo de membro privado é usado na CustomLifetimeLease classe para acompanhar o 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 de forma a garantir que o Dispatcher chame o método NotifyIdle.
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 registra uma função de retorno de chamada usando o método NotifyIdle. Esse método recebe uma referência ao InstanceContext que está sendo liberado. Portanto, o código de exemplo pode consultar a extensão 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 que a propriedade ICustomLease.IsIdle seja verificada, a propriedade de retorno de chamada precisa ser definida, pois é muito importante para CustomLeaseExtension notificar o Dispatcher quando está ocioso. Se ICustomLease.IsIdle retornar true, o membro particular isIdle será simplesmente definido em CustomLifetimeLease para true e chamará 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 na próxima vez que o Dispatcher chamar IInstanceContextProvider.IsIdle, retorna true e permite que o Dispatcher libere a instância.
Agora que as bases da extensão personalizada foram concluídas, ela precisa ser conectada ao modelo de serviço. Para conectar a implementação CustomLeaseExtension ao InstanceContext, o WCF fornece a interface IInstanceContextInitializer para executar a inicialização de InstanceContext. No exemplo, CustomLeaseInitializer classe implementa essa interface e adiciona uma instância de CustomLeaseExtension à coleção Extensions 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);
}
Por fim, 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 e respostas da operação são exibidas nas janelas do serviço e do console do cliente. Pressione ENTER em cada janela do console para desligar o serviço e o cliente.
Para configurar, compilar e executar o exemplo
Verifique se você executou o procedimento de instalaçãoOne-Time para os exemplos do Windows Communication Foundation.
Para compilar a edição .NET do C# ou do Visual Basic da solução, siga as instruções contidas em Como Compilar as Amostras do Windows Communication Foundation.
Para executar o exemplo em uma configuração única ou entre máquinas, siga as instruções em Executando os exemplos do Windows Communication Foundation.