26 July, 2010

Sending flat file attachments with an HTML email from BizTalk

I got this working in two different ways, both from within orchestrations. In both examples, I only ever needed to attach one file per email. However, I think the second example could be easily modified to work with multiple attachments by adding more parts to the multi-part message type. I haven't yet tried using SMTP.Attachments to add multiple attachments (in the first example), but it could be worth a try.

Example 1:
The requirement here was to email a file to the client upon dispatch of an order batch. To do this, I archived the dispatch file (in my case, this was an Excel file) out to a specific file directory using a FILE send port early in the orchestration (as file archiving was a requirement anyway). I used a dynamic send port, so I could set the file name and use this to reference the file later. Note that in the orchestration, I also set up a loop with a delay shape to check for the existence of the archived file before creating the email (in case the send port had been disabled or something else went wrong). To do this, I used a helper class which returned the boolean value of System.IO.File.Exists(path)- where path is a string variable equal to the archive file directory path and file name.

I created a multi-part message type for the email. It only had one part of type RawString (Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString - sample code for this is in the SDK).

In a message assignment shape, I built the body of the email (usually I'd have HTML tags in the string variable for the body, but it's a pain to publish them here) using a string variable responseText and message ProvDispatch (an instance of the multi-part message type) eg.


responseText = "This is to confirm an order requiring provisioning has been dispatched.";

ProvDispatch.ResponseBody = new Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString(responseText);
ProvDispatch.ResponseBody(Microsoft.XLANGs.BaseTypes.ContentType) = "text/html";
ProvDispatch(SMTP.EmailBodyFileCharset) = "UTF-8";

In the same shape, I then added the Excel file created earlier in the orchestration as an attachment. The file path was stored in a string variable "reportPath" and the file name was stored in a string variable "attachmentName".

ProvDispatch(SMTP.Attachments) = reportPath + attachmentName + ".xlsx";

Then set the subject and email addresses (if required):

ProvDispatch(SMTP.Subject) = "Dispatch Notification";
ProvDispatch(SMTP.From) = emailVariable;


I needed to use dynamic addressing for the outbound email, so I then configured my send port (DispatchResponse_Port) with the email to address, stored earlier in the orchestration in a string variable "provEmail":

DispatchResponse_Port(Microsoft.XLANGs.BaseTypes.Address) = "mailto:" + provEmail;

Your port should be configured to use the PassThru pipeline.

This example produces an email with a formatted HTML body "This is to confirm an order requiring provisioning has been dispatched" with the required file attached in the correct format with the correct name. For example, if the variable "attachmentName" above had a value "TestFile", the attachment on the email would be "TestFile.xlsx".

Example 2:
In this example, the client was sending an order file which needed to undergo some validation checks. If the file failed validation, it needed to be emailed back to the client as an attachment, with a list of validation errors in the body of the email.

The order file was in CSV format, which was being mapped to a common order schema on the receive port. So, if data validation checks failed, I needed to recreate the CSV file the customer sent in order to email it back to them.

To do this, I:


  1. Created a map, which mapped the contents of the common order message to the CSV order message
  2. Created a send pipeline "MyFF_SPipe", using a Flat File Assembler (document schema = CSV order file schema)
  3. Created a receive pipeline "MyXDoc_RPipe", with nothing in the disassemble stage of the pipeline. This will be used to receive any document in a message variable of type System.XML.XmlDocument (which preserves the format of the inbound message - ie. does not convert it to XML)
  4. In the orchestration, created a message FFRequest (type = CSV order file schema). Constructed this message using the map file created in step 1.
  5. Created a message FFRequestOut (type = CSV order file schema)
  6. Created an atomic scope and defined a variable "inputMsgs" of type Microsoft.XLANGs.Pipeline.SendPipelineInputMessages (add a reference to Microsoft.XLANGS.Pipeline.dll to the project) and a variable "pipelineMsgs" of type Microsoft.XLANGs.Pipeline.ReceivePipelineOutputMessages
  7. To create the CSV file, in an atomic scope, I executed the send pipeline created in step 2 from a message assignment shape in a construct block (message constructed = FFRequestOut), using the following code:
    FFRequestOut = null; inputMsgs.Add(FFRequest); Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteSendPipeline (typeof(MySolution.pipelines.MyFF_SPipe) ,inputMsgs ,FFRequestOut);
  8. Called the receive pipeline MyXDoc_RPipe created in step 3 from an expression shape using the following code:
    pipelineMsgs = Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteReceivePipeline(typeof(MySolution.pipelines.MyXDoc_RPipe),FFRequestOut);
  9. In a construct block, assigned the first message (there should only be one) in pipelineMsgs to a message called "Attachment" of type System.Xml.XmlDocument using the following code:
    pipelineMsgs.MoveNext();
    Attachment = new System.Xml.XmlDocument();
    pipelineMsgs.GetCurrent(Attachment);
  10. Created a multi-part message type for the outbound email with two parts - BodyPart of type Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString and Attachment of type System.Xml.XmlDocument
  11. In a message assignment shape in a construct block (constructing a message "FailResp" with the multi-part message type created in step 10), build the message:
    responseText = "File rejected for the following reasons...."; FailResp.BodyPart = new Microsoft.Samples.BizTalk.XlangCustomFormatters.RawString(responseText); FailResp.BodyPart(Microsoft.XLANGs.BaseTypes.ContentType) = "text/html"; FailResp.Attachment = new System.Xml.XmlDocument(); FailResp.Attachment = Attachment; FailResp.Attachment(MIME.FileName) = fileName; FailResp(SMTP.MessagePartsAttachments) = 2; FailResp(SMTP.Subject) = "Order file REJECTED"; FailResp(SMTP.EmailBodyFileCharset) = "UTF-8"; FailResp(SMTP.From) = emailFrom;
    * emailFrom is a string variable, as is fileName (which simply sets the attachment name to something sensible eg. RejectedOrderFile.csv)
  12. I'm using a dynamic send port again, so I need to also configure the email to address (see example 1). The send port is also configured to use the PassThru pipeline.



No comments:

Post a Comment