[Source: http://geekswithblogs.net/EltonStoneman]
As part of the testing for the Dynamic Looping Convoy solution, I wanted performance testing with LoadGen, which proved to need almost as much development as the BizTalk design.
The standard LoadGen message creation from templates, and even the Dynamic Message Creator wouldn't work for my case where I wanted to generate messages in related sets, all of the same type but each only populating part of the schema. So the message id would be the same for a set, but the segment number would dictate what else was populated.
I ended up writing a custom IMessageCreator implementation to generate the convoy messages, which is not complex, but doesn't seem to be too well-trodden a path. Here's the class with the interface methods highlighted:
- OptimizeFileSizeLimit lets you specify when streaming comes into play for large message; in my case the message were all small so this is just a get/set of a local field;
- CreateNewMessage is where the message generation happens. In the convoy case you need to be wary that LoadGen can operate in multiple threads, so I have static methods to return the current message id and segment number*. The method retrieves the next segment info, selectively populates a segment, and returns it to LoadGen. LoadGen expects a byte array, so the class is serialized to XML and the bytes are passed out:
public Message CreateNewMessage(Message OriginalMessage, string TemplateFilePath, string NewMessageUniqueFileSuffix)
{
//construct the segment:
MessageSegment messageSegment = GetNextSegment();
IncomingSegment segment = new IncomingSegment();
segment.HireId = messageSegment.MessageId;
segment.SegmentNumber = messageSegment.SementNumber;
//add data:
switch (segment.SegmentNumber)
{
case 1:
segment.CustomerId = "CUST_X";
break;
//etc...
}
//retrieve XML as byte array:
byte[] buffer = this.SerializeSegment(segment);
//populate outgoing message:
Message outgoing = new Message();
outgoing.MessageData = buffer;
outgoing.MessageLength = buffer.Length;
outgoing.MessagePath = OriginalMessage.MessagePath;
outgoing.MessageExtension = OriginalMessage.MessageExtension;
return outgoing;
}
* - there are issues with this in any case, as you may have the situation where the thread count is not a multiple of the expected segment count so you'll get incomplete sets generated. Useful if you want to check error handling in your BizTalk solution, but you can stick to a single LoadGen thread if not (although this has its own limitations).
You get the last message created by LoadGen in the OriginalMessage parameter, which you can use to populate the path and extension of the outgoing message – note the path will be ignored in favour of the DstFilePath set in config. The complete Section in config looks like this:
<Section Name="FileSection">
<MessageCreator Mode="Synchronous">
<SleepInterval>10</SleepInterval>
<QueueLength>10</QueueLength>
<Assembly>x.y.Test.dll/x.y.Test.LoadGen.ConvoyMessageCreator</Assembly> <TemplateFilePath>x:\Msg100Segment1.xml</TemplateFilePath>
</MessageCreator>
<SrcFilePath>x:\Msg100Segment1.xml</SrcFilePath>
<DstLocation>
<Parameters>
<DstFilePath>x:\x.y\Locations\SegmentReceive</DstFilePath>
</Parameters>
</DstLocation>
</Section>
Note that you need to specify TemplateFilePath and SrcFilePath and they need to exist, even if you don't use them. And I couldn't get Asynchronous mode to work (it would always use the source file path over the message creator config), but Synchronous runs the message creator.