2011-10-04 4 views
0

로깅, 유효성 검사, 감사 등과 같은 교차 절단 문제를 해결하기 위해 WCF와 PIAB를 어울리는 것이 이상적입니다 (http://msdn.microsoft.com/en-us/magazine/cc136759.aspx 방문).WCF에서 정책 삽입으로 기록 할 사용자 지정 컨텍스트 데이터를 추가하는 방법?

그러나 늪지 표준 로그 호출 처리기는 로그에 대해 제한된 "확장 속성"집합 만 지원합니다. 클라이언트 IP 주소, 사용자 ID 등과 같은 추가 정보가 기록되도록 요구 사항이 있다면 어떻게 될까요? 많은 내가 같은 쿼리를 다른 사람에게 도움 희망이 솔루션을 내놓았다 주위에 파고 후

+0

스택 오버플로 질문입니다 및 응답 사이트. 답변을 질문으로 게시 한 것 같습니다. 질문에서 대답을 제거하고 답변으로 게시해야합니다. –

+0

예, 저는 그것을 깨달았습니다. 나는 매우 희미한 회색 색상의 편집 링크를 보지 못했습니다. 나는 이제 질문을 업데이트하고 5 시간 후에 대답을 추가 할 것이다. 그것을 지적 주셔서 감사합니다. – binjiezhao

답변

0

:

답변 (때문에 낮은 등급 회원에 유래의 이상한 정책 나중에 응답으로 추가됩니다).

우선, 사용자 지정 호출 처리기를 사용하여 로그에 필요한 모든 추가 데이터를 포함해야합니다. entlib 소스 코드를 참조하고 LogCallHandler를 찾을 수 있습니다. GetLogEntry 개인 방법에 추가 데이터를 추가

private TraceLogEntry GetLogEntry(IMethodInvocation input) 
    { 
     var logEntry = new CustomLogEntry(); 
     var formatter = new CategoryFormatter(input.MethodBase); 
     foreach (string category in categories) 
     { 
      logEntry.Categories.Add(formatter.FormatCategory(category)); 
     } 

     //slot = Thread.GetNamedDataSlot("PatientId"); 
     //logEntry.PatientId = Thread.GetData(slot).ToString(); 
     //logEntry.PatientId = CallContext.GetData("__PatientId").ToString(); 
     logEntry.AppName = ApplicationContext.Current["AppName"].ToString(); 
     logEntry.ClientIp = ApplicationContext.Current["ClientIp"].ToString(); 
     logEntry.UserId = ApplicationContext.Current["UserId"].ToString(); 
     logEntry.PatientId = ApplicationContext.Current["PatientId"].ToString(); 
     logEntry.EventId = eventId; 
     logEntry.Priority = priority; 
     logEntry.Severity = severity; 
     logEntry.Title = LogCallHandlerDefaults.Title; 

     if (includeParameters) 
     { 
      Dictionary<string, object> parameters = new Dictionary<string, object>(); 
      for (int i = 0; i < input.Arguments.Count; ++i) 
      { 
       parameters[input.Arguments.GetParameterInfo(i).Name] = input.Arguments[i]; 
      } 

      logEntry.ExtendedProperties = parameters; 
     } 

     if (includeCallStack) 
     { 
      logEntry.CallStack = Environment.StackTrace; 
     } 

     logEntry.TypeName = input.Target.GetType().FullName; 
     logEntry.MethodName = input.MethodBase.Name; 
     return logEntry; 
    } 

thatn 후에는 클라이언트에서 서버에 컨텍스트 데이터를 전파 할 infrastruture를 creat에해야합니다.

서비스 클라이언트에서
[Serializable] 
public class ApplicationContext : Dictionary<string, object> 
{ 
    private const string CALL_CONTEXT_KEY = "__Context"; 
    public const string ContextHeaderLocalName = "__Context"; 
    public const string ContextHeaderNamespace = "urn:tempuri.org"; 

    private static void EnsureSerializable(object value) 
    { 
     if (value == null) 
     { 
      throw new ArgumentNullException("value"); 
     } 
     if (!value.GetType().IsSerializable) 
     { 
      throw new ArgumentException(string.Format("The argument of the type \"{0}\" is not serializable!", value.GetType().FullName)); 
     } 
    } 

    public new object this[string key] 
    { 
     get { return base[key]; } 
     set 
     { EnsureSerializable(value); base[key] = value; } 
    } 

    public int Counter 
    { 
     get { return (int)this["__Count"]; } 
     set { this["__Count"] = value; } 
    } 

    public static ApplicationContext Current 
    { 
     get 
     { 
      if (CallContext.GetData(CALL_CONTEXT_KEY) == null) 
      { 
       CallContext.SetData(CALL_CONTEXT_KEY, new ApplicationContext()); 
      } 

      return CallContext.GetData(CALL_CONTEXT_KEY) as ApplicationContext; 
     } 
     set 
     { 
      CallContext.SetData(CALL_CONTEXT_KEY, value); 
     } 
    } 
} 

,이 문맥 IClientMessageInspector 구현을 통해 요청 메시지 헤더에 추가됩니다 CallContext 컨텍스트 데이터에 대한 사전 객체를 저장하는 나는 래퍼 클래스가 있습니다.

public class AuditInfoCallContextInitializer : ICallContextInitializer 
{ 
    #region Implementation of ICallContextInitializer 
    /// <summary> 
    /// Extract context data from message header through local name and namespace, 
    /// set the data to ApplicationContext.Current. 
    /// </summary> 
    /// <param name="instanceContext"></param> 
    /// <param name="channel"></param> 
    /// <param name="message"></param> 
    /// <returns></returns> 
    public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message) 
    { 
     var context = message.Headers.GetHeader<ApplicationContext>(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace); 
     if (context == null) { return null; } 

     ApplicationContext.Current = context; 
     return ApplicationContext.Current; 

    } 

    /// <summary> 
    /// Retrieve context from correlationState and store it back to reply message header for client. 
    /// </summary> 
    /// <param name="correlationState"></param> 
    public void AfterInvoke(object correlationState) 
    { 
     var context = correlationState as ApplicationContext; 
     if (context == null) 
     { 
      return; 
     } 
     var contextHeader = new MessageHeader<ApplicationContext>(context); 
     OperationContext.Current.OutgoingMessageHeaders.Add(contextHeader.GetUntypedHeader(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace)); 
     ApplicationContext.Current = null; 

    } 

    #endregion 
} 

이 본질적으로 왕복입니다 : 서비스 측면에서

public class ClientAuditInfoInspector : IClientMessageInspector 
{ 
    #region Implementation of IClientMessageInspector 

    public object BeforeSendRequest(ref Message request, IClientChannel channel) 
    { 
     var contextHeader = new MessageHeader<ApplicationContext>(ApplicationContext.Current); 
     request.Headers.Add(contextHeader.GetUntypedHeader(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace)); 
     return null; 
    } 

    public void AfterReceiveReply(ref Message reply, object correlationState) 
    { 
     if (reply.Headers.FindHeader(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace) < 0) { return; } 
     var context = reply.Headers.GetHeader<ApplicationContext>(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace); 
     if (context == null) { return; } 
     ApplicationContext.Current = context; 
    } 

    #endregion 
} 

, 나는 수신 메시지에서 메시지 헤더를 검색하고 보내는 메시지로 다시 설정 ICallContextInitializer의 사용자 지정 구현을 메시지 헤더 페이로드가 이동하기 때문입니다. AfterInvoke 메서드에서 메시지 헤더를 다시 보내기 전에 수정할 수 있습니다. 마지막으로 MessageInspector 및 CallContextInitializer를 적용하기위한 끝점 동작을 만들었습니다.

public class AuditInfoContextPropagationEndpointBehavior : BehaviorExtensionElement, IEndpointBehavior 
{ 
    #region Overrides of BehaviorExtensionElement 

    protected override object CreateBehavior() 
    { 
     return new AuditInfoContextPropagationEndpointBehavior(); 
    } 

    public override Type BehaviorType 
    { 
     get { return typeof(AuditInfoContextPropagationEndpointBehavior); } 
    } 

    #endregion 

    #region Implementation of IEndpointBehavior 

    public void Validate(ServiceEndpoint endpoint) 
    { 
     return; 
    } 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
     return; 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
     foreach (var operation in endpointDispatcher.DispatchRuntime.Operations) 
     { 
      operation.CallContextInitializers.Add(new AuditInfoCallContextInitializer()); 
     } 

    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
     clientRuntime.MessageInspectors.Add(new ClientAuditInfoInspector()); 
    } 

    #endregion 
} 

서비스/계약을 행동 특성으로 꾸미면 동일한 계약 행위를 작성할 수도 있습니다. 서비스 클라이언트에서 지금

, 당신은 다음과 같은 모든 상황에 맞는 데이터를 설정할 수 있습니다

using (var channelFactory = new ChannelFactory<ICustomerService>("WSHttpBinding_ICustomerService")) 
     { 
      var client = channelFactory.CreateChannel(); 
      ApplicationContext.Current["AppName"] = "Test application"; 
      ApplicationContext.Current["ClientIp"] = @"1.1.0.1"; 
      ApplicationContext.Current["UserId"] = "foo"; 
      ApplicationContext.Current["PatientId"] = "bar123"; 

      Console.WriteLine("Retreiving Customer 1"); 
      Customer cust = client.GetCustomer("1"); 
      Console.WriteLine("Retreived Customer, Name: [" + cust.Name + "]"); 
     } 

이도에 entlib.codeplex의 토론 게시판에 게시가 : http://entlib.codeplex.com/discussions/266963