Pregunta

Tengo un pequeño motor de alojamiento WCF que estoy escribiendo que creará dinámicamente ServiceHosts basado en el archivo .config. La idea general es permitirnos eliminar los servicios existentes, así como agregar nuevos servicios, en tiempo de ejecución sin tener que desconectar todos nuestros servicios.

Me encontré con un problema de prueba de unidad que indica que esto puede no ser tan fácil como parece. Parece que solo puede existir un ServiceHost para un punto final dado (aunque pueden existir múltiples puntos finales diferentes para un servicio en un solo ServiceHost). Normalmente, esto no es un problema, sin embargo, cuando un servicio necesita ser reconfigurado, la desactivación del ServiceHost original no anula el registro de esa dirección de punto final. El intento de crear otro ServiceHost, para el mismo servicio (lo que significa que se usan los mismos puntos finales) falla con la siguiente excepción:

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/'.

Realmente me encuentro con el error durante la prueba de la unidad. Las pruebas ejercerán una unidad, que cierra por completo el motor de alojamiento y alojamiento de servicios tanto como sea humanamente posible. Luego crea otra instancia del motor de alojamiento, que intenta recrear los mismos ServiceHosts nuevamente para una prueba diferente. La segunda prueba encuentra el error anterior. Supongo que mientras se llamó a ServiceHost.Close (), eso no destruye el servidor de servicio ... por lo que todavía está en la memoria. No puedo decir si el GC está limpiando o no los antiguos servidores de servicio ... el problema persiste sin desaparecer después de que ocurre inicialmente (lo mejor que he podido determinar ... He esperado unos 30 minutos hasta ahora). )

Mi archivo de configuración para system.serviceModel es el siguiente:

  <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>
¿Fue útil?

Solución

Para responder a esta pregunta, en caso de que alguien más se haya encontrado con el problema. En realidad resultó que hay dos causas para este problema, de la siguiente manera:

1) Durante la prueba de la unidad, si se encontrara una excepción, generalmente se rompería el código que se estaba probando antes de que se pudiera cerrar el ServiceHost. Esto dejó el ServiceHost vinculado a un punto final en particular. Esto causó que TODAS las pruebas subsiguientes fallaran y ejercieran la misma pieza de código. Como estaba haciendo BDD con SubSpec y xUnit, un solo caso de prueba (preocupación en términos de BDD) realizó una aseveración por prueba, y un solo caso de prueba podría abarcar hasta una docena o más de aserciones.

2) Tenga cuidado con el punto final MEX. El punto final MEX solo puede existir una vez por servicio. Inicialmente, había creado un punto final http y net.tcp mex. Sin embargo, esto causó un problema, ya que la instancia del punto final MEX que se inició en segundo lugar lanzó una excepción. En términos generales, si utiliza el punto final MEX, HTTP es el protocolo más útil de usar, a menos que haya algún problema de infraestructura física que le impida hacerlo.

En términos generales, llamar al método Close () en un ServiceHost lo desenlazará completamente, permitiendo que las direcciones que antes estaban vinculadas a sus puntos finales se puedan usar nuevamente. A veces el cierre puede tardar un tiempo, y en casos raros, se puede lanzar una excepción. Si está haciendo BDD con SubSpec y sigue la regla de la aserción única por prueba, una excepción lanzada en una prueba que impida el cierre de ServiceHosts hará que todas las pruebas subsiguientes fallen.

Otros consejos

Una respuesta es agregar una Guid a la URL del Host del Servicio cada vez que gire una, y utilice un enfoque de fábrica que aumente las instancias de ServiceHost y devuelva el canal del lado del Cliente, para que el cliente sepa cuál url a utilizar.

El ejemplo InProcFactory de IDesign utiliza este enfoque, por lo que puede usarlo tal como está:

http://www.idesign.net/idesign/DesktopDefault .aspx? tabindex = 5 & amp; tabid = 11

Tenga en cuenta que tendrá que registrarse en el sitio de IDesign para descargar la muestra, y le enviarán un anuncio ocasional sobre la capacitación, pero no es demasiado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top