我目前有一个简单的用例。
1)使用城堡的AsWcfClient选项连接到WCF服务的客户端应用程序。
2) WCF服务"A“,它使用城堡托管,并注入单个依赖项。此依赖项是另一个WCF服务(称为"B")的客户端代理。
3)服务"B“做一些工作。
可视化:客户端->服务"A“,向->服务"B”注入城堡代理
很简单对吧?如果服务"B“主机已经启动并运行,这将是一个很大的假设。
我已经看到并可以按需复制的行为是,如果服务"B“被关闭,那么调用链就会在没有任何提示的情况下完成。换句话说,城堡没有抛出任何解析异常,也没有WCF异常。我将此与IsOneWay=true操作的处理方式隔离开来。
这是一个很大的问题,因为您认为一切都已正确执行,但实际上您的代码都没有被执行过!
这是预期的行为吗?我是否可以在城堡中打开一些选项,以便在WCF客户端代理是已解决的依赖项时抛出和异常?还有别的选择吗?
另一个注意事项是,当/如果您在客户端代理上执行Container.Release()时抛出异常,则会出现问题的唯一线索。这不能取决于你,因为各种原因,不值得进入这里。
谢谢!
此外,下面是重新创建此问题的代码。要运行它,1)在Visual 2中创建一个新的单元测试项目( 2)通过NuGet 3将代码粘贴到.cs文件中,从而使它变得简单。4)运行两个单元测试,SomeOperation_With3Containers_NoException()作为依赖服务(上面的服务"B“)正在运行。SomeOperation_With2Containers_NoException() fails是.Release 5)设置的断点,您可以看到实现中没有命中任何代码。
*UPDATE*:需要处理的主要方法是植入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);
}
}
}
}
}
发布于 2014-10-02 12:03:33
一种方式的操作存在的目的是“火和忘记”的场景。无论结果是否成功,都不关心。您不需要等待来响应服务器(只有初始的TCP握手(如果是HTTP ))。通过使用单向操作,客户端只能确信服务器在有线上成功地接收到了消息,而服务器无法保证它将成功地处理消息。这在HTTP协议中是正确的。在其他协议(如Microsoft或IBM )中,服务器甚至不需要与客户端同时联机。
在您的场景中,客户端不会收到异常,因为服务A已经启动并正在运行。如果服务A被关闭,您将看到一个错误(同样,假设HTTP,或者在您的例子中是.net管道)。服务B的条件并不重要,因为服务B是服务A的实现细节,而您的客户端并不关心服务A的返回值。如果您要在服务B关闭时调试服务A(通过附加到它),那么您将看到第一次机会,甚至可能是未处理的异常(取决于服务A的实现)。
无论如何,城堡都不应该抛出异常,因为它已经成功地解决了服务A中服务B的代理问题,而服务B已关闭这一事实与城堡无关,也与任何其他DI容器无关。
https://stackoverflow.com/questions/26150471
复制