kipusoep
kipusoep

Reputation: 2232

Changing the Endpoint.Binding of a WCF System.ServiceModel.ClientBase doesn't work

I'm working with a programmatically configurated WCF Client (System.ServiceModel.ClientBase). This WCF Client is configured using a CustomBinding, which has a TextMessageEncodingBindingElement by default.

Now when I try to switch to Mtom encoding, I change the Client's Endpoint.Binding property, which works fine. The Endpoint.Binding property show's it has changed.

Unfortunately when I execute one of the methods the WCF service exposes, it still uses TextMessageEncoding and I can't figure out why.

I've got it working though, by constructing a new ClientBase and passing the new EndPointBinding in the constructor:

socialProxy = new SocialProxyClient(SocialProxyClientSettings.SocialProxyMTomEndPointBinding, new EndpointAddress(SocialProxyClientSettings.SocialProxyEndPointAddress));

But when I try this it doesn't work:

socialProxy.Endpoint.Binding = SocialProxyClientSettings.SocialProxyMTomEndPointBinding;

These are my definitions for the EndPointBindings:

public static TextMessageEncodingBindingElement TextMessageEncodingBindingElement
{
    get
    {
        if (_textMessageEncodingBindingElement == null)
        {
            _textMessageEncodingBindingElement = new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.Soap11 };
            _textMessageEncodingBindingElement.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
            {
                MaxDepth = 32,
                MaxStringContentLength = 5242880,
                MaxArrayLength = 204800000,
                MaxBytesPerRead = 5242880,
                MaxNameTableCharCount = 5242880
            };
        }
        return _textMessageEncodingBindingElement;
    }
}

public static MtomMessageEncodingBindingElement MtomMessageEncodingBindingElement
{
    get
    {
        if (_mtomMessageEncodingBindingElement == null)
        {
            _mtomMessageEncodingBindingElement = new MtomMessageEncodingBindingElement();
            _mtomMessageEncodingBindingElement.MaxReadPoolSize = TextMessageEncodingBindingElement.MaxReadPoolSize;
            _mtomMessageEncodingBindingElement.MaxWritePoolSize = TextMessageEncodingBindingElement.MaxWritePoolSize;
            _mtomMessageEncodingBindingElement.MessageVersion = TextMessageEncodingBindingElement.MessageVersion;
            _mtomMessageEncodingBindingElement.ReaderQuotas.MaxDepth = TextMessageEncodingBindingElement.ReaderQuotas.MaxDepth;
            _mtomMessageEncodingBindingElement.ReaderQuotas.MaxStringContentLength = TextMessageEncodingBindingElement.ReaderQuotas.MaxStringContentLength;
            _mtomMessageEncodingBindingElement.ReaderQuotas.MaxArrayLength = TextMessageEncodingBindingElement.ReaderQuotas.MaxArrayLength;
            _mtomMessageEncodingBindingElement.ReaderQuotas.MaxBytesPerRead = TextMessageEncodingBindingElement.ReaderQuotas.MaxBytesPerRead;
            _mtomMessageEncodingBindingElement.ReaderQuotas.MaxNameTableCharCount = TextMessageEncodingBindingElement.ReaderQuotas.MaxNameTableCharCount;
        }
        return _mtomMessageEncodingBindingElement;
    }
}

Can someone explain why changing the Endpoint.Binding programmatically doesn't work?

Upvotes: 2

Views: 4664

Answers (1)

Jesse Chisholm
Jesse Chisholm

Reputation: 4026

I believe that during construction of the ClientBase, the original Binding is used to create some helper objects. Changing the binding later does not change those helper objects.

To make any adjustments after construction, you likely need a custom Binding Behavior that you can tweak the internals of the Binding as you need. Use that in the construction so all helper objects are prepared for your later changes. As usual, all you want is one simple behavior change, but you will need to also write the ancillary helper classes to support your one behavior change.

See the SO thread: ONVIF Authentication in .NET 4.0 with Visual Studio 2010 For a discussion of CustomBinding issues.

See the blog post: Supporting the WS-I Basic Profile Password Digest in a WCF Client Proxy For an example of a custom Behavior that lets you change the Username Token on the fly.

Perhaps something similar can be done to let you control the local endpoint binding on the fly.

UPDATE: More reading here in StackOverflow, and pages it links to and I believe i have found the answer you are looking for.

For PasswordDigestBehavior: see: ONVIF Authentication in .NET 4.0 with Visual Studios 2010 and: http://benpowell.org/supporting-the-ws-i-basic-profile-password-digest-in-a-wcf-client-proxy/

For local NIC binding: see: Specify the outgoing IP address to use with WCF client

// ASSUMPTIONS:
//  1: DeviceClient is generated by svcutil from your WSDL.
//  1.1: DeviceClient is derived from
//           System.ServiceModel.ClientBase<Your.Wsdl.Device>
//  2: serviceAddress is the Uri provided for your service.
//
private static DeviceClient CreateDeviceClient(IPAddress nicAddress,
                                               Uri serviceAddress,
                                               String username,
                                               String password)
{
    if (null == serviceAddress)
        throw new ArgumentNullException("serviceAddress");

    //////////////////////////////////////////////////////////////////////////////
    // I didn't know how to put a variable set of credentials into a static
    //  app.config file.
    // But I found this article that talks about how to set up the right kind
    //  of binding on the fly.
    // I also found the implementation of PasswordDigestBehavior to get it all to work.
    //
    // from: https://stackoverflow.com/questions/5638247/onvif-authentication-in-net-4-0-with-visual-studios-2010
    // see: http://benpowell.org/supporting-the-ws-i-basic-profile-password-digest-in-a-wcf-client-proxy/
    //
    EndpointAddress serviceEndpointAddress = new EndpointAddress(serviceAddress);
    HttpTransportBindingElement httpBinding = new HttpTransportBindingElement();
    if (!String.IsNullOrEmpty(username))
    {
        httpBinding.AuthenticationScheme = AuthenticationSchemes.Digest;
    }
    else
    {
        httpBinding.AuthenticationScheme = AuthenticationSchemes.Anonymous;
    }
    var messageElement = new TextMessageEncodingBindingElement();
    messageElement.MessageVersion =
       MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None);

    CustomBinding bind = new CustomBinding(messageElement, httpBinding);

    ////////////////////////////////////////////////////////////////////////////////
    // from: https://stackoverflow.com/questions/3249846/specify-the-outgoing-ip-address-to-use-with-wcf-client
    //  Adjust the serviceEndpointAddress to bind to the local NIC, if at all possible.
    //
    ServicePoint sPoint = ServicePointManager.FindServicePoint(serviceAddress);
    sPoint.BindIPEndPointDelegate = delegate(
            System.Net.ServicePoint servicePoint,
            System.Net.IPEndPoint remoteEndPoint,
            int retryCount)
    {
        // if we know our NIC local address, use it
        //
        if ((null != nicAddress)
            && (nicAddress.AddressFamily == remoteEndPoint.AddressFamily))
        {
            return new System.Net.IPEndPoint(nicAddress, 0);
        }
        else if (System.Net.Sockets.AddressFamily.InterNetworkV6 == remoteEndPoint.AddressFamily)
        {
            return new System.Net.IPEndPoint(System.Net.IPAddress.IPv6Any, 0);
        }
        else // if (System.Net.Sockets.AddressFamily.InterNetwork == remoteEndPoint.AddressFamily)
        {
            return new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
        }
    };
    /////////////////////////////////////////////////////////////////////////////

    DeviceClient client = new DeviceClient(bind, serviceEndpointAddress);

    // Add our custom behavior
    //  - this requires the Microsoft WSE 3.0 SDK file: Microsoft.Web.Services3.dll
    //
    PasswordDigestBehavior behavior = new PasswordDigestBehavior(username, password);
    client.Endpoint.Behaviors.Add(behavior);

    return client;
}

Upvotes: 3

Related Questions