簡體   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