Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Endpoint Behavior not being used in WCF Client with Service Reference

I have an issue that I am not quite sure how it started. I am fairly certain it was working fine before, but do not remember making any changes.

First, please do not focus too much on the setup unless it is directly impacting why it is not working. I am not looking for critiquing as much as I am what is causing it to not work.

I am exposing an API that uses HTTP header authentication. I am consuming operations from this API in my solution. In order to avoid boilerplate code, I created a ClientFactory which I want to initialize the services, using a CustomClientMessageInspector and a CustomCredentialBehavior which is responsible for adding the headers to the message.

The idea is that when I need to use a service, the code would look similar to this:

IClientCredentials credentials = ClientCredentials.FromToken();
var service = ClientFactory.CreateClientInstance<API.Clients.ClientServiceClient>(credentials);

The ClientFactory looks like the following(Please don't judge this too much).

public class ClientFactory
{
    public static T CreateClientInstance<T>(IClientCredentials clientCredentials)
    {

        T t = Activator.CreateInstance<T>();
        var factory = t.GetType().GetProperty("ChannelFactory");
        using (var scope = new OperationContextScope((IContextChannel) t.GetType().GetProperty("InnerChannel").GetValue(t,null)))
        {

            var endpointBehavior = new CustomCredentialBehavior(clientCredentials);
            if (((ChannelFactory) factory.GetValue((t),null)).Endpoint.Behaviors.Any(p => p.GetType() == typeof(CustomCredentialBehavior)))
            {
                var behavior =
                    ((ChannelFactory) factory.GetValue((t),null)).Endpoint.Behaviors.FirstOrDefault(
                        p => p.GetType() == typeof (CustomCredentialBehavior));
                ((ChannelFactory) factory.GetValue((t),null)).Endpoint.Behaviors.Remove(behavior);
            }

            ((ChannelFactory)factory.GetValue((t),null)).Endpoint.Behaviors.Add(endpointBehavior);

            return t;
        }
    }
}

The CustomEndpointBehavior:

public class CustomCredentialBehavior:IEndpointBehavior
{
    private IClientCredentials _clientCredentials { get; set; }

    public CustomCredentialBehavior(IClientCredentials clientCredentials)
    {
        _clientCredentials = clientCredentials;
    }



    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.MessageInspectors.Add(new ClientCredentialMessageInspector(_clientCredentials));
    }
}

The ClientMessageInspector:

public class ClientCredentialMessageInspector:IClientMessageInspector
{
    private IClientCredentials _clientCredentials;

    public ClientCredentialMessageInspector(IClientCredentials clientCredentials)
    {
        _clientCredentials = clientCredentials;
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
            var buffer = request.CreateBufferedCopy(Int32.MaxValue);
            request = buffer.CreateMessage();

            HttpRequestMessageProperty messageProperty =
                (HttpRequestMessageProperty) request.Properties["httpRequest"];
            messageProperty.Headers.Add("AccessKey", _clientCredentials.AccessKey.ToString());
            messageProperty.Headers.Add("ClientKey", _clientCredentials.ClientKey.ToString());
            messageProperty.Headers.Add("AuthorizationKey", _clientCredentials.AuthorizationKey);
        return null;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {

    }
}

The problem I am facing is that even though I can see the service returned from the ClientFactory with the correct endpoint behavior, the ApplyClientBehavior() is never invoked. For some reason, it doesn't seem to be utilizign the WCF pipeline the way it originally was.

How do I get the ClientFactory to create an instance so that the EndpointBehavior I add is used?

EDIT Just to show what I am looking at, here is a screenshot of the watch window right before the service operation is invoked. It shows my CustomCredentialBehavior initialized with all the correct values.

enter image description here

EDIT I took Carlos' advice (in the comments) and simplified the call, and used the following call instead which works. I am happy that it works, however, I would like to figure out where the problem is in my original code so I do not have to call the boilerplate code every time I need to use a service reference.

The following code works:

        API.Clients.Client client = new API.Clients.Client();
        client.Active = true;
        client.Enabled = true;
        client.Name = model.ClientName;

        API.Clients.ClientServiceClient service = new ClientServiceClient();
        service.ChannelFactory.Endpoint.Behaviors.Add(new CustomCredentialBehavior(ClientCredentials.FromToken()));
        var response = service.CreateClient(new CreateClientRequest() { Client = client });

Any idea why the initial code does not work, and this one does?

like image 723
TheJediCowboy Avatar asked Oct 04 '22 17:10

TheJediCowboy


1 Answers

I might be missing something here, but I don't really understand why you would go with such a complex route.

If you have the interface (not the Client class) contract, just do the following:

var factory = new ChannelFactory<T>(); //T is the interface of the service

factory.Endpoint.Behaviors.Add() will provide you with the way to add your behaviors. And then to create the actual service you just call factory.CreateChannel() which returns a T.

So in summary:

public T Create<T>()
{
    var factory = new ChannelFactory<T>();
    factory.Endpoint.Behaviors.Add(<here goes your behavior>);
    return factory.CreateChannel();
}

Also remember to dispose of the client channel accordingly. If you're using IoC the container probably can handle that for you (Autofac can, for example).

like image 175
Pablo Romeo Avatar answered Oct 13 '22 10:10

Pablo Romeo