29 July, 2010

VB IsDate equivalent in C#

Now I'm living in a C# world... and my memory fails me every time I need to do this (which frankly, says worrying things about my memory)... DateTime.TryParse returns a boolean indicating whether the input string can be converted to a datetime or not. So you can write a wee function like the following:

public Boolean checkDate(string inputDate)
{
Boolean retValue = false;
DateTime outputDate;
if (DateTime.TryParse(inputDate, out outputDate))
{
retValue = true;
}
return retValue;
}

27 July, 2010

ESB Toolkit and UDDI: Binding a UDDI service to SQL

Maybe I was just blind or stupid when I needed to do this, but it took me a while to find some info on how to set up bindings for SQL in UDDI. So I'm noting these down for next time...

Context: Building a solution using BizTalk 2009 and the ESB Toolkit. I need to call a SQL stored procedure to return price data for a given product. Schemas have already been created using the WCF LOB adapter. Now I want to use a UDDI resolver to configure my off-ramp.

  1. Browse to the publish tab in UDDI
  2. Right click on the desired provider and choose "add service"
  3. Either specify a key or allow the system to generate one
  4. Edit the service name (eg. GetPriceData)
  5. Right click on the service (or click on the bindings tab) and add a binding
  6. Configure the endpoint - instead of an http service, we have an mssql service, so the endpoint will be in this format: mssql://databaseservername//databasename?
  7. Configure the transport type: Add a category and select the scheme microsoft-com:esb:runtimeresolution:transporttype. Choose a value of WCF Custom Binding.
  8. Configure the BizTalk application: Add a custom category and search for tModels using %biztalk%. Select microsoft-com:esb:runtimeresolution:biztalkapplication. Type "BizTalk Application" for the key name and the name of the your BizTalk application container (this is in your admin console) for the key value.
  9. Add instance info (search for tModels containing %esb%) - you need to add the following:
  • microsoft-com:esb:runtimeresolution:transporttype (use WCF-Custom, by which I mean set the instance parameter to WCF-Custom)
  • microsoft-com:esb:runtimeresolution:targetnamespace (target namespace of your SQL schema - in my case http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo )
  • microsoft-com:esb:runtimeresolution:action (the correct action is in your schema, if you created it using the WCF LOB adapter - in my case it was {TypedProcedure/dbo/GetPrice} - note that you need to enclose the action in curly braces)
  • microsoft-com:esb:runtimeresolution:messageexchangepattern (use "Two-Way", if you're sending a request and expecting a response)
  • microsoft-com:esb:runtimeresolution:jaxrpcresponse (set to "false")
  • microsoft-com:esb:runtimeresolution:cachetimeout (set to "-1")
  • microsoft-com:esb:runtimeresolution:endpointconfig (set to "BindingType=sqlBinding")

To get the key to use in your resolver, click on the service name. On the details tab, click the "more details" link on the right. This brings up the UDDI keys. Copy the value for the version 3 key into the "service key" property for the UDDI resolver in your itinerary.

26 July, 2010

Document structure validation and routing failed messages

A nice way of validating the document structure of an incoming message is to use a receive pipeline with both a disassembler component (which validates the structure) and a validation component (which apparently validates the message against any restrictions in the schema).

Configure your receive location to use this pipeline and check "enable routing for failed messages" on the receive port.

Now you can set up a send port (or whatever you need to handle invalid messages), which subscribes to messages with an ErrorReport.ReceivePortName = your receive port, ErrorReport.ErrorType = 'FailedMessage' and ErrorReport.FailureCategory = (you may need to do some testing to see what the appropriate categories are - the ErrorReport options are promoted properties on the failed message) and send an email alert or set up an error-handling orchestration etc.

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.