繁体   English   中英

温莎城堡WCF设施未处理单向运营

[英]Castle Windsor WCF Facility is not processing one way operations

我目前有一个简单的用例。

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

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

3)服务“ B”完成一些工作。

可视化:客户端->服务“ A”,其中Castle注入代理->服务“ B”

简单吧? 在没有问题IF的情况下可以正常工作,如果服务“ B”主机已启动并正在运行,那将是一个大问题。

我已经看到并且可以按需重现的行为是,如果服务“ B”关闭,则调用链完成,而没有任何暗示任何问题的提示。 换句话说,Castle没有抛出任何解析异常,也没有任何WCF异常。 我已将其隔离到如何处理IsOneWay = true操作。

这是一个主要问题,因为您认为一切都已正确执行,但实际上您的代码均未执行!

这是预期的行为吗? 是否可以在Castle中打开某些选项,以便在WCF Client代理是已解决的依赖项时它会引发异常? 还有其他选择吗?

还有一点需要注意的是,发生问题的唯一线索是何时/是否在客户端代理上执行Container.Release(),因为它引发异常。 出于各种原因(不值得进入此处),您不能依赖于此。

谢谢!

另外,以下是重新创建此问题的代码。 运行它1)在Visual Studio中创建一个新的单元测试项目2)通过NuGet添加Castle Windsor WCF集成工具3)将下面的代码粘贴到.cs文件中,所有内容都放在一个目录中以使其变得容易。 4)运行两个单元测试,SomeOperation_With3Containers_NoException()在依赖服务(上面的服务“ B”)运行时工作。 SomeOperation_With2Containers_NoException()失败是.Release 5)设置断点,您可以看到在实现中未命中任何代码。

**** UPDATE ****:处理此问题的主要方法是使用IErrorHandler植入(如Roman在下面的评论中所述)。 详细信息和示例可以在这里找到: http : //msdn.microsoft.com/zh-cn/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);
            }
        }
    }

}

}

存在一种用于“解雇”场景的操作方法。 您不关心结果是否成功。 您不必等待服务器响应(如果是HTTP绑定,则只需初始TCP握手)。 通过使用一种方式的操作,客户端仅能确信服务器成功地在线接收了该消息,并且服务器无法保证它将成功处理该消息。 在HTTP协议中是这样。 在其他协议中,例如Microsoft MSMQ或IBM MQ,服务器甚至不需要与客户端同时联机。

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

无论如何,Castle都不应抛出异常,因为它已成功解析了服务A中服务B的代理。服务B停机的事实与Castle或任何其他DI容器无关。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM