2015-12-15 2 views
2

У меня есть эта ситуация: у меня есть служба WCF. Я обрабатываю все исключения на MyErrorHandler с реализованным интерфейсом IErrorHandler. Ниже приведен весь рабочий код.Как ввести объект в IErrorHandler WCF?

Что я хочу делать, но понятия не имею, как: Я хочу ввести объект (например, ILogger) в класс MyErrorHandler. В основном это означает, что мне нужно ввести объект здесь: [GlobalErrorHandlerBehaviour(typeof(MyErrorHandler))]. Не могли бы вы помочь мне решить эту проблему?

[ServiceContract] 
public interface ITestService 
{ 
    [OperationContract] 
    int GetTest(); 
} 

[GlobalErrorHandlerBehaviour(typeof(MyErrorHandler))] 
public class TestService : ITestService 
{ 
    public TestService(ILogger logger) 
    { 
     // Here, I'm already injecting logger. 
     // It's not imported for my question so I removed it for now 
    } 
    public int GetTest() 
    { 
     throw new Exception("Test"); 
    } 
} 

// This is attribute added to TestService class 
// How can I inject (via constructor) ILogger, or any other class?? 
public class GlobalErrorHandlerBehaviourAttribute : Attribute, IServiceBehavior 
{ 
    private readonly Type errorHandlerType; 
    public GlobalErrorHandlerBehaviourAttribute(Type errorHandlerType) 
    { 
     this.errorHandlerType = errorHandlerType; 
    } 

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

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
     IErrorHandler handler = (IErrorHandler)Activator.CreateInstance(errorHandlerType); 

     foreach(ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers) 
     { 
      ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher; 
      if (channelDispatcher != null) 
      { 
       channelDispatcher.ErrorHandlers.Add(handler); 
      } 
     } 
    } 

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
    } 
}  

public class MyErrorHandler : IErrorHandler 
{ 

    //--------------------------------------------------// 
    // I MUST INJECT ILOGGER HERE (or any other object) // 
    //--------------------------------------------------// 


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

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 
    { 
     FaultException fe = new FaultException(); 
     MessageFault message = fe.CreateMessageFault(); 
     fault = Message.CreateMessage(version, message, null); 
    } 
} 

КПП. Я хочу использовать DI и вводить что-то в IErrorHandler Я не хочу использовать метод private static readonly с регистратором.

+0

Почему вернуть '' true' в IErrorHandler.HandleError (ошибка Exception) ' ? [Документация] (https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler.handleerror (v = vs.110) .aspx) говорит: ** 'true' **' if Windows Communication Foundation (WCF) не должен прерывать сеанс (если он есть) и контекст экземпляра, если контекст экземпляра не является Single; в противном случае '**' false' ** '. По умолчанию используется '**' false' ** '.' Почему вы хотите продолжить сеанс и контексты экземпляра после критической ошибки? Тем более, что вы не обрабатываете его, вы просто регистрируете его. –

ответ

6

This вопрос относится к вашему. В принципе, вам не нужен GlobalErrorHandlerBehaviourAttribute. Вы можете добавить поведение к своей службе вручную. Что вам нужно сделать, так это создать свой ServiceHost. В this answer я более подробно объяснил, как это сделать.

Вот рабочий код хост-приложения, который впрыскивается в ILoggerIErrorHandler:

using System; 
using System.Collections.ObjectModel; 
using System.ServiceModel; 
using System.ServiceModel.Channels; 
using System.ServiceModel.Description; 
using System.ServiceModel.Dispatcher; 

namespace ConsoleHost 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var logger = new DummyLogger(); 
      var errorHandler = new TestErrorHandler(logger); 

      ServiceHost host = new TestServiceHost(errorHandler, typeof(TestService), new Uri("net.tcp://localhost:8002")); 
      host.Open(); 

      Console.WriteLine("Press enter to exit"); 
      Console.ReadKey(); 
     } 
    } 

    [ServiceContract] 
    public interface ITestService 
    { 
     [OperationContract] 
     string Test(int input); 
    } 

    public class TestService : ITestService 
    { 
     public string Test(int input) 
     { 
      throw new Exception("Test exception!"); 
     } 
    } 

    public class TestErrorHandler : IErrorHandler 
    { 
     private ILogger Logger { get; } 

     public TestErrorHandler(ILogger logger) 
     { 
      Logger = logger; 
     } 

     public bool HandleError(Exception error) 
     { 
      Logger.Log(error.Message); 
      return true; 
     } 

     public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 
     { 
      FaultException fe = new FaultException(); 
      MessageFault message = fe.CreateMessageFault(); 
      fault = Message.CreateMessage(version, message, null); 
     } 
    } 

    public class TestServiceHost : ServiceHost 
    { 
     private readonly IErrorHandler errorHandler; 

     public TestServiceHost(IErrorHandler errorHandler, Type serviceType, params Uri[] baseAddresses) 
      : base(serviceType, baseAddresses) 
     { 
      this.errorHandler = errorHandler; 
     } 

     protected override void OnOpening() 
     { 
      Description.Behaviors.Add(new ErrorHandlerBehaviour(errorHandler)); 
      base.OnOpening(); 
     } 

     class ErrorHandlerBehaviour : IServiceBehavior, IErrorHandler 
     { 
      private readonly IErrorHandler errorHandler; 

      public ErrorHandlerBehaviour(IErrorHandler errorHandler) 
      { 
       this.errorHandler = errorHandler; 
      } 

      bool IErrorHandler.HandleError(Exception error) 
      { 
       return errorHandler.HandleError(error); 
      } 

      void IErrorHandler.ProvideFault(Exception error, MessageVersion version, ref Message fault) 
      { 
       errorHandler.ProvideFault(error, version, ref fault); 
      } 

      void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
      { 
       foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) 
       { 
        channelDispatcher.ErrorHandlers.Add(this); 
       } 
      } 

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

      void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
      { 
      } 
     } 
    } 

    // Dummy logger 
    public interface ILogger 
    { 
     void Log(string input); 
    } 

    public class DummyLogger : ILogger 
    { 
     public void Log(string input) => Console.WriteLine(input); 
    } 
} 

Config:

<system.serviceModel> 
    <services> 
    <service name="ConsoleHost.TestService"> 
     <endpoint address="net.tcp://localhost:8002/TestService" 
       binding="netTcpBinding" 
       contract="ConsoleHost.ITestService" /> 
    </service> 
    </services> 
</system.serviceModel> 
0

Это интересный вопрос, но настройка контейнера DI не так прямо в WCF. Вы должны выполнить следующие настройки:

  1. Создание пользовательского поставщика Instance
  2. Создать множество пользовательских услуг
  3. Если его не самостоятельно хостинг, то установку пользовательских ServiceHostFactory.

См. Полный образец кода в MSDN для how to setup DI in WCF. После того, как DI настроена вам просто нужно изменить реализацию ErrorHandler использовать ILogger с помощью инъекции конструктора:

public class MyErrorHandler : IErrorHandler 
{ 
    private ILogger logger; 
    public MyErrorHandler(ILogger logger) 
    { 
    this.logger = logger; 
    } 
} 

Вот additional source для больше вариантов настройки InstanceProvider с другим типом DI.