I've been looking all over the place and I can't seem to find the answer. I have an extension endpoint behaviour which uses IParameterInspector. How can I handle an exception when it's thrown in BeforeCall method?
I've tried adding try-catch to IEndPointBehavior and BehaviorExtensionElement neither of which handles it. Here is some code:
BehaviorExtensionElement:
public class ExtensionService : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
//try-catch doesn't work here
return new ExtensionBehavior();
}
public override Type BehaviorType
{
get { return typeof(ExtensionBehavior); }
}
}
IEndpointBehavior:
public class ExtensionBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
//throw new NotImplementedException();
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
foreach (ClientOperation clientOperation in clientRuntime.ClientOperations)
{
//try-catch here doesn't work
clientOperation.ClientParameterInspectors.Add(new ParamInspector());
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (DispatchOperation dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{
//try-catch here doesn't work
dispatchOperation.ParameterInspectors.Add(new ParamInspector());
}
}
public void Validate(ServiceEndpoint endpoint)
{
//throw new NotImplementedException();
}
}
IParameterInspector
public class ParamInspector : IParameterInspector
{
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
}
public object BeforeCall(string operationName, object[] inputs)
{
///an exception is thrown here
return null;
}
}
I did finally manage to solve it. I had to implement IErrorHandler like so:
public class CustomErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
//the procedure for handling the errors.
//False is returned because every time we have an exception we want to abort the session.
return false;
}
public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{
}
}
and then add this IErrorHandler to ApplyDispatchBehavior
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (DispatchOperation dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{
dispatchOperation.ParameterInspectors.Add(new ParamInspector(this.Class));
}
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new CustomErrorHandler());
}
Related
A few days ago I opened a question if I succeed with the answers. I had not focused the question well, and now with something more knowledge I ask again.
I need to capture the errors of all my endpoints to have them included in the same site. The idea is to add a behavior to these endpoints.
namespace SIPE.Search.Helpers
{
/// <summary>
/// Implements methods that can be used to extend run-time behavior for an endpoint in either a client application.
/// </summary>
public class ExternalClientBehavior : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ExternalClientBehaviorClass();
}
public override Type BehaviorType
{
get
{
return typeof(ExternalClientBehaviorClass);
}
}
/// <summary>
/// JSON REST[GET] Converter Behavior
/// </summary>
private class ExternalClientBehaviorClass : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ExternalClientMessageInspector clientInspector = new ExternalClientMessageInspector(endpoint);
clientRuntime.MessageInspectors.Add(clientInspector);
foreach (ClientOperation op in clientRuntime.Operations)
{
op.ParameterInspectors.Add(clientInspector);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
//("Behavior not supported on the consumer side!");
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
}
}
Now I know that it will never enter my ApplyDispatchBehaviour if the client does not implement my behaviour, and this will NEVER happen, since they are external providers and I do not have access to the code.
Also, my first error does not even leave my code, since I'm causing a NOT FOUND error.
I have found many similar sources with my problem without solution. I have found by several sites to add the following in ApplyClientBehaviour:
IErrorHandler errorHandler = new CustomErrorHandler();
clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(errorHandler);
But this does not work.
Other sources that happened to me: https://riptutorial.com/csharp/example/5460/implementing-ierrorhandler-for-wcf-services
It is NOT a solution, since it is for Services Behavior. I need to do it in EndPoint Behavior.
Thank you
Please refer to the following example.
Server side.
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:1100");
BasicHttpBinding binding = new BasicHttpBinding();
using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
{
ServiceEndpoint se = sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.HttpGetUrl = new Uri("http://localhost:1100/mex");
sh.Description.Behaviors.Add(smb);
}
MyEndpointBehavior bhv = new MyEndpointBehavior();
se.EndpointBehaviors.Add(bhv);
sh.Open();
Console.WriteLine("service is ready");
Console.ReadKey();
sh.Close();
}
}
}
[ServiceContract(ConfigurationName = "isv")]
public interface IService
{
[OperationContract]
string Delete(int value);
[OperationContract]
void UpdateAll();
}
[ServiceBehavior(ConfigurationName = "sv")]
public class MyService : IService
{
public string Delete(int value)
{
if (value <= 0)
{
throw new ArgumentException("Parameter should be greater than 0");
}
return "Hello";
}
public void UpdateAll()
{
throw new InvalidOperationException("Operation exception");
}
}
public class MyCustomErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultException faultException = new FaultException(error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, error.Message);
}
}
public class MyEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
MyCustomErrorHandler myCustomErrorHandler = new MyCustomErrorHandler();
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(myCustomErrorHandler);
}
public void Validate(ServiceEndpoint endpoint)
{
return;
}
}
Client.
static void Main(string[] args)
{
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
try
{
client.Delete(-3);
}
catch (FaultException fault)
{
Console.WriteLine(fault.Reason.GetMatchingTranslation().Text);
}
}
Result.
Feel free to let me know if there is anything I can help with.
I am throwing WebFaultException when I extend Attribute, IOperationBehavior, IParameterInspector adn it does not work.
public class ApplicationNotSupportedAttribute : Attribute, IOperationBehavior, IParameterInspector
{
private readonly ApplicationNotSupportedBehaviour behaviour;
public ApplicationNotSupportedAttribute()
{
Logging<string>.Error("ApplicationNotSupportedAttribute");
behaviour = new ApplicationNotSupportedBehaviour();
}
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
behaviour.AddBindingParameters(operationDescription, bindingParameters);
}
public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation)
{
behaviour.ApplyClientBehavior(operationDescription, clientOperation);
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
behaviour.ApplyDispatchBehavior(operationDescription, dispatchOperation);
}
public void Validate(OperationDescription operationDescription)
{
behaviour.Validate(operationDescription);
}
public object BeforeCall(string operationName, object[] inputs)
{
throw new WebFaultException(HttpStatusCode.Forbidden);
}
public void AfterCall(string operationName, object[] outputs,
object returnValue, object correlationState)
{
}
Then I have the in the service:
[WebInvoke(Method = "POST", UriTemplate = "myMethod", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
[ApplicationNotSupportedAttribute]
myMethodResult myMethod(MyMethodRequest myMethodRequest);
After I run the code from above I get 200 Ok. If I add
throw new WebFaultException(HttpStatusCode.Forbidden);
in myMethod I get 403 Forbidden. As in the bellow code:
public myMethodResult myMethod(MyMethodRequest myMethodRequest)
{
throw new WebFaultException<myObject>(myObj, HttpStatusCode.Forbidden);
}
It is somehow possible to get the 403 before we actually get inside the myMethod?
I've found the issue in class ApplicationNotSupportedAttribute all the overwritten methods could have been empty except BeforeCall and ApplyDispatchBehavior which should look like:
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(this);
}
public object BeforeCall(string operationName, object[] inputs)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Forbidden;
throw new WebFaultException<string>("Unauthorized", HttpStatusCode.Forbidden);
}
Seems I've missed the dispatchOperation.ParameterInspectors.Add(this). This solved my problem.
Hi all i have a problem while adding a custom behavior to a WorkflowServiceHost.
Here is my WorflowServiceHostFactory:
public class ScoringWorkflowServiceHostFactory : WorkflowServiceHostFactory, IServiceHost<IKernel>
{
private static IKernel _InjectionInstance;
public IKernel InjectionInstance
{
get { return _InjectionInstance ?? (_InjectionInstance = new StandardKernel(new ScoringWorkflowServicesNinjectModule(Scope))); }
}
public object Scope
{
get { return Guid.NewGuid(); }
}
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
String fullFilePath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, constructorString);
WorkflowService wf = CSharpExpressionCompiler.Compile(fullFilePath);
System.ServiceModel.Activities.WorkflowServiceHost host = base.CreateWorkflowServiceHost(wf, baseAddresses);
NinjectBehaviorAttributeWF behavior = new NinjectBehaviorAttributeWF(wf);
host.Description.Behaviors.Add(behavior);
host.AddNinjectResolverExtension(InjectionInstance, Scope);
TypeAdapterFactory.SetCurrent(new SvcMapperAdapterFactory());
LoggerFactory.SetCurrent(new EntLibLoggerFactory());
return host;
}
}
Here is my behavior:
public class NinjectBehaviorAttributeWF : Attribute, IServiceBehavior
{
private System.ServiceModel.Activities.WorkflowService host;
public NinjectBehaviorAttributeWF(System.ServiceModel.Activities.WorkflowService host)
{
this.host = host;
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in dispatcher.Endpoints)
{
DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
dispatchRuntime.InstanceContextProvider = new PerCallInstanceContextProvider(dispatchRuntime);
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
In this way, i have an error while loading my service(xamlx): The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.
I don't know neither it's possible, nor how can create the default constructor for a workflowservice, (because the real implementation is the xamlx and not a simple class)
So, I've tried with a custom Provider:
dispatchRuntime.InstanceProvider = new CustomInstanceProvider(host.Body);
where CustomInstanceProvider is:
public class CustomInstanceProvider : IInstanceProvider
{
string message;
private System.Activities.Activity activity;
public CustomInstanceProvider(string msg)
{
Console.WriteLine("The non-default constructor has been called.");
this.message = msg;
}
public CustomInstanceProvider(System.Activities.Activity activity)
{
this.activity = activity;
}
public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
{
Console.WriteLine("GetInstance is called:");
return this.activity;
}
public object GetInstance(InstanceContext instanceContext)
{
Console.WriteLine("GetInstance is called:");
return this.activity;
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
Console.WriteLine("ReleaseInstance is called");
}
}
But i have this error:
System.InvalidCastException: Unable to cast object of type 'System.ServiceModel.Activities.WorkflowService' to type 'IHttpGetMetadata'.
How can I resolve my problem? Thanks a lot
I am basically implementing the IErrorHandler interface to catch all kinds of exceptions from the WCF service and sending it to the client by implementing the ProvideFault method.
I am facing one critical issue however. All of the exceptions are sent to the client as a FaultException but this disables the client to handle specific exceptions that he may have defined in the service.
Consider: SomeException that has been defined and thrown in one of the OperationContract implementations. When the exception is thrown, its converted to a fault using the following code:
var faultException = new FaultException(error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
This does send the error as a string, but the client has to catch a general exception like:
try{...}
catch(Exception e){...}
and not:
try{...}
catch(SomeException e){...}
Not only custom exceptions like SomeException, but system exceptions like InvalidOperationException cannot be caught using the above process.
Any ideas as to how to implement this behavior?
In WCF desirable to use special exceptions described as contracts, because your client may not be .NET application which has information about standard .NET exceptions.
For do this you can define the FaultContract in the your service and then use the FaultException class.
SERVER SIDE
[ServiceContract]
public interface ISampleService
{
[OperationContract]
[FaultContractAttribute(typeof(MyFaultMessage))]
string SampleMethod(string msg);
}
[DataContract]
public class MyFaultMessage
{
public MyFaultMessage(string message)
{
Message = message;
}
[DataMember]
public string Message { get; set; }
}
class SampleService : ISampleService
{
public string SampleMethod(string msg)
{
throw new FaultException<MyFaultMessage>(new MyFaultMessage("An error occurred."));
}
}
In addition, you can specify in the configuration file that the server returns exception details in it's FaultExceptions, but this is not recommended in a production application:
<serviceBehaviors>
<behavior>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
After that, you can rewrite your method for handling exceptions:
var faultException = error as FaultException;
if (faultException == null)
{
//If includeExceptionDetailInFaults = true, the fault exception with details will created by WCF.
return;
}
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
CLIENT:
try
{
_client.SampleMethod();
}
catch (FaultException<MyFaultMessage> e)
{
//Handle
}
catch (FaultException<ExceptionDetail> exception)
{
//Getting original exception detail if includeExceptionDetailInFaults = true
ExceptionDetail exceptionDetail = exception.Detail;
}
This article might help:
http://www.olegsych.com/2008/07/simplifying-wcf-using-exceptions-as-faults/
An approach I've used with some success when I didn't want to enumerate the exceptions that might be thrown is to create a PassthroughExceptionHandlingBehavior class that implements an IErrorHandler behavior for the server-side and IClientMessageInspector for the client side. The IErrorHandler behavior serializes the exception into the fault message. The IClientMessageInspector deserializes and throws the exception.
You have to attach this behavior to both the WCF client and the WCF server. You can attach the behaviors using the config file or by applying the [PassthroughExceptionHandlingBehavior] attribute to your contract.
Here's the behavior class:
public class PassthroughExceptionHandlingBehavior : Attribute, IClientMessageInspector, IErrorHandler,
IEndpointBehavior, IServiceBehavior, IContractBehavior
{
#region IClientMessageInspector Members
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (reply.IsFault)
{
// Create a copy of the original reply to allow default processing of the message
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
Message copy = buffer.CreateMessage(); // Create a copy to work with
reply = buffer.CreateMessage(); // Restore the original message
var exception = ReadExceptionFromFaultDetail(copy) as Exception;
if (exception != null)
{
throw exception;
}
}
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
return null;
}
private static object ReadExceptionFromFaultDetail(Message reply)
{
const string detailElementName = "detail";
using (XmlDictionaryReader reader = reply.GetReaderAtBodyContents())
{
// Find <soap:Detail>
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
detailElementName.Equals(reader.LocalName, StringComparison.InvariantCultureIgnoreCase))
{
return ReadExceptionFromDetailNode(reader);
}
}
// Couldn't find it!
return null;
}
}
private static object ReadExceptionFromDetailNode(XmlDictionaryReader reader)
{
// Move to the contents of <soap:Detail>
if (!reader.Read())
{
return null;
}
// Return the deserialized fault
try
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
return serializer.ReadObject(reader);
}
catch (SerializationException)
{
return null;
}
}
#endregion
#region IErrorHandler Members
public bool HandleError(Exception error)
{
return false;
}
public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{
if (error is FaultException)
{
// Let WCF do normal processing
}
else
{
// Generate fault message manually including the exception as the fault detail
MessageFault messageFault = MessageFault.CreateFault(
new FaultCode("Sender"),
new FaultReason(error.Message),
error,
new NetDataContractSerializer());
fault = Message.CreateMessage(version, messageFault, null);
}
}
#endregion
#region IContractBehavior Members
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ApplyClientBehavior(clientRuntime);
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
ApplyDispatchBehavior(dispatchRuntime.ChannelDispatcher);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
ApplyClientBehavior(clientRuntime);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ApplyDispatchBehavior(endpointDispatcher.ChannelDispatcher);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
#region IServiceBehavior Members
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
ApplyDispatchBehavior(dispatcher);
}
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
#endregion
#region Behavior helpers
private static void ApplyClientBehavior(System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
foreach (IClientMessageInspector messageInspector in clientRuntime.MessageInspectors)
{
if (messageInspector is PassthroughExceptionHandlingBehavior)
{
return;
}
}
clientRuntime.MessageInspectors.Add(new PassthroughExceptionHandlingBehavior());
}
private static void ApplyDispatchBehavior(System.ServiceModel.Dispatcher.ChannelDispatcher dispatcher)
{
// Don't add an error handler if it already exists
foreach (IErrorHandler errorHandler in dispatcher.ErrorHandlers)
{
if (errorHandler is PassthroughExceptionHandlingBehavior)
{
return;
}
}
dispatcher.ErrorHandlers.Add(new PassthroughExceptionHandlingBehavior());
}
#endregion
}
#region PassthroughExceptionHandlingElement class
public class PassthroughExceptionExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(PassthroughExceptionHandlingBehavior); }
}
protected override object CreateBehavior()
{
System.Diagnostics.Debugger.Launch();
return new PassthroughExceptionHandlingBehavior();
}
}
#endregion
FaultException has a Code property which you can use fore exception handling.
try
{
...
}
catch (FaultException ex)
{
switch(ex.Code)
{
case 0:
break;
...
}
}
Is there way inspect wcf method call. I means anything equal to HttpModule in asp.net.
I want execute a method before any method call.
WCF has a very extensible architecture. It is possible to intercept and customize a lot of WCF functionality to do your own thing.
For your case you will have to write appropriate Service or Endpoint Behavior. The process between receiving a message from the transport channels and invoking your service method is called as dispatching. WCF uses a set of Behaviors to do the dispatching. You can provide your own Behavior to inspect the method calls.
This article provides an excellent overview and examples - Extending WCF with Custom Behaviors.
I would also suggest that you go through this WCF architecture overview http://msdn.microsoft.com/en-us/library/aa480210.aspx
It depends what you want to check or modify in message at what level. If you'd like to modify something at Encoding level, you can opt for Custom Message Encoder, if it's before request get deserialize or before sending reply to client, use Custom Message Inspector.
HTH.
The IOperationInvoker might have been what you were looking for:
public class MyOperationInvoker : IOperationInvoker
{
IOperationInvoker originalInvoker;
public MyOperationInvoker(IOperationInvoker originalInvoker)
{
this.originalInvoker = originalInvoker;
}
public bool IsSynchronous { get { return originalInvoker.IsSynchronous; } }
public object[] AllocateInputs() { return originalInvoker.AllocateInputs(); }
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
//Do stuff before call
var res = this.originalInvoker.Invoke(instance, inputs, out outputs);
//stuff after call
return res;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs,
AsyncCallback callback, object state)
{
//Do stuff before async call
var res = this.originalInvoker.InvokeBegin(instance, inputs, callback, state);
return res;
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
var res = this.InvokeEnd(instance, out outputs, result);
//Do stuff after async call
return res;
}
}
Implemented as an attribute for all operations in the service:
public class MyBehaviorAttribute : Attribute, IServiceBehavior, IOperationBehavior
{
//IOperationBehavior
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new MyOperationInvoker(dispatchOperation.Invoker);
}
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters) { /*Do nothing*/ }
public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation) { /*Do nothing*/ }
public void Validate(OperationDescription operationDescription) { /*Do nothing*/ }
//IServiceBehavior
public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase) { /*Do nothing*/ }
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters) { /*Do nothing*/ }
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceHostBase.Description.Endpoints)
{
foreach (var operation in endpoint.Contract.Operations)
{
operation.Behaviors.Add(this);
}
}
}
And the service:
[MyBehavior]
public class HelloService : IHelloService
{
...
}