[英]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.