How do typed messages get created from an object that has a message contract?

There seem to be a lot of examples that talk about how messages get produced when they're described by data contracts but relatively few descriptions of the equivalent process for message contracts. There's really nothing complicated or magical here so let's go through it.

I'll start with a message that I want to describe with a message contract and then build the contract class.

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<h:version xmlns:h="http://mycorp.com">1</h:version>
</s:Header>
<s:Body>
<name xmlns="http://mycorp.com">Microsoft</name>
<address xmlns="http://mycorp.com">1 Microsoft Way</address>
</s:Body>
</s:Envelope>

There's a message header whose type is an integer and two body members whose types are strings. This is a pretty easy message to describe although I have to change the settings for the wrapper and namespaces to get the structure to match. I apply an order to the body members because the default would reverse them.

[MessageContract(IsWrapped=false)]
class MyMessage
{
[MessageHeader(Namespace = "http://mycorp.com")]
public int version { get; set; }

[MessageBodyMember(Namespace = "http://mycorp.com", Order = 1)]
public string name { get; set; }

[MessageBodyMember(Namespace = "http://mycorp.com", Order = 2)]
public string address { get; set; }
}

Now I can use the TypedMessageConverter class to go between the message contract and a Message object.

MyMessage record = new MyMessage();
record.version = 1;
record.name = "Microsoft";
record.address = "1 Microsoft Way";
TypedMessageConverter converter = TypedMessageConverter.Create(typeof(MyMessage), null);
Message message = converter.ToMessage(record, MessageVersion.Soap12);
Console.WriteLine(message.ToString());

message.Headers.Clear();
message.Headers.Add(MessageHeader.CreateHeader("version", "http://mycorp.com", 2));
MyMessage record2 = (MyMessage)converter.FromMessage(message);
Console.WriteLine(record2.version);
Console.WriteLine(record2.name);
Console.WriteLine(record2.address);

I changed the version header for the return trip just to prove that there's no cheating going on. I gave an explicit message version when creating the message because otherwise I would have gotten SOAP 1.2 with WS-Addressing 1.0. My sample message didn't have any addressing information so I wanted to avoid potentially introducing some. TypedMessageConverter uses a factory pattern and doesn't promise anything about the default message version so I would never rely on the default behavior even if it seemed to match what I wanted.

Next time: Differences in Enum Serialization