Serializing (Binding, Address) Pairs in WCF

One of the great ways to procrastinate from finishing a presentation is to blog.

Another way to procrastinate is to find a better blog editor.  ;)  I'm having issues w/ code snippets in this UI.  But I'll save that procrastination venture for another time.

One of the core virtues (imho) of WCF is its strong emphasis on the decoupling between contracts, bindings, and addresses.

As I'm sure you know already, contracts define the “what“ of your service (from both the client and service perspective).  They are develop-time, compiled-in assumptions about your app.

Bindings represent the way that you interact with a particular contract.  This allows you to, say, expose the same application contract over multiple different bindings for the wide array of clients with whom you may have to interact.  Typically, your contract exposes some set of constraints on the binding (and you can optionally add more constraints with the [DeliveryRequirements] attribute).  Our goal in designing those constraints were to make them tight enough to ensure correctness while permissive enough to allow a strong degree of polymorphism amongst bindings.

Lastly, addresses represent the location of your service.

Typically, if you are writing a fairly robust client / service app, you want to minimize the couplings between the client and service.

In terms of endpoints, the pessimal set of couplings you could probably get would be by hard-coding the contract, binding, and address to your service in both your client and service application.  This means that if the service moves, you would have to re-compile both the client an the service to make everything work again.

A small improvement over that model would be to separate both the service's address and the client's view of that address into a run-time specifiable value.  Most apps either do this themselves or rely upon some built-in support in their app framework.  For instance, in WCF, if you want to change your service location, you can do that by updating both your client and service's config.

For WCF specifically, an improvement over that model would be to separate both the service's binding and address, as well as the client's view of that information, into a run-time specifiable value.  In fact, our config system is engineered to make this easy and our guidance is pretty prescriptive about this being a Good Thing(tm).  As such, if you wanted to change the binding and address where a WCF was located, you could do that by only updating the client and service config (no recompiles necessary).

In both cases, though, you still need to update both the client and the service for what is essentially a service change; this is a little non-ideal.  Ideally, you'd like the client to automatically adapt to the service changes.

One way that people typically do this is by having the address of the service be found through some sort of discovery mechanism (either directory-based or “peer-to-peer“-ish).  In those cases, the client asks for a service based on some quality (e.g., “give me a service that implements the contract foo at a binding bar“).  Via whatever discovery mechanism is defined, the client discovers an address for that service and then proceeds to interact with the service.  In this way, a service could update its address and the client would pick up that update “for free.“

In WCF, of course, you'd really want to go one step further.  Ideally, the only coupling between your client and your service is the contract (as contract-compatibility is a fundamental assumption that is compiled into your app).  The binding and address can be discovered at runtime.

We have some built-in support to the product for this scenario using the MetadataResolver and MetadataTransferClient scenarios.  The MetadataResolver class can be used to programmatically retrieve the metadata for a service and surface it as a set of ServiceEndpoints.  If you combine this with a discovery approach for metadata addresses, you can essentially achieve the aforementioned decoupling.

Another approach to solving this problem, however, is to make a (Binding, Address) pair itself serializable.  This means that you can pass it around itself.  For some samples that I've been writing, I've found the ability to do this quite useful.

Here's the code; warning, this is compiled against the very latest bits on my machine, so it may be slightly ahead of the latest CTP.

[DataContract]

public class SerializableEndpoint

{

    ServiceEndpoint endpoint;

    int priority;

 

    public SerializableEndpoint(ServiceEndpoint endpoint, int priority)

    {

        this.endpoint = SanitizeContractInEndpoint(endpoint);

        this.priority = priority;

    }

 

    public EndpointAddress Address

    {

        get { return this.endpoint.Address; }

    }

 

    public Binding Binding

    {

        get { return this.endpoint.Binding; }

    }

 

    public string ContractName

    {

        get { return this.endpoint.Contract.Name; }

    }

 

    public string ContractNamespace

    {

        get { return this.endpoint.Contract.Namespace; }

    }

 

    public string Name

    {

        get { return this.endpoint.Name; }

    }

 

    [DataMember(IsRequired = true)]

    public int Priority

    {

        get { return this.priority; }

        internal set { this.priority = value; }

    }

 

    [DataMember(IsRequired = true)]

    private MetadataSet Metadata

    {

        get

        {

            WsdlExporter exporter = new WsdlExporter();

            exporter.ExportEndpoint(this.endpoint);

            IEnumerable<MetadataSection> sections = exporter.GetGeneratedMetadata();

            return new MetadataSet(sections);

        }

        set

        {

           WsdlImporter importer = new WsdlImporter(value.MetadataSections);

           ServiceEndpointCollection endpoints = importer.ImportAllEndpoints();

           if (endpoints.Count != 1)

           {

               throw new ArgumentException("MetadataBundle must contain exactly one endpoint.");

           }

           this.endpoint = SanitizeContractInEndpoint(endpoints[0]);

        }

    }

 

    private ServiceEndpoint SanitizeContractInEndpoint(ServiceEndpoint endpoint)

    {

        ServiceEndpoint newEndpoint = new ServiceEndpoint(new ContractDescription(endpoint.Contract.Name, endpoint.Contract.Namespace));

        newEndpoint.Address = endpoint.Address;

        newEndpoint.Binding = endpoint.Binding;

        newEndpoint.Name = endpoint.Name;

        return newEndpoint;

    }

}

I include the contract's name and namespace because I hope that's sufficient to correlate the contract with your local one.  I've found it useful if I, say, get back a set of these (Binding, Address) pairs and want to see if any of them are usable with my contract.  I use priority as a way of weighting multiple matching (Binding, Address) pairs.

The serialization mechanism is a little non-ideal, but we are investigating better ways to make a binding itself serializable.

Interestingly, ieSpellCheck thinks that WsdlExporter is incorrectly spelled.  It thinks “sexpot” is a better spelling.  I find this amusing, especially  knowing that AlexDeJ named the class.

With that, back to the presentation.

(Nit: Another issue with the blog editor is the lack of “preview;” sorry to those folks that saw the first draft before I re-read it.)


Posted Mar 06 2006, 08:02 PM by mike-vernal
Filed under:

Comments

Thomas Schissler wrote re: Serializing (Binding, Address) Pairs in WCF
on 03-23-2007 10:14 AM
Great article!

I'm looking for a way to serialize a complete endpoint. I tried your code above, but found, thet with this onle the binding type (basicHTTP, NetTCP) is serialized. I didn't manage to serialize the parameters of the binding. For example if I want to set the AllowCookie Property of the basicHTTBinding to true, serialize the endpoint and deserialize it, this setting is lost and I have the default value which is false. Any suggestions how to solve this?
Magnus wrote re: Serializing (Binding, Address) Pairs in WCF
on 03-10-2008 5:57 AM
Hi.

I have a lot of clients who I don't want to update manually, so i'm writing code to automaticly genereate proxies contracts etc, so far so good. But is there any way to serialize the endpoints setting to disk, i.e. the ServiceEndpoint object, so I don't have to generate it each time? I can't see how your code will do that.

Thanx.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?