2014-10-01 56 views
2

我目前有一个简单的用例。城堡温莎WCF设施不处理单向操作

1),使用城堡的AsWcfClient选项连接到WCF服务的客户端应用程序。

2)WCF服务“A”正在使用城堡托管并且被注入的单一相关性。此依赖关系是另一个WCF服务(称为服务“B”)的客户端代理。

3)服务“B”做了一些工作。

形象化:客户端 - >服务 “A” 与青山注入代理 - >服务 “B”

简单吧?没有问题的工作IF,这是一个很大的,如果服务“B”主机启动并运行。

我所看到的和可以重现上需求的行为,如果服务“B”已关闭,调用链没有任何迹象表明有任何问题完成。换句话说,Castle没有引发解析异常,也没有任何WCF异常。我已经将它隔离为如何处理IsOneWay = true操作。

这是一个重大的问题,因为你觉得一切都已经正确执行,但在现实中没有你的代码已经被执行!

这是预期的行为?有没有我可以在Castle中打开一些选项,这样当WCF客户端代理被解析的依赖关系时它会抛出异常?其他选项?

还有一点需要注意的是,您遇到的唯一线索就是问题出现时,您是否在客户端代理上执行Container.Release()时发生异常。这不能因为你不值得进入的各种原因而依赖你。

谢谢!另外下面

是再现了这个问题的代码。要运行它 1)在Visual Studio 2从下面创建一个新的单元测试项目)通过的NuGet 3添加温莎城堡WCF集成工具)将代码粘贴到一个cs文件,一切都在一个可以很容易。 4)运行两个单元测试,SomeOperation_With3Containers_NoException()作为依赖服务(上面的服务“B”)运行。 SomeOperation_With2Containers_NoException()失败。释放 5)设置断点,你可以看到没有代码在实现中被命中。

****更新****:这需要处理的主要方式是使用IErrorHandler植入(正如在下面的评论中罗马提到的)。可以在此处找到详细信息和示例:http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.110).aspx

使用此实现记录单向操作中的任何异常并使用该数据采取适当的操作。

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

一种方式操作存在的目的 “火和忘记” 的情景。你不要保健关于结果,不管它是否成功。您不必等待供服务器响应(如果它是HTTP绑定,只有初始TCP握手)。通过使用单向操作,客户端只能得到服务器在线上成功接收到消息的信心,并且服务器不保证它将成功处理该消息。这在HTTP协议中是正确的。在其他协议中,如Microsoft MSMQ或IBM MQ,服务器甚至不需要与客户端同时在线。

在您的方案中,客户端不会收到异常,因为服务A已启动并正在运行。如果服务A关闭,你会看到一个错误(再次假设HTTP,或者在你的情况下.net管道)。服务B的条件无关紧要,因为服务B是服务A的实现细节,而客户端并不关心服务A的返回值。如果您在服务B关闭的情况下调试服务A(通过附加到服务),您将看到第一次机会,甚至可能未经处理的异常(取决于服务A的实现)。

无论如何,城堡不应该抛出异常,因为它已经成功地解析了服务A中的服务B的代理。服务B关闭的事实并不是城堡或任何其他DI容器的问题。

+0

感谢您的回应。首先在单向通话中,我不相信你的断言是正确的,也不相信单向通话或其在WCF中的设计的使用。我建议这个职位的更多细节:http://stackoverflow.com/questions/5318192/how-to-enable-reliability-for-one-way-methods – thorphin 2014-10-02 23:38:25

+0

你是对的,客户端没有收到异常,直到我尝试释放已解析的WCF客户端代理,这是唯一表示存在问题的指示。 这里的主要问题是为什么没有,或者在什么地方,或者如何启用城堡日志记录关于服务A的依赖关系不可用?它从来没有打我的代码,所以我可以登录有问题。没有这个,我的电话将会完全丢失,我的代码也不会执行。一个巨大的问题是肯定的! – thorphin 2014-10-02 23:46:34

+0

这是由于您使用net.pipe绑定而不是http,但它不会改变我们之前讨论的要点。会发生什么如下: 1.客户端发送单向请求,而不关心服务回复,无论好坏。 2.关于服务一方面,castle试图创建一个实现实例,它需要服务B代理,但由于服务B关闭,似乎无法创建net.pipe代理,这就是为什么你无法访问服务代码。 3.接下来,客户端发布代理会引发错误,因为这是net.pipe的工作方式。 – Roman 2014-10-03 05:46:46