我目前有一个简单的用例。城堡温莎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);
}
}
}
}
}
感谢您的回应。首先在单向通话中,我不相信你的断言是正确的,也不相信单向通话或其在WCF中的设计的使用。我建议这个职位的更多细节:http://stackoverflow.com/questions/5318192/how-to-enable-reliability-for-one-way-methods – thorphin 2014-10-02 23:38:25
你是对的,客户端没有收到异常,直到我尝试释放已解析的WCF客户端代理,这是唯一表示存在问题的指示。 这里的主要问题是为什么没有,或者在什么地方,或者如何启用城堡日志记录关于服务A的依赖关系不可用?它从来没有打我的代码,所以我可以登录有问题。没有这个,我的电话将会完全丢失,我的代码也不会执行。一个巨大的问题是肯定的! – thorphin 2014-10-02 23:46:34
这是由于您使用net.pipe绑定而不是http,但它不会改变我们之前讨论的要点。会发生什么如下: 1.客户端发送单向请求,而不关心服务回复,无论好坏。 2.关于服务一方面,castle试图创建一个实现实例,它需要服务B代理,但由于服务B关闭,似乎无法创建net.pipe代理,这就是为什么你无法访问服务代码。 3.接下来,客户端发布代理会引发错误,因为这是net.pipe的工作方式。 – Roman 2014-10-03 05:46:46