Add behaviorattribute to a WorkflowServiceHost - c#

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

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.

WCF Add Header Issue with AddressHeader.CreateAddressHeader() method

I'm trying to add a custom header to every outgoing request message via WCF.
I created the header object like that:
EndpointAddressBuilder eab = new EndpointAddressBuilder(combinedService.Endpoint.Address);
AddressHeader addressHeader = AddressHeader.CreateAddressHeader("HeaderData", String.Empty, "String data");
eab.Headers.Add(addressHeader);
combinedService.Endpoint.Address = eab.ToEndpointAddress();
I use this exact code in two positions in my code, one works well but the other does not.
The problem is in the following line of code:
AddressHeader addressHeader = AddressHeader.CreateAddressHeader("HeaderData", String.Empty, "String data");
When it works (pass the header data successfully) the created object looks like that:
But when it doesn't work, the created object looks like that:
The exact same method does this but two position code calls yield different results.
Is there any method I should execute on the addressHeader object to force it serialize the object? Maybe something like: Flush()?
I KNOW I can use several well known patterns of adding a custom headers like: "custom behavior", "Client Message Inspector" etc... But I have a requirement to add it on a specific point right before we send the message.
I've, finally, got a solution.
I just followed the steps in this and this excellent, detailed and simple articles.
Although this code might seems to be long and complecated, this is the most right manner for handling header data in WCF. So it'll finally worth it.
You just have to configure custom behavior for handling WCF header.
Here is how it goes:
Client Side:
public class FillHeaderDataBehaviourExtension : BehaviorExtensionElement, IEndpointBehavior
{
#region BehaviorExtensionElement Implementation
public override Type BehaviorType
{
get
{
return typeof(FillHeaderDataBehaviourExtension);
}
}
protected override object CreateBehavior()
{
return this;
}
#endregion
#region IServiceBehaviour Implementation
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new MessageInspector());
}
#endregion
}
public class MessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageHeader header = MessageHeader.CreateHeader("HeaderData", String.Empty, HeaderDataVM.GetInstance().GetBaseInstance());
request.Headers.Add(header); // There is no need for checking if exist before adding. Every request has it's own headers.
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
Server Side:
public class ExtractHeadersBehaviourExtension : BehaviorExtensionElement, IServiceBehavior
{
#region BehaviorExtensionElement Implementation
public override Type BehaviorType
{
get
{
return typeof(ExtractHeadersBehaviourExtension);
}
}
protected override object CreateBehavior()
{
return this;
}
#endregion
#region IServiceBehavior Implementation
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
{
ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
MessageInspector inspector = new MessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
#endregion
}
public class MessageInspector : IDispatchMessageInspector
{
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HeaderData headerData = request.Headers.GetHeader<HeaderData>("HeaderData", String.Empty);
if(headerData != null)
{
OperationContext.Current.IncomingMessageProperties.Add("HeaderData", headerData);
}
return null;
}
}
And finally, don't forget to configure it in the app.config files (client & server side) as follows:
<behaviors>
<endpointBehaviors>
<behavior name="NewBehavior">
<fillHeaderDataBehaviourExtension/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
You can also add these lines via the WCF config editor. To do so, look at this answer.
Important: Note that you'll get an error in the app config after adding these lines of configuration code:
Don't worry about this, your application will run correctly. It causes because the GAC (Global Assembly Cache) folder doesn't contain this behavior (since it is custom behavior). You can fix it by adding this behavior manually to your GAC folder on your computer.
However, this error might prevent you from updating service reference. If you try to, you'll get this error message:
So just comment out this line (<extractHeadersBehaviourExtension/>) (in client & server side) when you update your service reference.

How to handle exceptions thrown in IParameterInspector?

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());
}

Accessing ClientCredential properties from client message inspector

Can I reference the proxy client instance from a client message inspector?
The reason, I'd like to access the values of the following properties:
ClientCredentials.UserName.UserName
ClientCredentials.UserName.Password
Thanks
I managed to retrieve the credentials from within the inspector by passing a reference to "ClientCredentials" from my custom EndpointBehavior:
CustomBehaviour:
public class CustomEndpointBehaviour:IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
ClientCredentials credentials = endpoint.Behaviors.Find<ClientCredentials>();
clientRuntime.MessageInspectors.Add(new CustomMessageInspector(credentials));
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
And inspector:
public class CustomMessageInspector : IClientMessageInspector
{
ClientCredentials crendentials = null;
public CustomMessageInspector(ClientCredentials credentials)
{
this.crendentials = credentials;
}
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
string userName = "";
string passWord = "";
if (!(crendentials == null))
{
userName = crendentials.UserName.UserName;
passWord = crendentials.UserName.Password;
}
return null;
}
}

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