Pergunta

Eu tenho um pequeno mecanismo de hospedagem WCF que estou escrevendo que criará ServiceHosts dinamicamente com base no arquivo .config.A ideia geral é permitir-nos remover serviços existentes, bem como adicionar novos serviços, em tempo de execução, sem ter que colocar todos os nossos serviços offline.

Encontrei um problema de teste de unidade que indica que isso pode não ser tão fácil quanto parece.Parece que apenas um ServiceHost pode existir para qualquer endpoint (mesmo que vários endpoints diferentes para um serviço possam existir em um único ServiceHost).Normalmente, isso não é um problema; no entanto, quando um serviço precisa ser reconfigurado, desativar o ServiceHost original não elimina o registro desse endereço de terminal.A tentativa de criar outro ServiceHost, para o mesmo serviço (o que significa que os mesmos endpoints são usados) falha com a seguinte exceção:

System.InvalidOperationException: The ChannelDispatcher at 'net.pipe://localhost/' with contract(s) '"ITestService"' is unable to open its IChannelListener. --->
System.InvalidOperationException: A registration already exists for URI 'net.pipe://localhost/'.

Na verdade, estou encontrando o erro durante o teste de unidade.Os testes exercitarão uma unidade, que fecha totalmente o ServiceHosts e o mecanismo de hospedagem tanto quanto for humanamente possível.Em seguida, cria outra instância do mecanismo de hospedagem, que tenta recriar os mesmos ServiceHosts novamente para um teste diferente.O segundo teste encontra o erro acima.Suponho que, embora ServiceHost.Close() tenha sido chamado, isso na verdade não destrói o host de serviço... portanto, ele ainda permanece na memória.Não sei dizer se o GC está limpando os hosts de serviço antigos ou não... o problema persiste sem desaparecer depois de ocorrer inicialmente (o melhor que consegui determinar... esperei cerca de 30 minutos até agora. )

Meu arquivo de configuração para system.serviceModel é o seguinte:

  <system.serviceModel>
    <services>
      <service name="Campus.Core.ServiceModel.TestServiceStub">
        <endpoint          
          address="net.pipe://localhost"          
          binding="netNamedPipeBinding"           
          contract="Campus.Core.ServiceModel.ITestService"
        />
      </service>
    </services>
  </system.serviceModel>
Foi útil?

Solução

Para fornecer uma resposta a esta pergunta, caso alguém tenha enfrentado o problema.Na verdade, houve duas causas para esse problema, como segue:

1) Durante o teste de unidade, se uma exceção fosse encontrada, ela normalmente sairia do código que está sendo testado antes que o ServiceHost pudesse ser fechado.Isso deixou o ServiceHost vinculado a um endpoint específico.Isso fez com que TODOS os testes subsequentes falhassem e que exercitassem o mesmo trecho de código.Enquanto eu estava fazendo BDD com SubSpec e xUnit, um único caso de teste (preocupação em termos de BDD) executava uma única asserção por teste, e um único caso de teste poderia abranger até uma dúzia ou mais de asserções.

2) Cuidado com o ponto final MEX.O endpoint MEX só pode existir uma vez por serviço.Inicialmente, criei um endpoint http e net.tcp mex.No entanto, isso causou um problema, pois qualquer instância do endpoint MEX iniciada em segundo lugar gerou uma exceção.De modo geral, se você utilizar o endpoint MEX, o HTTP é o protocolo mais útil a ser usado, a menos que haja algum problema de infraestrutura física que o impeça de fazê-lo.

De modo geral, chamar o método Close() em um ServiceHost irá desvinculá-lo completamente, permitindo que quaisquer endereços que estavam anteriormente vinculados aos seus endpoints possam ser usados ​​novamente.Às vezes, o encerramento pode demorar um pouco e, em casos raros, uma exceção pode ser lançada.Se você estiver executando BDD com SubSpec e seguindo a regra de asserção única por teste, uma exceção lançada em um teste que impeça um fechamento de ServiceHosts fará com que todos os testes subsequentes falhem.

Outras dicas

Uma resposta é anexar um GUID ao URL para o host de serviço cada vez que você acelera um e usar uma abordagem de fábrica que parte das instâncias do serviço de serviço e retorna o canal do lado do cliente, para que o cliente saiba qual URL usar .

A amostra inprocFactory da Idesign usa essa abordagem, para que você possa usá-la como está:

http://www.idesign.net/idesign/desktopdefault.aspx?tabindex=5&tabid=11

Observe que você terá que se registrar no site da Idesign para baixar a amostra e eles enviarão o anúncio ocasional sobre treinamento e tal, mas não é demais.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top