BizTalk Server: Receive Pipeline Component To Validate Incoming Data
Problem
BizTalk default EDI validation will validate data base on Schema Enumerations only, not base on some other’s node value. By using this component you can achieve conditional validation.
Solution
Validate second Node’s value base on the first Node’s Value. Example: HIPPA 5010 834 When BGN08 = “4”, then INS03 must be “030” Or “024” Will return Error/ Warning or Information if validation failed.
Component Code
The component code has 4 blocks:
- IBaseComponent - To define Name, Version and Description of component.
- IpersistPropertyBag – To read write properties.
- IcomponentUI – Set Icon of component.
- Icomponent – Contain Execute() where we write code to process incoming message.
Execute(): Code here
/// <summary>
/// Implements IComponent.Execute method.
/// </summary>
/// <param name="pContext">Pipeline context</param>
/// <param name="pInMsg">Input message</param>
/// <returns>Original input message</returns>
/// <remarks>
/// IComponent.Execute method is used to initiate
/// the processing of the message in this pipeline component.
/// </remarks>
IBaseMessage Microsoft.BizTalk.Component.Interop.IComponent.Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
EventLog.WriteEntry("H19", "STARTED...V2.3", System.Diagnostics.EventLogEntryType.SuccessAudit);
bool IsValidVal = false;
string outVal = string.Empty;
string XmlData = string.Empty;
IBaseMessagePart bodyPart = pInMsg.BodyPart;
System.IO.StreamReader sr = new System.IO.StreamReader(bodyPart.Data);
XmlData = sr.ReadToEnd();
// IF EDI Ack then return as it is
if (string.Equals(XmlData, string.Empty) || XmlData.Contains("X12_997_Root") || XmlData.Contains("X12_TA1_Root"))
return pInMsg;
EventLog.WriteEntry("H19", "SourceValues=" + this._SourceValue + ", TargetValue=" + this._TargetValue, System.Diagnostics.EventLogEntryType.SuccessAudit);
IsValidVal = HimUtility.XML.IsValidateValue(XmlData, this._SourceNodeXpath, this._SourceValue, this._TargetNodeXpath, this._TargetValue, out outVal);
if (!IsValidVal)
{
StringBuilder sb = new StringBuilder("Invalid value ");
sb.Append(outVal);
sb.Append(" at node ");
sb.Append(HimUtility.XML.GetNodeFromXpath(this._TargetNodeXpath));
sb.Append(Environment.NewLine);
sb.Append("-----------------------------------------------------");
sb.Append(Environment.NewLine);
sb.Append(HimUtility.XML.GetNodeFromXpath(this._SourceNodeXpath));
sb.Append(" value= ");
sb.Append(this._SourceValue);
sb.Append(" then ");
sb.Append(HimUtility.XML.GetNodeFromXpath(this._TargetNodeXpath));
sb.Append(" node value must be =");
sb.Append(this._TargetValue);
if (this._FailedAction == FS.Error)
{
EventLog.WriteEntry("H19", "Data Validation Failed!!", System.Diagnostics.EventLogEntryType.Error);
Exception Inner = new Exception(sb.ToString());
Exception ex = new Exception("Data Validation Failed!!", Inner);
ex.Source = this.Name;
throw ex;
}
else if (this._FailedAction == FS.Warring)
{
EventLog.WriteEntry("H19", "Data Validation Failed!!" + Environment.NewLine + sb.ToString(), System.Diagnostics.EventLogEntryType.Warning);
}
else
{
EventLog.WriteEntry("H19", "Data Validation Failed!!" + Environment.NewLine + sb.ToString(), System.Diagnostics.EventLogEntryType.Information);
}
}
//Return message
System.IO.MemoryStream strm = new System.IO.MemoryStream(ASCIIEncoding.Default.GetBytes(XmlData));
strm.Position = 0;
bodyPart.Data = strm;
pContext.ResourceTracker.AddResource(strm);
return pInMsg;
}
Utility Function:
namespace HimUtility
{
public class XML
{
/// <summary>
/// Validate Second nodes value base on first node values
/// </summary>
/// <param name="XmlData">Inner/Outer XML data as string</param>
/// <param name="SrcXpath">Xpath of First node</param>
/// <param name="SrcVal">Colun (;) separated value of First node</param>
/// <param name="TrgXpath">Xpath of Second node</param>
/// <param name="TrgVal">Colun (;) separated value of Second node</param>
/// <param name="outVal">Out parameter, return the failed second node value</param>
/// <returns></returns>
public static bool IsValidateValue(string XmlData, string SrcXpath, string SrcVal, string TrgXpath, string TrgVal, out string outVal)
{
bool IsValid = false;
outVal = string.Empty;
XmlDocument Xdoc = new XmlDocument();
Xdoc.LoadXml(XmlData);
XPathNavigator nav = Xdoc.CreateNavigator();
XPathNodeIterator NodeIter;
//Getting First node Values
string[] FN = SrcVal.ToUpper().Split(';');
Hashtable htSrvVal = new Hashtable();
foreach (string s in FN)
htSrvVal.Add(s, s);
//Getting Second Nodes Values
string[] SN = TrgVal.ToUpper().Split(';');
Hashtable htTrgVal = new Hashtable();
foreach (string s in SN)
htTrgVal.Add(s, s);
//Get XML Value of xmlVal
XPathExpression expr;
expr = nav.Compile(SrcXpath); // Compile a standard XPath expression
string xmlVal = nav.SelectSingleNode(expr).InnerXml.ToUpper();
// If 1st value is true then check corresponding valid values
if (htSrvVal.ContainsKey(xmlVal))
{
NodeIter = nav.Select(TrgXpath);
while (NodeIter.MoveNext())
{
outVal = NodeIter.Current.Value.ToUpper();
IsValid = htTrgVal.ContainsKey(outVal);
if (!IsValid)
return false;
Console.WriteLine("TrgVal Title: {0} IsValid: {1}", NodeIter.Current.Value, IsValid);
}
}
else
{
return true;
}
return IsValid;
}
/// <summary>
/// Get node name from Xpath expression
/// </summary>
/// <param name="SrcXpath"></param>
/// <returns></returns>
public static string GetNodeFromXpath(string SrcXpath)
{
string NodeName = string.Empty;
char[] c = { (char)39 };
string[] tmp = SrcXpath.Split(c);
if (tmp.Length > 4)
NodeName = tmp[tmp.Length - 4];//True for EDI Schems
else
NodeName = tmp[tmp.Length];//For normal Schems's Xpath
return NodeName;
}
}
}
Using the component
To use component in any receive pipeline Setup following Values a
- FailedAction : What to do If validation failed, eg. Error out, Log warring or Information.
- SourceNodeXpath : Instance Xpath of First Node.
- SourceValue : Values the to trigger the validation check. Can give multipul vale separated by colun (;) eg: 4
- TargetNodeXpath : Instance Xpath of Second Node.
- TargetValue : Values the to check. Can give multipul vale separated by colun (;) eg: 030;024
The component code has 4 blocks:
- IBaseComponent - To define Name, Version and Description of component.
- IpersistPropertyBag – To read write properties
- IcomponentUI – Set Icon of component.
- Icomponent – Contain Execute() where we write code to process incoming message.
Getting : Instance Xpath From XSD
Registering Component
- Build then Deploy to GAC”
- On VS Command prompt
cd “Local path of .ddl"
gacutil /i "H19_PiplelineComponent.dll"
Verify @
C:\WINDOWS\assembly\GAC_MSIL
Adding it to tool bar
Browse then select
Points of Interest
Creating Dropdown on component configuration: Just create enum on component
public enum FS { Information = 1, Warring = 2, Error = 3 }
And it will shows as Dropdown on component properties
While creating this component and searching for some thing got Online Xpath testing site http://xpath.online-toolz.com/tools/xpath-editor.php
I took help from Biztalk pipeline component wizard (http://btsplcw.codeplex.com/releases/view/26930)
See Also
Another important place to find a huge amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is BizTalk Server Resources on the TechNet Wiki.