在我的Wcf服务项目中使用Castle windsor 3.0。我使用Topshelf将其作为Windows服务项目托管。我的所有wcf服务配置都在app.config文件中。 我正在使用城堡wcffacility并注册这样的服务 -
Container.AddFacility<WcfFacility>();
Container.AddFacility<LoggingFacility>(f => f.UseLog4Net());
container.Register(
Classes
.FromThisAssembly()
.Pick()
.If(x => x.IsClass
&& HasServiceContract(x))
.WithServiceDefaultInterfaces()
.Configure(c => c.AsWcfService().LifeStyle.HybridPerWcfOperationTransient()));
这会将ILog(来自log4net)注入到我的服务中而没有任何问题,但它无法注入IErrorHandler。
我已经使用IErrorHandler添加了ServiceBehaviour,以便我可以捕获用户未处理的异常并使用以下代码记录错误。
#region IErrorHandler Members
public ILog Logger { get; set; }
public bool HandleError(Exception error)
{
if (error is FaultException)
return false; // Let WCF do normal processing
return true; // Fault message is already generated
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
var uniqueKey = UniqueKeyGenerator.Generate();
//create the custom wcfexception before passing that to client
var wcfException = new CustomServerException("Unknown Error has occured. Please contact your administrator!", uniqueKey);
//log the exception
Logger.Error(uniqueKey, error);
MessageFault messageFault = MessageFault.CreateFault(
new FaultCode("Sender"),
new FaultReason(wcfException.Message),
wcfException,
new NetDataContractSerializer());
fault = Message.CreateMessage(version, messageFault, null);
}
我查看了这个stackoverflow post,但它太旧了,没有回复帖子所以发布了这个新问题。
更新
我通过greyAlien提供的答案得到了解决(部分暂时解决)。 我所要做的只是
将自定义servicebehavior类注册到城堡windsor。
Container.Register(
Component.For<IServiceBehavior>()
.ImplementedBy<PassThroughExceptionHandlingBehaviour>()
.Named("IServiceBehavior")
从app.config文件中删除serviceBehaviour扩展名。当我添加 配置文件中的行为扩展,由于某种原因,城堡不是 能够注入依赖项我认为Wcf正在创建新的 实例和记录器公共属性结果为空。
它现在适用于我,但需要了解(以后)如何使用behaviourextensions注入依赖项。
答案 0 :(得分:3)
这是一个自我托管的示例,可以完成我认为您正在尝试的事情。这是一个控制台应用程序。您需要以管理员身份启动visual studio才能让netsh注册localhost:55001
我正在使用城堡3.1。
源代码文件:
namespace WcfSelfHost
{
using System;
using Castle.Windsor;
using Castle.Facilities.WcfIntegration;
using System.ServiceModel;
using Castle.MicroKernel.Registration;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using Castle.MicroKernel;
public interface ILog
{
void LogMessage(string message);
}
public class ConsoleLogger : ILog
{
public void LogMessage(string message)
{
Console.WriteLine(message);
}
}
[ServiceBehavior]
public class CastleCreatedLoggingServiceBehavior : IServiceBehavior
{
private readonly ILog logger;
public CastleCreatedLoggingServiceBehavior(ILog logger)
{
this.logger = logger;
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
this.logger.LogMessage("in AddBindingParameters");
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
this.logger.LogMessage("in ApplyDispatchBehavior");
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
this.logger.LogMessage("in Validate");
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
void TheOneOperation(string data);
}
public class ServiceImplementation : IService
{
private readonly ILog logger;
public ServiceImplementation(ILog logger)
{
this.logger = logger;
}
public void TheOneOperation(string data)
{
this.logger.LogMessage("service received message:");
this.logger.LogMessage(data);
}
}
public class ConsoleApplication
{
public static void Main()
{
//making this a variable to show the requirement that the names match
string serviceName = "TheService";
//configure the container with all the items we need
IWindsorContainer container = new WindsorContainer()
.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
.Register(
Component.For<CastleCreatedLoggingServiceBehavior>(),
Component.For<ILog>()
.ImplementedBy<ConsoleLogger>()
.LifestyleSingleton(),
Component.For<IService>()
.ImplementedBy<ServiceImplementation>()
.LifestyleSingleton()
.Named(serviceName)
);
//setup our factory with that has knowledge of our kernel.
DefaultServiceHostFactory factory = new DefaultServiceHostFactory(container.Kernel);
//create a host for our service matching the name of the castle component. Not adding any additional base addresses.
using (ServiceHostBase host = factory.CreateServiceHost(serviceName, new Uri[0]))
{
host.Open();
Console.WriteLine("server listening for messages");
//and here's the client..
IWindsorContainer clientContainer = new WindsorContainer()
.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
.Register(
Component.For<IService>()
.AsWcfClient(WcfEndpoint.FromConfiguration("serviceEndpointDefinition"))
);
IService client = clientContainer.Resolve<IService>();
client.TheOneOperation("data from client");
Console.ReadLine();
}
}
}
}
这是控制台应用程序的app.config文件。我们本可以使用流畅的API在源代码中配置所有这些,但是将服务和服务分开。客户端配置很正常,所以我选择了配置文件路由。如果你想要一个c#流畅的API版本,请告诉我。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="overrideMessageSize_forBasicHttpBinding" maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647"/>
</basicHttpBinding>
</bindings>
<services>
<service name="WcfSelfHost.ServiceImplementation">
<host>
<baseAddresses>
<add baseAddress="http://localhost:55001/baseaddress"/>
</baseAddresses>
</host>
<endpoint
contract="WcfSelfHost.IService"
binding="basicHttpBinding"
bindingConfiguration="overrideMessageSize_forBasicHttpBinding"
address="http://localhost:55001/baseaddress/serviceimplementation"
/>
</service>
</services>
<client>
<endpoint
name="serviceEndpointDefinition"
contract="WcfSelfHost.IService"
binding="basicHttpBinding"
bindingConfiguration="overrideMessageSize_forBasicHttpBinding"
address="http://localhost:55001/baseaddress/serviceimplementation"
/>
</client>
</system.serviceModel>
</configuration>