2014-10-01 2 views
2

В настоящее время у меня простой пример использования.Замок Windsor WCF Facility не обрабатывает односторонние операции

1) Клиентское приложение, которое подключается к службе WCF с использованием опции AsWcfClient от Castle.

2) Служба WCF «А», которая размещается с использованием замка и вводит одну зависимость. Эта зависимость является клиентским прокси-сервером для другой службы WCF (вызов службы «B»).

3) Сервис «B» выполняет некоторые работы.

Для визуализации: клиент -> Сервис "А" с замком впрыскивается прокси -> Сервис "B"

Простой правильно? Работает без разрешения IF, и это большой, если хост службы «B» запущен и работает.

Поведение, которое я видел и могу воспроизвести по требованию, заключается в том, что если служба «B» не работает, цепочка вызовов завершается без какого-либо намека на наличие какой-либо проблемы. Чтобы сказать это по-другому, исключение исключений, заброшенное Замком, и исключение WCF. Я выделил это для обработки операций IsOneWay = true.

Это серьезная проблема, поскольку вы считаете, что все выполнено правильно, но на самом деле ни один из ваших кодов не был выполнен!

Ожидается ли такое поведение? Может быть, я могу включить какой-либо вариант в Castle, чтобы он выдавал и исключался, когда прокси-сервер WCF-клиента является разрешенной зависимостью? Какой-то другой вариант?

Еще одно замечание: единственный ключ, который у вас есть, это проблема, когда/если вы используете Container.Release() на прокси-сервере клиента, поскольку он генерирует исключение. Это не может зависеть от вас по разным причинам, не стоит здесь заходить.

Thank!

Также ниже приведен код, который воссоздает эту проблему. Чтобы запустить его 1) Создайте новый проект тестирования модулей в Visual Studio 2) Добавьте объект интеграции Castle Wondersor WCF через NuGet 3) Вставьте код снизу в файл .cs, все в одном, чтобы было легко. 4) Запустите два модульных теста, SomeOperation_With3Containers_NoException() работает как служба зависимостей (служба «B» сверху). SomeOperation_With2Containers_NoException() не работает .Release 5) Установите точки останова, и вы увидите, что в реализациях не попадает ни один код.

**** ОБНОВЛЕНИЕ ****: Основной способ, которым это необходимо обработать, - это имплантация IErrorHandler (как упоминалось в комментариях к Roman). Подробности и пример можно найти здесь: http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.110).aspx

Используйте эту реализацию, чтобы регистрировать любые исключения в операции One Way и использовать эти данные для принятия соответствующих мер.

using Castle.Facilities.WcfIntegration; 
using Castle.MicroKernel.Registration; 
using Castle.Windsor; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System; 
using System.ServiceModel; 
using System.ServiceModel.Description; 

namespace UnitTestProject1 
{ 
    [ServiceContract] 
    public interface IServiceContractA 
    { 
     [OperationContract(IsOneWay = true)] 
     void SomeOperation(); 
    } 

[ServiceContract] 
public interface IServiceDependancy 
{ 
    [OperationContract] 
    void SomeOperation(); 
} 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] 
public class ServiceContractAImplementation : IServiceContractA 
{ 
    private IServiceDependancy ServiceProxy; 

    public ServiceContractAImplementation() { } 
    public ServiceContractAImplementation(IServiceDependancy dep) 
    { 
     ServiceProxy = dep; 
    } 

    public void SomeOperation() 
    { 
     ServiceProxy.SomeOperation(); 
    } 
} 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] 
public class ServiceDependancyImplementation : IServiceDependancy 
{ 
    public void SomeOperation() 
    { 
     //do nothing, just want to see if we can create an instance and hit the operation. 
     //if we need to do something, do something you can see like: System.IO.File.Create(@"d:\temp\" + Guid.NewGuid().ToString()); 
    } 
} 

public class ServiceCastleInstaller : IWindsorInstaller 
{ 
    public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store) 
    { 
     container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero); 

     var returnFaults = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true, HttpHelpPageEnabled = true }; 

     container.Register(Component.For<IServiceBehavior>().Instance(returnFaults)); 


     //local in-proc service hosting 
     var namedPipeBinding = new NetNamedPipeBinding(); 

     //it works using Named Pipes 
     var serviceModelPipes = new DefaultServiceModel().AddEndpoints(
      WcfEndpoint.BoundTo(namedPipeBinding).At("net.pipe://localhost/IServiceContractA") 
         ).Discoverable(); 

     container.Register(Component.For<IServiceContractA>() 
              .ImplementedBy<ServiceContractAImplementation>() 
              .LifeStyle.PerWcfOperation() 
              .AsWcfService(serviceModelPipes) 
              ); 

     //our service (IServiceContractA) has a dependancy on another service so needs a client to access it. 
     container.Register(Castle.MicroKernel.Registration.Component.For<IServiceDependancy>() 
      .AsWcfClient(WcfEndpoint.BoundTo(namedPipeBinding) 
      .At(@"net.pipe://localhost/IServiceDependancy")).LifeStyle.Transient); 

    } 
} 

public class ServiceDependancyCastleInstaller : IWindsorInstaller 
{ 
    public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store) 
    { 
     container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero); 

     var returnFaults = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true, HttpHelpPageEnabled = true }; 

     container.Register(Component.For<IServiceBehavior>().Instance(returnFaults)); 

     //local in-proc service hosting 
     var namedPipeBinding = new NetNamedPipeBinding(); 

     var serviceModel = new DefaultServiceModel().AddEndpoints(
      WcfEndpoint.BoundTo(namedPipeBinding).At("net.pipe://localhost/IServiceDependancy") 
         ).Discoverable(); 

     container.Register(Component.For<IServiceDependancy>() 
              .ImplementedBy<ServiceDependancyImplementation>() 
              .LifeStyle.PerWcfOperation() 
              .AsWcfService(serviceModel) 
              ); 
    } 

} 


[TestClass] 
public class UnitTest1 
{ 
    [TestMethod] 
    public void SomeOperation_With3Containers_NoException() 
    { 
     //setup the container that is going to host the service dependancy 
     using (var dependancyContainer = new WindsorContainer().Install(new ServiceDependancyCastleInstaller())) 
     { 
      //container that host the service that the client will call. 
      using (var serviceContainer = new WindsorContainer().Install(new ServiceCastleInstaller())) 
      { 
       //client container, nice and simple so doing it in the test here. 
       using (var clientContainer = new WindsorContainer()) 
       { 
        clientContainer.AddFacility<WcfFacility>(); 

        var endpoint = WcfEndpoint.BoundTo(new NetNamedPipeBinding()) 
         .At("net.pipe://localhost/IServiceContractA"); 

        clientContainer.Register(Castle.MicroKernel.Registration.Component.For<IServiceContractA>() 
         .AsWcfClient(endpoint).LifeStyle.Transient); 

        var proxy = clientContainer.Resolve<IServiceContractA>(); 

        proxy.SomeOperation(); 

        clientContainer.Release(proxy); 
       } 
      } 
     } 
    } 

    [TestMethod] 
    public void SomeOperation_With2Containers_NoException() 
    { 
     //this one fails. 
     // this test omits the dependancy that the IServiceContractA has 
     //Note that all seems to work, the only hint you have that it doesnt 
     //is the .Release call throws and exception. 

     //container that host the service that the client will call. 
     using (var serviceContainer = new WindsorContainer().Install(new ServiceCastleInstaller())) 
     { 
      //client container, nice and simple so doing it in the test here. 
      using (var clientContainer = new WindsorContainer()) 
      { 
       clientContainer.AddFacility<WcfFacility>(); 

       var endpoint = WcfEndpoint.BoundTo(new NetNamedPipeBinding()) 
        .At("net.pipe://localhost/IServiceContractA"); 

       clientContainer.Register(Castle.MicroKernel.Registration.Component.For<IServiceContractA>() 
        .AsWcfClient(endpoint).LifeStyle.Transient); 

       var proxy = clientContainer.Resolve<IServiceContractA>(); 

       //this call seems like it works but any break points 
       //in code don't get hit. 
       proxy.SomeOperation(); 

       //this throws and exception 
       clientContainer.Release(proxy); 
      } 
     } 
    } 

} 

}

ответ

4

Односторонние операции существует с целью "Огонь и забыть" сценарии. Вы не уход о результате, было ли это удачным или нет. Вам не обязательно wait для ответа сервера (только начальное рукопожатие TCP, если это HTTP-привязка).Используя односторонние операции, клиент получает только уверенность в том, что сервер получил сообщение успешно на проводе, и сервер не гарантирует, что ему удастся обработать сообщение. Это верно в протоколе HTTP. В других протоколах, таких как Microsoft MSMQ или IBM MQ, серверу даже не нужно подключаться к сети одновременно с клиентом.

В вашем сценарии клиент не получает исключения, так как работает служба A. Если служба A была недоступна, вы бы увидели ошибку (опять же, предполагая HTTP или в вашем случае .net-канал). Условие обслуживания B не имеет значения, поскольку служба B является деталью реализации службы A, а ваш клиент не заботится о возвращаемых значениях сервиса. Если бы вам пришлось отлаживать сервис A (привязавшись к нему), пока служба B не работает, вы бы увидели первый шанс и, возможно, даже необработанные исключения (в зависимости от реализации службы A).

Замок не должен был вызывать исключения в любом случае, поскольку он успешно разрешил прокси-сервер для обслуживания B в сервисе A. Тот факт, что служба B не работает, не касается Замка или любого другого контейнера DI в этом отношении.

+0

Спасибо за ответ. Во-первых, вызовы One way, я не верю, что ваше утверждение будет правильным, или предполагаемое использование One Way вызывает или их дизайн в WCF. Я предлагаю этот пост для более подробной информации: http://stackoverflow.com/questions/5318192/how-to-enable-reliability-for-one-way-methods – thorphin

+0

Вы правы, клиент не получает исключения до тех пор, пока я попробуйте освободить разрешенный клиентский прокси WCF, что является единственным признаком того, что есть проблема. Основной вопрос здесь в том, почему нет, или где это сделать, или как включить ведение журнала замка в отношении недоступности службы A? Он никогда не попадает в мой код, поэтому я могу зарегистрировать проблему. Без этого мои вызовы будут полностью потеряны, а мой код никогда не будет выполнен. Огромный вопрос, чтобы быть уверенным! – thorphin

+0

Это связано с использованием вами привязки net.pipe вместо http, однако оно не меняет основные моменты, которые мы обсуждали ранее. Что происходит, так это: 1. клиент отправляет односторонний запрос, не заботясь вообще о ответе на обслуживание, хорошо или плохо. 2. on service Сторона, замок пытается создать экземпляр реализации, для которого требуется служебный прокси B, но поскольку служба B не работает, кажется, что прокси-сервер net.pipe не может быть создан, и поэтому вы не можете сервис Код. 3. Затем релиз прокси на стороне клиента вызывает ошибку, так как работает net.pipe. – Roman

Смежные вопросы