none
How to access Orchestration variables in MAP?

    Question

  • Hello,

    I have a input Message (input message 1) which has a distinguished field.

    My map uses another message (input Message 2) as input source for transformation.

    In my Orchestration once I receive the input message 1, i  set a variable (string) with the distinguished property. I want to access this variable in map  to set this to  my destination schema.

    I checked this thread

    http://social.msdn.microsoft.com/Forums/en-US/biztalkgeneral/thread/20b60f52-ac11-47d8-a91e-cccd26b4f516

    If i proceed in this manner, i loose my existing map. My map is bit complex and I cannot recreate this with short span. Is there any other way to access the Orchestration variable? I also checked http://biztalkmessages.vansplunteren.net/2009/04/05/orchestration-variable-retriever-functoid-and-why-you-should-not-use-it/ but I don't think i will have privilege to install custom Funtoids on server

    any help would be appreciated

    Thanks

    Karthik

    Thursday, March 17, 2011 5:52 AM

Answers

  • Hi there,

    there is a simple solution to this:

    Use a small static helper class that stores values in an internal Dictionary<int,Dictionary<string,string>> object. The outer Dictionary is used to identify your current process (use a key, that will stay the same within orchestration an mapping, current Thread id for example). The inner dictionary is used to store the values you will need in the mapping. Add methods to Get a value (make sure it returns a default value if the key cannot be found in the dictionary), to set a value and to remove the inner dictionary for your current process.

    Now you fill the dictionary within a Message Assignment shape directly before the Transform shape (this is in case your orchestration gets persisted to the message box and is re-hydrated after a host restart - then the dictionary would be empty if you fill it earlier in the process). After the Transform shape you use the remove method within another message assignment shape to clear the values from the dictionary to make sure you don't run out of memory because of old instances. Inside the mapping you can access the static helper class using the fully qualified name and use the get method to retrieve the value - this will always return the default value during "Test Map" in Visual Studio of course - since you have no chance to initialize the values inside the dictionary.

    Working helper code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace MapDictionaryHelper
    {
      public class MapValueDictionary
      {
        static Dictionary<int, Dictionary<string, string>> internalStore = new Dictionary<int, Dictionary<string, string>>();
    
        public static void Add(string key,string value) {
          Dictionary<string,string> currentDictionary;
          if (internalStore.TryGetValue(Thread.CurrentThread.ManagedThreadId, out currentDictionary))
          {
            currentDictionary[key] = value;
          }
          else
          {
            currentDictionary = new Dictionary<string, string>();
            currentDictionary.Add(key, value);
            internalStore.Add(Thread.CurrentThread.ManagedThreadId, currentDictionary);
          }
        }
    
        public static string Get(string key,string defaultValue)
        {
          Dictionary<string, string> currentDictionary;
          if (internalStore.TryGetValue(Thread.CurrentThread.ManagedThreadId, out currentDictionary))
          {
            string value;
            if (currentDictionary.TryGetValue(key, out value))
              return value;
          }
          return defaultValue;
        }
    
        public static void RemoveData()
        {
          if (internalStore.ContainsKey(Thread.CurrentThread.ManagedThreadId))
            internalStore.Remove(Thread.CurrentThread.ManagedThreadId);
        }
      }
    }
    

    Use Method Get as External Assembly Scripting Functoid. Use like this in orchestration within construct message block:

    MapDictionaryHelper.MapValueDictionary.Add("Value1", "Value1+"+System.Convert.ToString(Microsoft.XLANGs.Core.Service.RootService.InstanceId));
    MapDictionaryHelper.MapValueDictionary.Add("Value2", "Value2+"+System.Convert.ToString(Microsoft.XLANGs.Core.Service.RootService.InstanceId));
    MapDictionaryHelper.MapValueDictionary.Add("Value3", "Value3+"+System.Convert.ToString(Microsoft.XLANGs.Core.Service.RootService.InstanceId));
    

    Then call your Transform and afterwards cleanup:

    MapDictionaryHelper.MapValueDictionary.RemoveData();

    That's it! I just built this and tested it - works like a charm.

    Regards,

    Leo

    Thursday, March 17, 2011 7:39 AM
  • Providing that the map is running directly from the Orchestration you can use ThreadStatic variables in a .NET helper to hold data.  Similar to Leo's routine:

    using System;
    using System.Collections.Generic;
    
    namespace Whatever
    {
    	public class Mapping
    	{
    		[ThreadStatic]
    		public static Dictionary<string, string> Data = new Dictionary<string,string>();
    
    		public static string GetDataValue(string key)
    		{
    			if (Data.ContainsKey(key))
    			{
    				return Data[key];
    			}
    			else
    			{
    				return null;
    			}
    		}
    
    		public static void SetDataValue(string key, string value)
    		{
    			Data[key] = value;
    		}
    	}
    }
    
    


    If this is helpful or answers your question - please mark accordingly.
    Because I get points for it which gives my life purpose (also, it helps other people find answers quickly)
    • Marked as answer by KKarthik Friday, March 18, 2011 8:38 AM
    Thursday, March 17, 2011 9:15 AM

All replies

  • Karthik

    It is possible to access the orchestration variables in the map but it comes with little performance hit.

    For one my client I work for had the same scenario where the client's existing map with X12 source to a SQL Schema destination had situation to access a variable from the orchestration. This is what we did

    Option 1:

    1. Promoted the orchestration variable to a context properties of the message.

    2. Then wrote a custom functiod to access this context property from the message this message had a one constant input parameter where message name which you assign it in orchestration.

    3. The code works basically playing with XLangMessage Type(which is not recommended best practice) then fetch the value from the context property and return the value.

    Now in your case since you don't have luxary of creating the custom function probably you can try this using the External library called in a scripting functiod.

    However we had few issues when we have run few testsm, hence we thought about the option 2

    Option 2:

    Create a map with two source schema's message1 and message2  you can create this map using orchestration transformation shape.

    If you want to persist these links from the old map then you will have to manually open the existing map and manually tweak the xpath of the new map and save the old map content to the new map.

    I accept this is little tedious work but it will help gain much of the performance/less issues rather than accessing the orchestration variables from the map.

    I prefer to use the option 2 though it is really painful job.

     


    Nandri,
    Sathish
    Thursday, March 17, 2011 6:42 AM
  • Thanks Satish

    Looks like i have to work on option 2. as you said it would be painful for me in terms of re-mapping and testing. all these to be done for getting one value from input message. Perhaps i will look for other alternatives

     

    Thursday, March 17, 2011 7:00 AM
  • Hi there,

    there is a simple solution to this:

    Use a small static helper class that stores values in an internal Dictionary<int,Dictionary<string,string>> object. The outer Dictionary is used to identify your current process (use a key, that will stay the same within orchestration an mapping, current Thread id for example). The inner dictionary is used to store the values you will need in the mapping. Add methods to Get a value (make sure it returns a default value if the key cannot be found in the dictionary), to set a value and to remove the inner dictionary for your current process.

    Now you fill the dictionary within a Message Assignment shape directly before the Transform shape (this is in case your orchestration gets persisted to the message box and is re-hydrated after a host restart - then the dictionary would be empty if you fill it earlier in the process). After the Transform shape you use the remove method within another message assignment shape to clear the values from the dictionary to make sure you don't run out of memory because of old instances. Inside the mapping you can access the static helper class using the fully qualified name and use the get method to retrieve the value - this will always return the default value during "Test Map" in Visual Studio of course - since you have no chance to initialize the values inside the dictionary.

    Working helper code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace MapDictionaryHelper
    {
      public class MapValueDictionary
      {
        static Dictionary<int, Dictionary<string, string>> internalStore = new Dictionary<int, Dictionary<string, string>>();
    
        public static void Add(string key,string value) {
          Dictionary<string,string> currentDictionary;
          if (internalStore.TryGetValue(Thread.CurrentThread.ManagedThreadId, out currentDictionary))
          {
            currentDictionary[key] = value;
          }
          else
          {
            currentDictionary = new Dictionary<string, string>();
            currentDictionary.Add(key, value);
            internalStore.Add(Thread.CurrentThread.ManagedThreadId, currentDictionary);
          }
        }
    
        public static string Get(string key,string defaultValue)
        {
          Dictionary<string, string> currentDictionary;
          if (internalStore.TryGetValue(Thread.CurrentThread.ManagedThreadId, out currentDictionary))
          {
            string value;
            if (currentDictionary.TryGetValue(key, out value))
              return value;
          }
          return defaultValue;
        }
    
        public static void RemoveData()
        {
          if (internalStore.ContainsKey(Thread.CurrentThread.ManagedThreadId))
            internalStore.Remove(Thread.CurrentThread.ManagedThreadId);
        }
      }
    }
    

    Use Method Get as External Assembly Scripting Functoid. Use like this in orchestration within construct message block:

    MapDictionaryHelper.MapValueDictionary.Add("Value1", "Value1+"+System.Convert.ToString(Microsoft.XLANGs.Core.Service.RootService.InstanceId));
    MapDictionaryHelper.MapValueDictionary.Add("Value2", "Value2+"+System.Convert.ToString(Microsoft.XLANGs.Core.Service.RootService.InstanceId));
    MapDictionaryHelper.MapValueDictionary.Add("Value3", "Value3+"+System.Convert.ToString(Microsoft.XLANGs.Core.Service.RootService.InstanceId));
    

    Then call your Transform and afterwards cleanup:

    MapDictionaryHelper.MapValueDictionary.RemoveData();

    That's it! I just built this and tested it - works like a charm.

    Regards,

    Leo

    Thursday, March 17, 2011 7:39 AM
  • Hi...

    Look the below link

    http://social.msdn.microsoft.com/Forums/en-US/biztalkgeneral/thread/602b4ec7-ad5e-44a8-9dde-d1cabb5af893

     

     


    Srikanth Peddy. MCTS-BizTalk Server Please mark as answered .
    Thursday, March 17, 2011 8:38 AM
  • Providing that the map is running directly from the Orchestration you can use ThreadStatic variables in a .NET helper to hold data.  Similar to Leo's routine:

    using System;
    using System.Collections.Generic;
    
    namespace Whatever
    {
    	public class Mapping
    	{
    		[ThreadStatic]
    		public static Dictionary<string, string> Data = new Dictionary<string,string>();
    
    		public static string GetDataValue(string key)
    		{
    			if (Data.ContainsKey(key))
    			{
    				return Data[key];
    			}
    			else
    			{
    				return null;
    			}
    		}
    
    		public static void SetDataValue(string key, string value)
    		{
    			Data[key] = value;
    		}
    	}
    }
    
    


    If this is helpful or answers your question - please mark accordingly.
    Because I get points for it which gives my life purpose (also, it helps other people find answers quickly)
    • Marked as answer by KKarthik Friday, March 18, 2011 8:38 AM
    Thursday, March 17, 2011 9:15 AM
  • Hi Leo,

    When i try to do add in my Orchestration, the ADD is not visible. Any idea why?
    Thursday, March 17, 2011 12:41 PM
  • Hi,

    you access it like this: MapDictionaryHelper.MapValueDictionary.Add(...) - you need to have a reference to the helper project in your orchestration project.

    I hope this helps.

    Regards, Leo


    Please mark it as Answer if this answers your question.
    Thursday, March 17, 2011 1:17 PM
  • Hi Leo,

     

    when i remove Static, everything works. BTW how can i pass the parameters to Get() method using "External Assembly" in scripting Functoid?

     

    regards

    Karthik

    Thursday, March 17, 2011 1:23 PM
  • Hi,

    in the end it will not work if you remove static. The point is, because it is a static method, you have to call it like I wrote above: <NAMESPACE>.<CLASSNAME>.<STATICMETHOD> - You do NOT need a local variable with this type within your orchestration, which I think you did and named it after the class name.

    You set the parameters for the Get Method in the scripting functoid on the "Functoid Input" tab. Click the "+" Sign to add the two parameters.

    Regards,

    Leo


    Please mark it as Answer if this answers your question.
    Thursday, March 17, 2011 4:32 PM
  • Hi,

    Point is if its Static, i dont see that method in my orchestration. It works only in cases of non-static method

    thanks

    Friday, March 18, 2011 4:10 AM
  • I did something similar using ThreadStatic.

    Sadly, it doesn't work 100% of the time.  There are times when 15 instances of the same orchestration with mapping runs, one of them would overwrite the data in one of the maps.

    The source message of my map has a field which holds data in JSON format.  I pass that to a script functoid which parses the JSON string and stores data in a ThreadStatic dictionary from which I get the data from to map into the target message.

    I wish there were a better explanation about the threading behavior of maps when invoked within an orchestration..


    [Serializable]
        public class JSONBizTalkMapAccessor
        {
            #region Private Members
            /// <summary>
            /// Holds the attribute hash table.  Must be thread static due to the way objects are persisted across multiple
            /// instances of the map in orchestrations.
            /// </summary>
            [ThreadStatic]
            private static Dictionary<string, string> attributeTable = new Dictionary<string, string>();
    
            /// <summary>
            /// Indicates if attribute has previously been loaded or not.  Must be thread static due to the way objects are persisted across multiple
            /// instances of the map in orchestrations.
            /// </summary>
            [ThreadStatic]
            private static bool isInit = false;
    
            private static string title = typeof(JSONBizTalkMapAccessor).ToString();
            #endregion
    
          
            public void InitGlobals(string jsonString, string turnKeysToLowerCase)
            {
                try
                {
                    if (!JSONBizTalkMapAccessor.isInit)
                    {
                        // Check value of turn keys to lower case
                        bool turnKeysToLowerCaseBool = (turnKeysToLowerCase.ToLower() == "true") ? true : false;
    
                        JSONBizTalkMapAccessor.attributeTable = JSONParser.BuildStringDictionaryFromJSON(jsonString, turnKeysToLowerCaseBool);
                        JSONBizTalkMapAccessor.isInit = true;
                    }
                }
                catch(System.Exception ex)
                {                
                    throw ex;
                }
            } 
    
            /// <summary>
            /// Returns the corresponding value of attribute if exists.
            /// </summary>
            /// <param name="key"></param>
            /// <returns></returns>
            public string GetAttributeValue(string key, string nothing)
            {
                try
                {
                    if (JSONBizTalkMapAccessor.attributeTable != null && JSONBizTalkMapAccessor.attributeTable.ContainsKey(key))
                    {
                        return JSONBizTalkMapAccessor.attributeTable[key];
                    }
    
                    return string.Empty;
                }
                catch (System.Exception ex)
                {
                    throw ex;
                }
            }
    
            /// <summary>
            /// Adds key value pair to dictionary. If key value pair exists, it overwrites the value
            /// of the existing one.
            /// </summary>
            /// <param name="keyName"></param>
            /// <param name="value"></param>
            public void AddKeyValuePair(string keyName, string value)
            {
                if (JSONBizTalkMapAccessor.attributeTable.ContainsKey(keyName))
                {
                    JSONBizTalkMapAccessor.attributeTable.Add(keyName, value);
                }
                else
                {
                    JSONBizTalkMapAccessor.attributeTable[keyName] = value;
                }
            }
    
            /// <summary>
            /// Converts the string dictionary into JSON format
            /// </summary>
            /// <returns></returns>
            public string BuildJSONFromDictionary()
            {
                return JSONParser.BuildJSONFromDictionary(JSONBizTalkMapAccessor.attributeTable, false);
            }
        }


    Thursday, May 31, 2012 4:35 AM