简体   繁体   English

Castle Windsor - 将记录器注入IErrorHandler

[英]Castle Windsor - Injecting logger into IErrorHandler

Am using Castle windsor 3.0 in my Wcf services project. 我在Wcf服务项目中使用Castle windsor 3.0。 I'm hosting it as windows service project using Topshelf. 我使用Topshelf将其作为Windows服务项目托管。 All my wcf services configuration is on app.config file. 我的所有wcf服务配置都在app.config文件中。 I'm using castle wcffacility and registering services like this - 我正在使用城堡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()));

This injects ILog (from log4net) into my services without any problem but it fails to inject into IErrorHandler. 这会将ILog(来自log4net)注入到我的服务中而没有任何问题,但它无法注入到IErrorHandler中。

I have added ServiceBehaviour with IErrorHandler so that I can catch user unhandled exceptions and log the errors using the below code. 我已经使用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);

    }

I looked at this stackoverflow post but it was too old and no answer was posted so am posting this new question. 我查看了这个stackoverflow 帖子,但它太旧了,没有回复帖子所以我发布了这个新问题。

Update 更新

I got this resolved (partially for now) with the answer provided by greyAlien. 我通过greyAlien提供的答案得到了解决(部分暂时解决)。 All I had to do was 我所要做的就是

  1. register custom servicebehavior class to castle windsor. 将自定义servicebehavior类注册到城堡windsor。

     Container.Register( Component.For<IServiceBehavior>() .ImplementedBy<PassThroughExceptionHandlingBehaviour>() .Named("IServiceBehavior") 
  2. Remove the serviceBehaviour extension from the app.config file. 从app.config文件中删除serviceBehaviour扩展。 When I add behavior extension in config file, for some reason, castle is not able to inject the dependencies instead I think Wcf is creating new instances and logger public property is turning out to be null. 当我在配置文件中添加行为扩展时,由于某种原因,castle无法注入依赖项,而我认为Wcf正在创建新实例,而logger公共属性变为null。

It works for me now but need to understand (in future) on how to inject dependencies using behaviourextensions as well. 它现在适用于我,但需要了解(以后)如何使用behaviourextensions注入依赖项。

Here's a self hosted sample that does the things I believe you are trying to do. 这是一个自我托管的示例,它可以完成我认为您正在尝试的事情。 It's a console app. 这是一个控制台应用程序。 You will need to start visual studio as an administrator in order to get netsh to register localhost:55001 您需要以管理员身份启动visual studio才能让netsh注册localhost:55001

I'm using castle 3.1. 我正在使用城堡3.1。

The source code file: 源代码文件:

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();

        }
    }
}
}

Here's the app.config file for the console application. 这是控制台应用程序的app.config文件。 We could have used the fluent API to configure all of this in the source code, but separating out the services & client config is pretty normal, so I chose to go config file route. 我们可以使用流畅的API在源代码中配置所有这些,但是分离出服务和客户端配置是很正常的,所以我选择了配置文件路由。 Let me know if you want ac# fluent API version. 如果你想要ac #fluent 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM