【问题标题】:WCF Generic Error Handler with Autofac Injection带有 Autofac 注入的 WCF 通用错误处理程序
【发布时间】:2018-10-23 18:23:53
【问题描述】:

我试图在我的 wcf 项目中实现一个简单的通用错误处理程序,但问题是我想注入一个服务(使用 autofac)以保存所有异常。我到处找,我什么也没找到。我将 Autofac 与 Autofac.Integration.Wcf 一起使用。

public class GlobalErrorHandler : IErrorHandler
{

    private IErrorService ErrorService;

    public GlobalErrorHandler(IErrorService errorService)
    {
        this.ErrorService = errorService;
    }

    public bool HandleError(Exception error)
    {
        return true;
    }

    public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
    {
        //log the error using the service
    }
}

public class ErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
{

    public override Type BehaviorType
    {
        get { return GetType(); }
    }

    protected override object CreateBehavior()
    {
        return this;
    }

    private IErrorHandler GetInstance()
    {
        return new GlobalErrorHandler();
    }

    void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        IErrorHandler errorHandlerInstance = GetInstance();

        foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
        {
            dispatcher.ErrorHandlers.Add(errorHandlerInstance);
        }
    }

    void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
        {
            if (endpoint.Contract.Name.Equals("IMetadataExchange") &&
                endpoint.Contract.Namespace.Equals("http://schemas.microsoft.com/2006/04/mex"))
                continue;

            foreach (OperationDescription description in endpoint.Contract.Operations)
            {
                if (description.Faults.Count == 0)
                {
                    throw new InvalidOperationException("FaultContractAttribute not found on this method");
                }
            }
        }
    }
}

web.config

<extensions>
  <behaviorExtensions>
    <add name="errorHandler"
          type="Base.WCF.Helpers.Error_Handler.ErrorHandlerExtension, Base.WCF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </behaviorExtensions>
</extensions>

global.asax

var builder = new ContainerBuilder();
builder.RegisterType<Base.WCF.BaseService>();
builder.RegisterType<ErrorService>().As<IErrorService>();
var IOCContainer = builder.Build();
AutofacHostFactory.Container = IOCContainer;

我找不到任何方法可以让我在 IErrorHandler 中注入服务,因为我无法解决它的依赖关系。问题是我必须通过 ApplyDispatchBehavior 注册自定义 IErrorHandler。我也在我的 ErrorHandlerExtension 中尝试了构造函数注入,但它也没有工作。我的 wcf 服务方法中的所有其他注入也可以正常工作。

有什么方法可以在我的 IErrorHandler 中注入 IErrorService 吗?

编辑

根据 Travis 的回答,我还必须解决我的其他存储库注入问题,因此我使用了以下方法

        using (var scope = AutofacHostFactory.Container.BeginLifetimeScope())
        {
            var svc = scope.Resolve<IErrorHistoryService>();
            scope.Resolve<IErrorHistoryRepository>();
            scope.Resolve<IUnitOfWork>();
            svc.AddError(new ErrorLog_BO());
            svc.SaveError();
        }

【问题讨论】:

    标签: c# rest wcf autofac


    【解决方案1】:

    这里的简短答案是确实没有什么好方法可以做到这一点。

    WCF 在依赖注入方面有着臭名昭著的糟糕故事。您甚至可以对服务实例进行 DI 的唯一方法是因为有一个特定的 IInstanceProvider interface 用于处理创建服务的新实例。

    您会注意到,在实现您的扩展时,您最终不得不将错误处理程序的实例附加到通道,而不是提供某种可用的工厂/函数。

    一个不太好的选择是在您的IErrorHandler 中使用服务位置。

    public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
    {
      //log the error using the service
      var svc = AutofacHostFactory.Container.Resolve<IErrorService>();
      svc.Log(error); 
    }
    

    这不是很好的原因有几个。

    • 它会从根容器中出来,所以如果 IErrorService 中的任何内容是 IDisposable,您就会遇到内存泄漏,因为在容器被释放之前它不会被释放。
    • 您不会与服务处于相同的生命周期范围内,因此除非它们是单例,否则您不会有共享依赖项。

    您可以在某种程度上像这样修复第一个问题:

    public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
    {
      using(var scope = AutofacHostFactory.Container.BeginLifetimeScope())
      {
        var svc = scope.Resolve<IErrorService>();
        svc.Log(error);
      } 
    }
    

    这至少应该避免IDisposable 实例的内存泄漏......但是您仍然会遇到共享依赖问题。也许这无关紧要。取决于你。

    但是...不,WCF 中的大多数内容都不会通过 DI 运行,因此您将被困在这个和其他类似的事情上。

    【讨论】:

    • 这看起来不错,简单而且有效!我的 errorService 使用了另外 2 个构造函数注入(IErrorRepository 和 IUnitOfWork),所以我还必须通过在 using 范围内使用 scope.Resolve() 和 scope.Resolve() 来解决它们,以便它工作。我用你的回答编辑了我原来的帖子
    • 如果这是答案,请接受它,以便我为您提供帮助。
    猜你喜欢
    • 2014-12-15
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    相关资源
    最近更新 更多