Calling a BizTalk Transform (Map) from .Net Code

Now that BizTalk 2010 beta is out, you can use BizTalk maps in Workflow Foundation through the Mapper activity. (Installation of the WCF LOB Adapter SDK is required.) I was curious to see if there is a simple way to do the same, i.e. map from data contract to data contract, from pure .Net code (without the workflow context). It turned out to be relatively simple. I modified Paolo Salvatori’s How To Boost Message Transformations Using the XslCompiledTransform class removing BizTalk message parameters as follows:

public class XslCompiledTransformHelper<TransformType, InputDataContractType, OutputDataContractType> where TransformType : Microsoft.XLANGs.BaseTypes.TransformBase
{
    #region Private Constants
    private const int DefaultBufferSize = 10240; //10 KB
    private const int DefaultThresholdSize = 1048576; //1 MB
    #endregion

    #region Private Static Fields
    private static Dictionary<string, MapInfo> mapDictionary;
    private static DataContractSerializer inputDCSerializer;
    private static XmlSerializer inputXmlSerializer;
    private static DataContractSerializer outputDCSerializer;
    private static XmlSerializer outputXmlSerializer;
    #endregion

    #region Static Constructor
    static XslCompiledTransformHelper()
    {
        mapDictionary = new Dictionary<string, MapInfo>();

        if (UseXmlSerializer(typeof(InputDataContractType)))
            inputXmlSerializer = new XmlSerializer(typeof(InputDataContractType));
        else
            inputDCSerializer = new DataContractSerializer(typeof(InputDataContractType));
        if (UseXmlSerializer(typeof(OutputDataContractType)))
            outputXmlSerializer = new XmlSerializer(typeof(OutputDataContractType));
        else
            outputDCSerializer = new DataContractSerializer(typeof(OutputDataContractType));
    }
    #endregion

    #region Public Static Methods
    public static OutputDataContractType Transform(InputDataContractType input)
    {
        return Transform(input, false, DefaultBufferSize, DefaultThresholdSize);
    }

    public static OutputDataContractType Transform(InputDataContractType input, bool debug)
    {
        return Transform(input, debug, DefaultBufferSize, DefaultThresholdSize);
    }

    public static OutputDataContractType Transform(InputDataContractType input, bool debug, int bufferSize, int thresholdSize)
    {
        try
        {
            return Deserialize(Transform(Serialize(input), debug, bufferSize, thresholdSize));
        }
        catch (Exception ex)
        {
            ExceptionHelper.HandleException("XslCompiledTransformHelper", ex);
            TraceHelper.WriteLineIf(debug, ex.Message, EventLogEntryType.Error);
            throw;
        }
    }

    public static Stream Transform(Stream stream)
    {
        return Transform(stream, false, DefaultBufferSize, DefaultThresholdSize);
    }

    public static Stream Transform(Stream stream, bool debug)
    {
        return Transform(stream, debug, DefaultBufferSize, DefaultThresholdSize);
    }

    public static Stream Transform(Stream stream, bool debug, int bufferSize, int thresholdSize)
    {
        try
        {
            MapInfo mapInfo = GetMapInfo(typeof(TransformType).FullName, debug);
            if (mapInfo != null)
            {
                XmlTextReader xmlTextReader = null;

                try
                {
                    VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize);
                    xmlTextReader = new XmlTextReader(stream);
                    mapInfo.Xsl.Transform(xmlTextReader, mapInfo.Arguments, virtualStream);
                    virtualStream.Seek(0, SeekOrigin.Begin);
                    return virtualStream;
                }
                finally
                {
                    if (xmlTextReader != null)
                    {
                        xmlTextReader.Close();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            ExceptionHelper.HandleException("XslCompiledTransformHelper", ex);
            TraceHelper.WriteLineIf(debug, ex.Message, EventLogEntryType.Error);
            throw;
        }
        return null;
    }
    #endregion

    #region Private Static Methods

    private static MapInfo GetMapInfo(string mapFullyQualifiedName, bool debug)
    {
        MapInfo mapInfo = null;
        lock (mapDictionary)
        {
            if (!mapDictionary.ContainsKey(mapFullyQualifiedName))
            {
                TransformType transformBase = Activator.CreateInstance<TransformType>();
                if (transformBase != null)
                {
                    XslCompiledTransform map = new XslCompiledTransform(debug);
                    using (StringReader stringReader = new StringReader(transformBase.XmlContent))
                    {
                        XmlTextReader xmlTextReader = null;

                        try
                        {
                            xmlTextReader = new XmlTextReader(stringReader);
                            XsltSettings settings = new XsltSettings(true, true);
                            map.Load(xmlTextReader, settings, new XmlUrlResolver());
                            mapInfo = new MapInfo(map, transformBase.TransformArgs);
                            mapDictionary[mapFullyQualifiedName] = mapInfo;
                        }
                        finally
                        {
                            if (xmlTextReader != null)
                            {
                                xmlTextReader.Close();
                            }
                        }
                    }
                }
            }
            else
            {
                mapInfo = mapDictionary[mapFullyQualifiedName];
            }
        }
        return mapInfo;
    }

    private static Stream Serialize(InputDataContractType input)
    {
        VirtualStream stream = new VirtualStream();
        if (inputDCSerializer != null)
        {
            inputDCSerializer.WriteObject(stream, input);
        }
        else
        {
            inputXmlSerializer.Serialize(stream, input);
        }
        stream.Seek(0, SeekOrigin.Begin);
        return stream;
    }

    private static OutputDataContractType Deserialize(Stream stream)
    {
        if (outputDCSerializer != null)
        {
            XmlReader reader = XmlReader.Create(stream);
            return (OutputDataContractType)outputDCSerializer.ReadObject(reader);
        }
        else
            return (OutputDataContractType)outputXmlSerializer.Deserialize(stream);
    }

    private static bool UseXmlSerializer(Type type)
    {
        do
        {
            object[] customAttributes = type.GetCustomAttributes(typeof(XmlTypeAttribute), true);
            if ((customAttributes != null) && (customAttributes.Length > 0))
            {
                return true;
            }
            customAttributes = type.GetCustomAttributes(typeof(XmlRootAttribute), true);
            if ((customAttributes != null) && (customAttributes.Length > 0))
            {
                return true;
            }
            if (type.IsArray)
            {
                type = type.GetElementType();
            }
            else
            {
                type = null;
            }
        }
        while (type != null);
        return false;
    }

    #endregion
}

I tested this helper class using the following contracts:

[DataContract(Namespace="urn:Namespace1")]
public class Person1
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public string HomePhone { get; set; }
    [DataMember]
    public string WorkPhone { get; set; }
    [DataMember]
    public string Address { get; set; }
    [DataMember]
    public string Email { get; set; }

    public override string ToString()
    {
        return string.Format("Name: {0}rnE-mail: {1}", Name, Email);
    }
}

[DataContract(Namespace = "urn:Namespace2")]
public class Person2
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public string HomePhone { get; set; }
    [DataMember]
    public string WorkPhone { get; set; }
    [DataMember]
    public string Address { get; set; }
    [DataMember]
    public string Email { get; set; }

    public override string ToString()
    {
        return string.Format("Name: {0}rnE-mail: {1}", Name, Email);
    }
}

I then used the workflow designer to generate a transform class (deriving from Microsoft.XLANGs.BaseTypes.TransformBase) called PersonToPerson. The I used the folowing code lines to run the transformation:

Person1 person1 = new Person1() { … };
Person2 person2 = XslCompiledTransformHelper<PersonToPerson, Person1, Person2>.Transform(person1);

Advertisements