I have created a custom implementation of the IDispatchMessageInspector interface, and my code is working 99% fine.
My problem is that I need to free some managed objects when the WCF service host is killed and/or frees an instance of my class. My objects to free implement IDisposable but they aren't being disposed. I've been through the MSDN library (more confused) and the SO archives, but haven't found anything that addresses the question "When/where does a WCF service host destroy MessageInspectors?"
Do I need to hook an event someplace? Do I need to implement something even more arcane from the ServiceModel namespace?
Can anyone give me a pointer in the right direction?
Edit 1: Clarifications
At the moment, I am running in the IDE using the automatic webserver. I am not ultimately in control of the host once in production, could be any of the valid server host choices.
The MyCore.My and MyCore.MyProperties objects are the ones I am trying to dispose of when the WCF server host is killed/bounced.
Even when I have killed the webserver processes (those things in the Taskbar) the Dispose() is never called.
Edit 2: Code snippets added.
using /* snip */
using MyCore = Acme.My;
namespace My.SOAP
{
public class MyMessageInspector : IDispatchMessageInspector
{
protected static MyCore.My _My;
protected static MyCore.MyProperties _MyProps;
protected static ConcurrentDictionary<string, MyCore.AnotherSecretThing> _anotherSecretThings = new ConcurrentDictionary<string, MyCore.AnotherSecretThing>();
protected static void InitMy()
{
if (_My != null) return;
_MyProps = new MyCore.MyProperties();
MyCore.MySqlDatabaseLogger logger = new MyCore.MySqlDatabaseLogger(_MyProps);
_My = new MyCore.My(logger);
}
public MyMessageInspector()
{
InitMy();
}
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
MyMessageHeader header = null;
try
{
// find My header
Int32 headerPosition = request.Headers.FindHeader(MyMessageHeaderKey.MyHeaderElementName, MyMessageHeaderKey.MyNamespace);
// get reader
XmlDictionaryReader reader = request.Headers.GetReaderAtHeader(headerPosition);
// get My header object
header = MyMessageHeader.ReadHeader(reader);
// add to incoming messages properties dictionary
OperationContext.Current.IncomingMessageProperties.Add(MyMessageHeaderKey.MyHeaderElementName, header);
}
catch (Exception ex)
{
// log via ExceptionHandlingBlock
}
MyCore.SecretThings SecretThings = CreateSecretThings(/* snip */);
return SecretThings.Id;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
MyCore.SecretThings req = _My.GetSecretThingsOnThread();
// if we didn't find the SecretThings, there is nothing to stop() and no data to put in the MessageHeaders
if (req == null) return;
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();
var MyHeader = new MyMessageHeader(/* snip */);
reply.Headers.Add(MyHeader);
req.Stop(MyCore.Status.SUCCESS);
}
protected MyCore.SecretThings CreateSecretThings(string key, Dictionary<string, string> ids)
{
/* snip */
return _My.GetSecretThings(/* snip */);
}
}
}
I've been having a look at the DispatchMessageInspector and how it is implemented.
As you probably know you register your MessageInspectors with an IEndpointBehavior (adding endpoint behaviours through configuration or code). You create an instance of your DispatchMessageInspector within the EndpointBehaviour.
public class MyBehaviour : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint,
System.ServiceModel.Channels.BindingParameterCollection
bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
var inspector = new SampleMessageInspector(); //register
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
According to http://msdn.microsoft.com/en-us/magazine/cc163302.aspx Endpoint behaviours are registered by the Service Host
These behavior collections are automatically populated during the ServiceHost and ChannelFactory construction process with any behaviors that are found in your code (via attributes) or within the configuration file (more on this shortly). You can also add behaviors to these collections manually after construction. The following example shows how to add the ConsoleMessageTracing to the host as a service behavior:
ServiceHost host = new ServiceHost(typeof(ZipCodeService));
host.Description.Behaviors.Add(new ConsoleMessageTracing());
And further dictates that the ServiceHost has a lifetime as long as the service...
ServiceHost extension objects remain in memory for the lifetime of the ServiceHost while InstanceContext and OperationContext extension objects only remain in memory for the lifetime of the service instance or operation invocation. Your custom dispatcher/proxy extensions can use these collections to store (and look up) user-defined state throughout the pipeline.
I'm assuming this is why your objects within your MessageInspectors are never being destroyed.
Some would see it as an anti-pattern but I would possibly recommend a ServiceLocator that your MessageInspectors can use to retrieve objects. You could then look at setting their lifetime as long as its parent usage?
public class SampleMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
{
var objectToDispose = ServiceLocator.Current.GetInstance<ObjectToDispose>();
//do your work
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
//do some other work
}
}
To follow on from what i've mentioned...
As an example this post mentions using Ninject as the IoC container and set the life time of your objects as the lifetime of the WCF service
Bind(...).To(...).InScope(() => OperationContext.Current)
Ninject WCF Garbage Collection on repositories
You could then get access to the Ninject Kernal through the ServiceLocator and the objects (_MyProperties etc...) would be disposed off
Related
I'm trying to log all outbound requests that go to service references, including the full request and response body. I thought I had a solution using behaviorExtensions but, after deploying, it became clear that the extension was shared between multiple requests.
Here's my current code:
public class LoggingBehaviorExtender : BehaviorExtensionElement
{
public override Type BehaviorType => typeof(LoggingRequestExtender);
protected override object CreateBehavior() { return new LoggingRequestExtender(); }
}
public class LoggingRequestExtender : IClientMessageInspector, IEndpointBehavior
{
public string Request { get; private set; }
public string Response { get; private set; }
#region IClientMessageInspector
public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
Request = request.ToString();
Response = null;
return null;
}
public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
Response = reply.ToString();
}
#endregion
#region IEndpointBehavior
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(this);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
#endregion
}
Then, when I reach the point to log, I extract the behavior...
var lre = client.Endpoint.Behaviors.OfType<LoggingRequestExtender>().FirstOrDefault();
var req = lre?.Request;
var resp = lre?.Response;
Adding debugging logging to the LoggingRequestExtender, I found it was only instantiated once for multiple requests.
Is there a way to make sure this behavior class is instantiated fresh for each thread? Or is there a better way of getting the full request / response body when making service calls?
Edit / Partial answer:
Since writing this I have discovered that the value returned by BeforeSendRequest is passed into AfterReceiveReply as the correlationState so I can connect the request and response using a guid:
public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
var guid = Guid.NewGuid();
WebServiceLog.LogCallStart(guid, channel.RemoteAddress.ToString(), request.ToString());
return guid;
}
public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
Guid guid = (Guid)correlationState;
WebServiceLog.LogCallEnd(guid, reply.ToString());
}
I see two flaws to this approach. One, which is livable, is that this requires a log insert and then update rather than a single insert.
The second is more of an issue: In the case of an exception (e.g. timeout), we never hit AfterRecieveSupply so the log doesn't know what happened. I can separately log the exception...
try
{
response = client.SomeFunction(request);
}
catch (Exception ex)
{
AppLog.Error("Some function failed", ex);
}
... but I can't see a way of accessing the guid outside of BeforeSendRequest / AfterReceiveReply so I have nothing to tie the exception log to the service request log.
There are several approaches to this.
1, The situation you have described with having to log calls separately doesn't have to be like that. If your WCF service is in a non load balanced server just add the request to a MemoryCache using the Guid as a key. When the request comes in then pull off the request and log in one go. To capture the timed out calls you could run a process on a thread that would check the MemoryCache every x minutes to pull out and log (using an adequate lock to ensure thread saftey).
If the WCF service is in a load balanced environment then all you do is the same as above but store to a no sql type data store.
2, Is the code that makes the outbound calls within your scope for change? If so, you can forgo creating a behavior extension and create a bespoke message logger instead. Using a class that implements IDisposable you can write nice code like this..
RequestMessage request = new RequestMessage();
ResponseMessage response = null;
using (_messageLogger.LogMessage(request, () => response, CallContextHelper.GetContextId(), enabled))
{
response = _outboundService.DoSomething(request);
}
This will then not need another process to capture any timed out threads which will be handled in the dispose method.
If you need more clarity then let me know, hopefully this helps you...
Problem: As part of a debugging a problem regarding some validation issues I would like to read the XML request of an WCF webservice.
Apparently, this is more difficult than it appears and any help in this regard would be much appreciated. Below are what I've tried already. Much like the answer to a similar question here on StackOverflow (link).
My solution: I've created the client setting the endpoint given by the provider of the webservice. I've added my client credentials as an endpoint behavior. Right before I make the call to service I add another endpoint behavior to write the request and response as XML-files. Alas, to no avail.
The simple call to the webservice:
public SaveAvailabilityAssessmentResponseType SaveAvailabilityAssessment(SaveAvailabilityAssessmentRequestType request)
{
Client.Endpoint.Behaviors.Add(new CustomEndpointBehavior());
return Client.SaveAvailabilityAssessment(_ocesCertHeader, _activeOrganisationHeader, request);
}
And here are the CustomEndpointBehavior class (simplified a bit):
public class CustomEndpointBehavior : IEndpointBehavior
{
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new MessageExpector());
}
}
And here's the MessageExpector class:
internal class MessageExpector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
using (var sw = new StreamWriter(#"C:\temp\response.xml"))
{
sw.WriteLine(reply);
}
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
using (var sw = new StreamWriter(#"C:\temp\request.xml"))
{
sw.WriteLine(request);
}
return new object();
}
}
Can anyone tell me what I'm missing?
Edit: Further debugging has showed, that the code in the CustomEndpointBehavior hasn't been activated. It is as if the customendpoint hasn't been added to the client's endpoint behaviors. But how can that be?
You can configure message logging without modifying your code. Here's a link to documentation.
You can use SvcTraceViewer.exe for viewing this logs
In my WCF service I have a custom message inspector for validating incoming messages as raw XML against an XML Schema. The message inspector has a few dependencies that it takes (such as a logger and the XML schema collection). My question is, can I use a Dependency Injection framework (I'm using Ninject at the moment) to instantiate these custom behaviours and automatically inject the dependencies?
I've made a simple example demonstrating the concept:
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Ninject.Extensions.Logging;
public class LogMessageInspector : IDispatchMessageInspector
{
private readonly ILogger log;
public LogMessageInspector(ILogger log)
{
this.log = log;
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
LogMessage(ref request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
LogMessage(ref reply);
}
private void LogMessage(ref Message message)
{
//... copy the message and log using this.log ...
}
}
public class LogMessageBehavior : IEndpointBehavior
{
private readonly IDispatchMessageInspector inspector;
public LogMessageBehavior(IDispatchMessageInspector inspector)
{
this.inspector = inspector;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this.inspector);
}
public void Validate(ServiceEndpoint endpoint) { }
}
How can I go about injecting an ILogger into LogMessageInspector and a LogMessageInspector into LogMessageBehavior?
Second question, is this overkill?
Edit: I can get this to work if I build my service in code because I create the behaviour using Ninject. However, when configuring the service via config, I need to add an additional class that extends BehaviorExtensionElement. This class is created by WCF and I can't seem to find a way to cause that to be created by Ninject instead. Configured in code:
static void Main(string[] args)
{
using (IKernel kernel = new StandardKernel())
{
kernel.Bind<IEchoService>().To<EchoService>();
kernel.Bind<LogMessageInspector>().ToSelf();
kernel.Bind<LogMessageBehavior>().ToSelf();
NinjectServiceHost<EchoService> host = kernel.Get<NinjectServiceHost<EchoService>>();
ServiceEndpoint endpoint = host.AddServiceEndpoint(
typeof(IEchoService),
new NetNamedPipeBinding(),
"net.pipe://localhost/EchoService"
);
endpoint.Behaviors.Add(kernel.Get<LogMessageBehavior>());
host.Open();
Console.WriteLine("Server started, press enter to exit");
Console.ReadLine();
}
}
This works fine, but I don't know how to create the behaviour when configured via my app.config:
<system.serviceModel>
<services>
<service name="Service.EchoService">
<endpoint address="net.pipe://localhost/EchoService"
binding="netNamedPipeBinding"
contract="Contracts.IEchoService"
behaviorConfiguration="LogBehaviour"
/>
</service>
</services>
<extensions>
<behaviorExtensions>
<add name="logMessages" type="Service.LogMessagesExtensionElement, Service" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="LogBehaviour">
<logMessages />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
public class LogMessagesExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(LogMessageBehavior); }
}
protected override object CreateBehavior()
{
//how do I create an instance using the IoC container here?
}
}
How can I go about injecting an ILogger into LogMessageInspector and a
LogMessageInspector into LogMessageBehavior?
The approach has been described here
UPDATE
Please correct me if I'm wrong but I guess the question boils down to how could you get an instance of Ninject kernel in BehaviorExtensionElement.CreateBehavior? The answer depends on your hosting scenario. If hosted under IIS you could add something like this to your NinjectWebCommon:
public static StandardKernel Kernel
{
get { return (StandardKernel)bootstrapper.Kernel; }
}
Since you seem to be self-hosting, you might want to go for a static instance of the kernel too. In my view, however, this is not a terribly good idea.
I'd actually vote for your own approach and configure the behavior programmatically unless BehaviorExtensionElement is necessary because you need to be able to configure the behavior through the config file.
is this overkill?
It depends, but definitely not if you're going to unit test the implementation.
Instead of validation raw XML against an XML schema, why not take a more object oriented approach? You could for instance model each operation as a single message (a DTO) and hide the actual logic behind a generic interface. So instead of having a WCF service which contains a MoveCustomer(int customerId, Address address) method, there would be a MoveCustomerCommand { CustomerId, Address } class and the actual logic would be implemented by a class that implements the ICommandHandler<MoveCustomerCommand> interface with a single Handle(TCommand) method.
This design gives the following advantages:
Every operation in the system gets its own class (SRP)
Those message/command objects will get the WCF contract
The WCF service will contain just a single service class with a single method. This leads to a WCF service that is highly maintainable.
Allows adding cross-cutting concerns by implementing decorators for the ICommandHandler<T> interface (OCP)
Allows validation to be placed on the message/command objects (using attributes for instance) and allows this validation to be added again by using decorators.
When you apply a design based around a single generic ICommandHandler<TCommand> interface, its very easy to create generic decorators that can be applied to all implementations. Some decorators might only be needed to be applied when running inside a WCF service, others (like validation) might be needed for other application types as well.
A message could be defined as follows:
public class MoveCustomerCommand
{
[Range(1, Int32.MaxValue)]
public int CustomerId { get; set; }
[Required]
[ObjectValidator]
public Address NewAddress { get; set; }
}
This message defines an operation that will move the customer with CustomerId to the supplied NewAddress. The attributes define what state is valid. With this we can simply do object based validation using .NET DataAnnotations or Enterprise Library Validation Application Block. This is much nicer than having to write XSD based XML validations which are quite unmaintainable. And this is much nicer than having to do complex WCF configurations as you are currently trying to solve. And instead of baking this validation inside the WCF service, we can simply define a decorator class that ensures every command is validated as follows:
public class ValidationCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private ICommandHandler<TCommand> decoratedHandler;
public ValidationCommandHandlerDecorator(
ICommandHandler<TCommand> decoratedHandler)
{
this.decoratedHandler = decoratedHandler;
}
public void Handle(TCommand command)
{
// Throws a ValidationException if invalid.
Validator.Validate(command);
this.decoratedHandler.Handle(command);
}
}
This ValidationCommandHandlerDecorator<T> decorator can be used by any type of application; not only WCF. Since WCF will by default not handle any thrown ValidationException, you might define a special decorator for WCF:
public class WcfFaultsCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private ICommandHandler<TCommand> decoratedHandler;
public WcfFaultsCommandHandlerDecorator(
ICommandHandler<TCommand> decoratedHandler)
{
this.decoratedHandler = decoratedHandler;
}
public void Handle(TCommand command)
{
try
{
this.decoratedHandler.Handle(command);
}
catch (ValidationException ex)
{
// Allows WCF to communicate the validation
// exception back to the client.
throw new FaultException<ValidationResults>(
ex.ValidationResults);
}
}
}
Without using a DI container, a new command handler could be created as follows:
ICommandHandler<MoveCustomerCommand> handler =
new WcfFaultsCommandHandlerDecorator<MoveCustomerCommand>(
new ValidationCommandHandlerDecorator<MoveCustomerCommand>(
// the real thing
new MoveCustomerCommandHandler()
)
);
handler.Handle(command);
If you want to know more about this type of design, read the following articles:
Meanwhile... on the command side of my architecture
Writing Highly Maintainable WCF Services
Try having your LogMessageBehavior also use BehaviorExtensionElement as its base class, then you should be able to do the following:
public override Type BehaviorType
{
get { return this.GetType(); }
}
protected override object CreateBehavior()
{
return this;
}
I am implementing WCF service that exposes a method whose [OperationContract] is [XmlSerializerFormat]. I sometimes get request whose body is not valid XML. In such cases I want to log the original body, so I can know why it didn't constitute valid XML. However, I can't get it from the Message object, see my attempts (by implementing IDispatchMessageInspector interface):
public object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
request.ToString(); // "... Error reading body: System.Xml.XmlException: The data at the root level is invalid. Line 1, position 1. ..."
request.WriteBody(...); // Serialization Exception, also in WriteMessage and other Write* methods
request.GetReaderAtBodyContents(...); // Same
HttpRequestMessageProperty httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; // no body in httpRequest
}
When looking in watch, request.messageData appears to contain the body - but that's a private member.
How can I get the message buffer without trying to deserialize it?
Yes, you need custom MessageEncoder, unlike message inspectors (IDispatchMessageInspector / IClientMessageInspector) it sees original byte content including any malformed XML data.
However it's not trivial how to implement this approach. You have to wrap a standard textMessageEncoding as custom binding element and adjust config file to use that custom binding.
Also you can see as example how I did it in my project - wrapping textMessageEncoding, logging encoder, custom binding element and config.
For the opposite direction (I am writing a WCF client and the server returns invalid XML), I was able to extract the raw reply message in IClientMessageInspector.AfterReceiveReply by
accessing the internal MessageData property of reply via Reflection, and then
accessing its Buffer property, which is an ArraySegment<byte>.
Something similar might be available for the request message on the server side; so it might be worth examining the request variable in the debugger.
I'm aware that this is not exactly what you are asking for (since you are on the server side, not on the client side), and I'm also aware that using reflection is error-prone and ugly. But since the correct solution is prohibitively complex (see baur's answer for details) and this "raw dump" is usually only required for debugging, I'll share my code anyways, in case it is helpful to someone in the future. It works for me on .NET Framework 4.8.
public void AfterReceiveReply(ref Message reply, object correlationState)
{
object messageData = reply.GetType()
.GetProperty("MessageData",
BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(reply, null);
var buffer = (ArraySegment<byte>)messageData.GetType()
.GetProperty("Buffer")
.GetValue(messageData, null);
byte[] rawData =
buffer.Array.Skip(buffer.Offset).Take(buffer.Count).ToArray();
// ... do something with rawData
}
And here's the full code of the EndpointBehavior:
public class WcfLogger : IEndpointBehavior
{
public byte[] RawLastResponseBytes { get; private set; }
// We don't need these IEndpointBehavior methods
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new MessageCaptureInspector(this));
}
internal class MessageCaptureInspector : IClientMessageInspector
{
private WcfLogger logger;
public MessageCaptureInspector(WcfLogger logger)
{
this.logger = logger;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Ugly reflection magic. We need this for the case where
// the reply is not valid XML, and, thus, reply.ToString()
// only contains an error message.
object messageData = reply.GetType()
.GetProperty("MessageData",
BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(reply, null);
var buffer = (ArraySegment<byte>)messageData.GetType()
.GetProperty("Buffer")
.GetValue(messageData, null);
logger.RawLastResponseBytes =
buffer.Array.Skip(buffer.Offset).Take(buffer.Count).ToArray();
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
}
Usage:
var logger = new WcfLogger();
myWcfClient.Endpoint.EndpointBehaviors.Add(logger);
try
{
// ... call WCF method that returns invalid XML
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
File.SaveAllBytes(#"C:\temp\raw_response.bin", logger.RawLastResponseBytes);
// Use the exception message and examine raw_response.bin with
// a hex editor to find the problem.
UPDATE
Some others that have run into this issue appear to have created a Customer Message Encoder.
A message encoding binding element serializes an outgoing Message and
passes it to the transport, or receives the serialized form of a
message from the transport and passes it to the protocol layer if
present, or to the application, if not present.
I have implemented a WCF inspector in my client application that consumes numerous web services.
I am using this inspector as a logging mechanism to log calls sent from the application to those web services and the responses they give back.
public class WcfClientInterceptor : IClientMessageInspector
{
protected static readonly ILog log4net = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly IMessageLogger Logger = new Log4NetLogger();
private MessageLogEntry LogEntry;// = new MessageLogEntry();
public void AfterReceiveReply(ref Message reply, object correlationState)
{
if (Logger.IsLogEnabled)
{
LogEntry.ResponseBody = reply.ToString();
Logger.Log(LogEntry);
}
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if (Logger.IsLogEnabled)
{
LogEntry = LogEntry ?? new MessageLogEntry();
//instanceContext.GetServiceInstance().GetType().Name
//LogEntry.WebServiceIdentity = request.Headers.Action;
LogEntry.WebServiceIdentity = OperationContext.Current.IncomingMessageHeaders.Action;
LogEntry.RequestBody = request.ToString();
}
return null;
}
}
My problem is that I don't know what web service is called. I want to get some kind of reference to them and log it.
This is the only method that works request.Headers.Action but it doesn't always work. Most of the time this is String.Empty.
OperationContext.Current is null which I understand is normal on the client side.
Is there any other way of getting the name of the webservice that is called?
or the name of the calling method? or something?
Thank you
it works with LogEntry.WebServiceIdentity = request.Headers.Action;
I made a mistake