Конфликт ServiceHost по адресу при тестировании модуля - PullRequest
1 голос
/ 11 сентября 2009

У меня есть небольшой хостинг 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>

Ответы [ 2 ]

3 голосов
/ 11 ноября 2009

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

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

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

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

1 голос
/ 11 сентября 2009

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

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

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

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

...