c# func parameter not being used - c#

I have a legacy app that logs the input / output of services. Currently, every method has the same lines to log the request and response objects. I would like to use AOP, but without adding any extra tool (Postsharp, Castle, etc), or wrap every service class into another class (ServiceWrapper).
In order to do that, I'm trying to create a Generic class that knows that it should log the request and response objects. Here's what I'm trying:
using System;
namespace ProxyTest
{
class Program
{
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(request), "abc");
Console.Read();
}
}
class ServiceProxy
{
public static void Invoke(Func<object, object> service, object request)
{
Console.WriteLine("input:" + request);
var response = service(request);
Console.WriteLine("output:" + response);
}
}
class FooService
{
public string DoFoo(object a)
{
return a + ": returning: Do Foo";
}
}
}
Although it's working, the "abc" string is just to compile the application, but it's not being used as the request parameter. If I remove that, the code does not compile. Am I missing something?
UPDATE
Changing to the following did the trick:
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
Console.Read();
}

You should call it like this:
class Program
{
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(fooService.DoFoo, "abc"); // lose the DoFoo parameter.
Console.Read();
}
}
You should pass the DoFoo as Func, instead of calling it. Also you should change the method signature to:
class FooService
{
public object DoFoo(object a)
{
return a + ": returning: Do Foo";
}
}

For this task you can just add logging behavior on dispatcher.
First, you create ServiceBehavior with such content:
public class ServiceLoggingBehavior : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
IOperationBehavior behavior = new LoggingOperationBehavior();
operation.Behaviors.Add(behavior);
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Then you need to create operation behavior:
internal class LoggingOperationBehavior : IOperationBehavior
{
public void Validate(OperationDescription operationDescription)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new LoggingOperationInvoker(dispatchOperation.Invoker, dispatchOperation);
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
}
And finally create invoker for all methods on server side:
internal class LoggingOperationInvoker : IOperationInvoker
{
private readonly IOperationInvoker _baseInvoker;
private readonly string _operationName;
public LoggingOperationInvoker(IOperationInvoker baseInvoker, DispatchOperation operation)
{
_baseInvoker = baseInvoker;
_operationName = operation.Name;
}
public bool IsSynchronous
{
get { return _baseInvoker.IsSynchronous; }
}
public object[] AllocateInputs()
{
return _baseInvoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
var sw = new Stopwatch();
try
{
LogBegin();
sw.Start();
var response = _baseInvoker.Invoke(instance, inputs, out outputs);
return response;
}
finally
{
sw.Stop();
LogEnd(sw.Elapsed);
}
}
private void LogBegin()
{
//you can log begin here.
}
private void LogEnd(TimeSpan elapsed)
{
//you can log end here.
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _baseInvoker.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _baseInvoker.InvokeEnd(instance, out outputs, result);
}
}
If you want to log request, you can just serialize and log inputs variable in Invoke method. For response - just serialize and log response variable.
And finaly, most enjoyable part, just attach it like attribute:
[ServiceLoggingBehavior]
public MyService : IMyServiceContract
{
...
}

Your Invoke-method clearly asks for a Func- and an object-parameter, so you have to provide both. No idea what exactly you´re expecting when you omit one of the params. I assume you want to make the Func to return the response created by a specific request-object. Furthermore it might be good idea to make your request- and response-arguments generic:
class Program
{
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
Console.Read();
}
}
class ServiceProxy
{
public static void Invoke<TRequest, TResponse>(Func<TRequest, TResponse> service, TRequest request)
{
Console.WriteLine("input:" + request.ToString());
var response = service(request);
Console.WriteLine("output:" + response.ToString());
}
}
The Invoke-call can further be simplyfied to ServiceProxy.Invoke(fooService.DoFoo, request);

Thanks for all the responses. I was able to achieve what I was looking for using:
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(fooService.DoFoo, request);
Console.Read();
}
or
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
Console.Read();
}

Related

Hadler Error EndPoint in my host - WCF - Behaviour

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.

Add behaviorattribute to a WorkflowServiceHost

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

global object that needs to be referenced in a message handler

I have a signalr client that I want to be global.
I think creating the signalr client in the Init() of the endpointconfig would be best.
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
{
public static HubConnection hubConnection;
public static IHubProxy hubProxy;
public void Init()
{
Configure.With()
.DefiningEventsAs(t => t.Namespace != null && t.Namespace.Contains(".Events."))
.DefiningMessagesAs(t => t.Namespace != null && t.Namespace.Contains(".Messages."))
.StructureMapBuilder(new Container(new DependencyRegistry()));
Configure.Serialization.Json();
hubConnection = new HubConnection("http://localhost:58120");
hubProxy = hubConnection.CreateHubProxy("AmsHub");
hubProxy.On<string>("receiveServerPush", x => System.Diagnostics.Debug.WriteLine(x));
hubConnection.Start().Wait();
}
public class DependencyRegistry : Registry
{
public DependencyRegistry()
{
Scan(x =>
{
x.AssembliesFromApplicationBaseDirectory();
x.ExcludeNamespace("StructureMap");
x.WithDefaultConventions();
});
}
}
}
What I'm confused about, is how am I supposed to reference the hubConnection and hubProxy in a message handler? I seems like I'm jerry rigging NServicebus.
public class TestHandler : IHandleMessages<AMS.Infrastructure.Events.IEvent>
{
public void Handle(AMS.Infrastructure.Events.IEvent message)
{
EndpointConfig.hubProxy.Invoke("ServerFunction", "yodle");
}
}
PS: the reason I need the connection and proxy to be global is because spawning up a new hubConnection is expensive according to the signalr people. They highly discourage creating and destroying hubconnections over and over again. They found that making the hubconnection global/static(?) ok though.
In this case, your Hub Connection/Proxy really are unrelated to the EndPointConfiguration class. They don't use nor require any data from this type in order to function.
I would recommend placing them in their own lazy initialized singleton, and start them automatically upon first access. This would look like:
public class Hub
{
private static Lazy<Hub> instance = new Lazy<Hub>(() => new Hub());
public static Hub Instance { get { return instance.Value; } }
private Hub()
{
this.Connection = new HubConnection("http://localhost:58120");
this.Proxy = Connection.CreateHubProxy("AmsHub");
this.Proxy.On<string>("receiveServerPush", x => System.Diagnostics.Debug.WriteLine(x));
this.Connection.Start().Wait();
}
public HubConnection Connection { get; private set; }
public IHubProxy Proxy { get; private set; }
}
Your consumers then just use:
public class TestHandler : IHandleMessages<AMS.Infrastructure.Events.IEvent>
{
public void Handle(AMS.Infrastructure.Events.IEvent message)
{
Hub.Instance.Proxy.Invoke("ServerFunction", "yodle");
}
}
This has the benefit of not creating and starting until first use, and isolates this type into it's own class.
Given that you're also handling the subscription internally, you also could, optionally, encapsulate your methods to simplify usage:
public class Hub
{
private static Lazy<Hub> instance = new Lazy<Hub>(() => new Hub());
public static Hub Instance { get { return instance.Value; } }
private Hub()
{
this.Connection = new HubConnection("http://localhost:58120");
this.Proxy = Connection.CreateHubProxy("AmsHub");
this.Proxy.On<string>("receiveServerPush", x => System.Diagnostics.Debug.WriteLine(x));
this.Connection.Start().Wait();
}
private HubConnection Connection { get; set; }
private IHubProxy Proxy { get; set; }
public static Task Invoke(string method, params Object[] args)
{
return Instance.Proxy.Invoke(method, args);
}
public static Task<T> Invoke<T>(string method, params Object[] args)
{
return Instance.Proxy.Invoke<T>(method, args);
}
}
With the above, you could just use: Hub.Invoke("ServerFunction", "yodle");
#reed-copsey Old post, but thanks for your reply, it helped me a lot.
In my case I am creating an Azure Function, which will connect to an SignalR Hub which is part of an ASP.NET MVC site. I needed the connection to be secure / authenticated before sending a notification.
So my example included authenticating and getting a cookie.
public class Hub
{
private static readonly string HOMEPAGE = ConfigurationManager.AppSettings["Homepage"];
private static readonly string NOTIFICATION_USER = ConfigurationManager.AppSettings["NotificationUser"];
private static readonly string NOTIFICATION_PASSWORD = ConfigurationManager.AppSettings["NotificationPassword"];
private static Lazy<Hub> instance = new Lazy<Hub>(() => new Hub());
public static Hub Instance { get { return instance.Value; } }
private Hub()
{
ClientHandler = new HttpClientHandler();
ClientHandler.CookieContainer = new CookieContainer();
using (Client = new HttpClient(ClientHandler))
{
var content = string.Format("Email={0}&Password={1}", NOTIFICATION_USER, NOTIFICATION_PASSWORD);
var response = this.Client.PostAsync(HOMEPAGE + "/Account/Login", new StringContent(content, Encoding.UTF8, "application/x-www-form-urlencoded")).Result;
}
Connection = new HubConnection($"{HOMEPAGE}/");
Connection.CookieContainer = ClientHandler.CookieContainer;
Proxy = Connection.CreateHubProxy("notificationsHub");
//this.Proxy.On<string>("receiveServerPush", x => System.Diagnostics.Debug.WriteLine(x));
Connection.Start().Wait();
}
public HttpClientHandler ClientHandler { get; private set; }
public HttpClient Client { get; private set; }
public HubConnection Connection { get; private set; }
public IHubProxy Proxy { get; private set; }
public static Task Invoke(string method, params Object[] args)
{
return Instance.Proxy.Invoke(method, args);
}
public static Task<T> Invoke<T>(string method, params Object[] args)
{
return Instance.Proxy.Invoke<T>(method, args);
}
}

Need example on implementing MessageHeaders.WriteHeaderContents

I have a custom header stored in a "string" variable, I need to replace the header of a outgoing SOAP request from my WCF client with the header in the "string" type variable. Based on research I see that implementing the MessageHeaders.WriteHeaderContents can work but this method accepts only XmlDictionaryWriter or XmlWriter types as input. I have a string input. How do I code in C# ..
Message headers are a SOAP concept, and SOAP requests are XML documents, so you really need some XML-ness there. But for your scenario, you actually don't need to override MessageHeaders, you can use an inspector and simply replace the header at that point, as shown in the example below (the conversion to XML will be done by the MessageHeader class).
public class StackOverflow_7141998
{
[MessageContract]
public class MyMC
{
[MessageHeader(Name = "MyHeader", Namespace = "http://my.namespace.com")]
public string HeaderValue { get; set; }
[MessageBodyMember(Name = "MyBody", Namespace = "http://my.namespace.com")]
public string BodyValue { get; set; }
}
[ServiceContract]
public interface ITest
{
[OperationContract]
void Process(MyMC mc);
}
public class Service : ITest
{
public void Process(MyMC mc)
{
Console.WriteLine("Header value: {0}", mc.HeaderValue);
}
}
public class MyInspector : IEndpointBehavior, IClientMessageInspector
{
public string NewHeaderValue { get; set; }
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)
{
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
int originalIndex = request.Headers.FindHeader("MyHeader", "http://my.namespace.com");
if (originalIndex >= 0)
{
request.Headers.Insert(originalIndex, MessageHeader.CreateHeader("MyHeader", "http://my.namespace.com", this.NewHeaderValue));
request.Headers.RemoveAt(originalIndex + 1);
}
return null;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), new WSHttpBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new WSHttpBinding(), new EndpointAddress(baseAddress));
MyInspector inspector = new MyInspector { NewHeaderValue = "Modified header value" };
factory.Endpoint.Behaviors.Add(inspector);
ITest proxy = factory.CreateChannel();
proxy.Process(new MyMC { HeaderValue = "Original header value", BodyValue = "The body" });
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Hooking into wcf pipeline

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
{
...
}

Categories