Конфликт ServiceHost по адресу при тестировании модуля

StackOverflow https://stackoverflow.com/questions/1411923

Вопрос

У меня есть небольшой хостинг WCF, который я пишу, который будет динамически создавать ServiceHosts на основе файла .config. Общая идея состоит в том, чтобы позволить нам удалять существующие сервисы, а также добавлять новые сервисы во время выполнения без необходимости переводить все наши сервисы в автономный режим.

Я столкнулся с проблемой модульного тестирования, которое показывает, что это может быть не так просто, как кажется. Кажется, что только одна ServiceHost может существовать для любой данной конечной точки (даже если в одной ServiceHost может существовать несколько разных конечных точек для службы). Обычно это не проблема, однако, когда требуется переконфигурировать службу, сбой исходного ServiceHost фактически не уничтожает регистрацию для этого адреса конечной точки. Попытка создать другой ServiceHost для той же службы (что означает, что используются те же конечные точки) завершается неудачей со следующим исключением:

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

Я действительно столкнулся с ошибкой во время модульного тестирования. В тестах будет задействован один модуль, который полностью закрывает ServiceHosts и хост-движок настолько, насколько это возможно. Затем создает другой экземпляр хост-движка, который пытается заново создать тот же ServiceHosts для другого теста. Второй тест встречает ошибку выше. Я предполагаю, что хотя ServiceHost.Close () был вызван, это на самом деле не разрушает хост службы ... так что он все еще находится в памяти. Я не могу сказать, очищает ли GC старые сервисные хосты или нет ... проблема сохраняется, не исчезая после того, как она первоначально возникла (насколько я смог определить ... я ждал около 30 минут). )

Мой файл конфигурации для system.serviceModel выглядит следующим образом:

  <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>
Это было полезно?

Решение

Чтобы дать ответ на этот вопрос, на случай, если кто-то еще столкнулся с проблемой. На самом деле, есть две причины этой проблемы:

1) Во время модульного тестирования, если возникла исключительная ситуация, она обычно выпадает из кода, тестируемого до закрытия ServiceHost. Это оставило ServiceHost привязанным к конкретной конечной точке. Это вызвало сбой ВСЕХ последующих тестов, которые выполняли один и тот же кусок кода. Поскольку я выполнял BDD с SubSpec и xUnit, один тестовый случай (в терминах BDD) выполнял одно утверждение на тест, а один тестовый случай мог охватывать до дюжины или более утверждений.

2) Остерегайтесь конечной точки MEX. Конечная точка MEX может существовать только один раз для каждой услуги. Изначально я создал конечную точку http и net.tcp mex. Однако это вызвало проблему, так как какой бы экземпляр конечной точки MEX не запускался второй, генерировалось исключение. Вообще говоря, если вы используете конечную точку MEX, HTTP является наиболее полезным протоколом для использования, если только какая-то физическая инфраструктурная проблема не мешает вам сделать это.

Вообще говоря, вызов метода Close () в ServiceHost полностью отвяжет его, что позволит снова использовать любые адреса, которые ранее были привязаны к его конечным точкам. Иногда закрытие может занять некоторое время, а в редких случаях может быть выдано исключение. Если вы выполняете BDD с SubSpec и следуете правилу одиночного утверждения для каждого теста, исключение, выброшенное в одном тесте, которое предотвращает закрытие ServiceHosts, приведет к сбою всех последующих тестов.

Другие советы

Один из ответов - добавлять Guid к URL-адресу узла службы каждый раз, когда вы его увеличиваете, и использовать фабричный подход, который одновременно раскручивает экземпляры ServiceHost и возвращает канал на стороне клиента, чтобы клиент знал, какой URL для использования.

Пример IDProign InProcFactory использует этот подход, поэтому вы можете использовать его как есть:

http://www.idesign.net/idesign/DesktopDefault .aspx TabIndex = 5 & Amp;? табетический = 11

Обратите внимание, что вам необходимо зарегистрироваться на сайте IDesign, чтобы загрузить образец, и они будут время от времени отправлять вам объявления об обучении и тому подобное, но это не так уж много.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top