Пользовательское поведение конечной точки не используется в клиенте WCF со ссылкой на службу

У меня проблема, я не совсем уверен, как она началась. Я совершенно уверен, что раньше он работал нормально, но не помню, чтобы вносил какие-либо изменения.

Во-первых, пожалуйста, не уделяйте слишком много внимания настройке, если только она напрямую не влияет на то, почему она не работает. Я не столько критикую, сколько то, что заставляет это не работать.

Я предоставляю API, который использует аутентификацию HTTP-заголовка. Я использую операции из этого API в своем решении. Чтобы избежать шаблонного кода, я создал ClientFactory, который я хочу инициализировать службы, используя CustomClientMessageInspector и CustomCredentialBehavior, который отвечает за добавление заголовков к сообщению.

Идея состоит в том, что когда мне нужно использовать сервис, код будет выглядеть примерно так:

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

ClientFactory выглядит следующим образом (пожалуйста, не судите слишком строго).

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

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

Инспектор сообщений клиента:

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)
    {

    }
}

Проблема, с которой я столкнулся, заключается в том, что, хотя я вижу, что служба возвращается из ClientFactory с правильным поведением конечной точки, ApplyClientBehavior() никогда не вызывается. По какой-то причине кажется, что он не использует конвейер WCF так, как он был изначально.

Как заставить ClientFactory создать экземпляр, чтобы использовался EndpointBehavior, который я добавляю?

РЕДАКТИРОВАТЬ Просто чтобы показать, на что я смотрю, вот скриншот окна просмотра прямо перед вызовом операции службы. Он показывает, что мой CustomCredentialBehavior инициализирован со всеми правильными значениями.

введите здесь описание изображения

EDIT Я последовал совету Карлоса (в комментариях) и упростил вызов, а вместо этого использовал следующий вызов, который работает. Я рад, что это работает, однако я хотел бы выяснить, где проблема в моем исходном коде, чтобы мне не приходилось вызывать шаблонный код каждый раз, когда мне нужно использовать ссылку на службу.

Работает следующий код:

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

Есть идеи, почему исходный код не работает, а этот работает?


person TheJediCowboy    schedule 14.03.2013    source источник
comment
В этом коде много косвенности... вы можете попытаться упростить его, используя простой метод (т. е. без фабрик, дженериков, отражений и т. д.), чтобы увидеть, когда он начнет работать; в этот момент начните возвращаться к абстракциям и посмотрите, где это сломается. Это укажет вам на проблему.   -  person carlosfigueira    schedule 14.03.2013
comment
Я определенно попробую это. Кроме того, может ли это быть связано с недавним переходом с .NET 4.5 на 4.0? Мне пришлось модифицировать пару реализаций расширяемости, которые я должен был заставить его скомпилировать. В основном с поведением конечной точки   -  person TheJediCowboy    schedule 14.03.2013
comment
@carlosfigueira, я последовал вашему совету и упростил его до более конкретной версии, показанной в редактировании выше, и это работает. Теперь я пытаюсь выяснить, что не так с первой версией. Ты видишь что-нибудь выше головы?   -  person TheJediCowboy    schedule 15.03.2013
comment
но я все еще сталкиваюсь с проблемами, не уверен, что это лучшее решение. Мне кажется, я слишком усложняю этот процесс.   -  person TheJediCowboy    schedule 15.03.2013
comment
Я пошел более простым альтернативным путем, я понял, что то, что я делаю, слишком сложно. Спасибо за вашу постоянную помощь!   -  person TheJediCowboy    schedule 15.03.2013


Ответы (1)


Я мог бы что-то упустить здесь, но я действительно не понимаю, почему вы выбрали такой сложный маршрут.

Если у вас есть контракт интерфейса (а не класса Client), просто сделайте следующее:

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

factory.Endpoint.Behaviors.Add() предоставит вам способ добавить свои модели поведения. А затем, чтобы создать реальную службу, вы просто вызываете factory.CreateChannel(), который возвращает T.

Итак, вкратце:

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

Также не забудьте соответствующим образом распорядиться клиентским каналом. Если вы используете IoC, контейнер, вероятно, справится с этим за вас (например, Autofac).

person Pablo Romeo    schedule 15.03.2013