locked
Mapped variables and MapValues RRS feed

  • Question

  • I've been searching for days for detailed information about what mapped variables are and how they are related to the MapValues method in PersistenceParticipant, but all I can find is information about how to use them, rather than what they are for or when to use them.

    If I had to guess, I would say that mapped variables are added as write-only values at some point between the CollectValues and MapValues stage, but at what point do we restore these mapped variables? The PurchaseProcess sample application is quite confusing, as it stores the requestForProposal mapped variable in the instance store as well as in the external XML store, but the requestForProposal is reloaded from the instance store without consulting the external XML store, despite being marked as write-only during the save process. What is the correct way to load mapped variables? I imagine that the SQL Server instance store does respect the write-only modifier, yet in the HiringRequestProcess sample, I can't see where the workflow loads the hiringRequestInfo variable from. 

    I suppose my actual question is this: I have a bunch of business entities which are persisted in a database elsewhere. I would prefer that the workflow not persist its own copy (especially if it ends up trying to persist the whole object graph) but instead reload the entities from the database as necessary. I don't think workflow extensions are the right way to do this, since we'll just end up having temporary variables in the workflow mirroring the real objects, and these temporary variables will be persisted, which is exactly what we wanted to avoid. So what is the intended use case for mapped variables, and how do I implement the behaviour I want?

    Tuesday, July 27, 2010 10:27 AM

Answers

  • When you persist your workflow, the variables will be stored in a serialized blob in the database.  Even if you deserialize the blob, it's not very readable (it's not meant to be). 

    In the case where you want to store you variables in a human readable format, you can map your variables.  By that I mean you set the variable Modifiers property to Mapped.  This will cause a Location variable to be passed into the WriteOnlyVariables parameter of the MapValues function.  You can iterate through the Xname values and find the Location variables to find your mapped variable. The Location contains the name, value and parent scope of the variable.

     You can make this more readable by digging out the value and returning your own XName property.

            protected override IDictionary<XName, object> MapValues(IDictionary<XName, object> readWriteValues,
                IDictionary<XName, object> writeOnlyValues)
            {
                Dictionary<XName, object> retval = new Dictionary<XName, object>();
                foreach (KeyValuePair<System.Xml.Linq.XName, object> item in writeOnlyValues)
                {
                    if (item.Value is LocationInfo) //find all mapped variables
                    {
                        LocationInfo li = (LocationInfo)item.Value; //get the LocationInfo object associated with this variable.
                        XName name = Common.variableNS.GetName(li.Name);
                        retval.Add(name, li.Value);
                    }
                }
                return retval;
            }

    When you return your own property (as I did above) it will put it into the serialized blob as well. 

    For example, if you map a variable and deserialized the blob, you would see an additional XName property that looks similiar to this:

    {urn:schemas-microsoft-com:System.Activities/4.0/properties/variables}activity.1.1-2_2

    with a value like this:

    <value z:Id="4" z:Type="System.Activities.Hosting.LocationInfo" z:Assembly="System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
         <Name z:Id="5" xmlns="http://schemas.datacontract.org/2004/07/System.Activities.Hosting">OrderState</Name>
         <OwnerDisplayName z:Id="6" xmlns="http://schemas.datacontract.org/2004/07/System.Activities.Hosting">Main Sequence</OwnerDisplayName>
         <Value z:Id="7" z:Type="System.String" z:Assembly="0" xmlns="http://schemas.datacontract.org/2004/07/System.Activities.Hosting">Sent</Value>
    </value>

    In the example above I created my own property with this XName:

    {urn:schemas-microsoft-com:System.Activities/4.0/properties/variables}OrderState

    and set the value to "Sent" which I got out of the Location.Value property.  This makes it easier to get to.

    There's a few things that you can do with that newly exposed data....

    You can Promote the properties in the SQLInstancestore.  This is actually really kind of cool.  Basically it will put it in the InstancePromotedPropertiesTable in the database.  To do this you would pass a list of XName properties to the SQLInstanceStoreBehavior.Promote function:

    List<XName> persistValues = new List<XName>();
    persistValues.Add(Common.variableNS.GetName("OrderState"));

    sqlServerInstStoreBeh.Promote("ScottPromotion", persistValues, null);

    From there you can create a view in SQL Server to show your values.

    OR

    you can use the PersistenceIOParticipant.BeginOnSave method to store writeOnlyValues to a separate location (database, xml, text, whatever). 

     

    As far as your last question, the readWriteProperties are passed into the PublishValues method and you will have access to them.  However, the variables internal to the workflow are stored inside an internal property:

    {urn:schemas-microsoft-com:System.Activities/4.0/properties}Workflow

    This property is where the actual workflow will get it's variables from.  In other words, the variables you mapped will be avialable to you for writing and reading from within the PP but the workflow instance itself won't use those additional mapped properties to rehydrate itself.

    Hope that helps,

    Scott

     


    MS Developer Support
    • Proposed as answer by Andrew_Zhu Monday, August 2, 2010 7:37 AM
    • Marked as answer by Andrew_Zhu Tuesday, August 3, 2010 5:47 AM
    Wednesday, July 28, 2010 12:26 AM

All replies

  • When you persist your workflow, the variables will be stored in a serialized blob in the database.  Even if you deserialize the blob, it's not very readable (it's not meant to be). 

    In the case where you want to store you variables in a human readable format, you can map your variables.  By that I mean you set the variable Modifiers property to Mapped.  This will cause a Location variable to be passed into the WriteOnlyVariables parameter of the MapValues function.  You can iterate through the Xname values and find the Location variables to find your mapped variable. The Location contains the name, value and parent scope of the variable.

     You can make this more readable by digging out the value and returning your own XName property.

            protected override IDictionary<XName, object> MapValues(IDictionary<XName, object> readWriteValues,
                IDictionary<XName, object> writeOnlyValues)
            {
                Dictionary<XName, object> retval = new Dictionary<XName, object>();
                foreach (KeyValuePair<System.Xml.Linq.XName, object> item in writeOnlyValues)
                {
                    if (item.Value is LocationInfo) //find all mapped variables
                    {
                        LocationInfo li = (LocationInfo)item.Value; //get the LocationInfo object associated with this variable.
                        XName name = Common.variableNS.GetName(li.Name);
                        retval.Add(name, li.Value);
                    }
                }
                return retval;
            }

    When you return your own property (as I did above) it will put it into the serialized blob as well. 

    For example, if you map a variable and deserialized the blob, you would see an additional XName property that looks similiar to this:

    {urn:schemas-microsoft-com:System.Activities/4.0/properties/variables}activity.1.1-2_2

    with a value like this:

    <value z:Id="4" z:Type="System.Activities.Hosting.LocationInfo" z:Assembly="System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
         <Name z:Id="5" xmlns="http://schemas.datacontract.org/2004/07/System.Activities.Hosting">OrderState</Name>
         <OwnerDisplayName z:Id="6" xmlns="http://schemas.datacontract.org/2004/07/System.Activities.Hosting">Main Sequence</OwnerDisplayName>
         <Value z:Id="7" z:Type="System.String" z:Assembly="0" xmlns="http://schemas.datacontract.org/2004/07/System.Activities.Hosting">Sent</Value>
    </value>

    In the example above I created my own property with this XName:

    {urn:schemas-microsoft-com:System.Activities/4.0/properties/variables}OrderState

    and set the value to "Sent" which I got out of the Location.Value property.  This makes it easier to get to.

    There's a few things that you can do with that newly exposed data....

    You can Promote the properties in the SQLInstancestore.  This is actually really kind of cool.  Basically it will put it in the InstancePromotedPropertiesTable in the database.  To do this you would pass a list of XName properties to the SQLInstanceStoreBehavior.Promote function:

    List<XName> persistValues = new List<XName>();
    persistValues.Add(Common.variableNS.GetName("OrderState"));

    sqlServerInstStoreBeh.Promote("ScottPromotion", persistValues, null);

    From there you can create a view in SQL Server to show your values.

    OR

    you can use the PersistenceIOParticipant.BeginOnSave method to store writeOnlyValues to a separate location (database, xml, text, whatever). 

     

    As far as your last question, the readWriteProperties are passed into the PublishValues method and you will have access to them.  However, the variables internal to the workflow are stored inside an internal property:

    {urn:schemas-microsoft-com:System.Activities/4.0/properties}Workflow

    This property is where the actual workflow will get it's variables from.  In other words, the variables you mapped will be avialable to you for writing and reading from within the PP but the workflow instance itself won't use those additional mapped properties to rehydrate itself.

    Hope that helps,

    Scott

     


    MS Developer Support
    • Proposed as answer by Andrew_Zhu Monday, August 2, 2010 7:37 AM
    • Marked as answer by Andrew_Zhu Tuesday, August 3, 2010 5:47 AM
    Wednesday, July 28, 2010 12:26 AM
  • I see - so mapped variables are a way to expose more data, rather than a way to store data differently? Hmmm. It seems a bit like wasteful duplication of data to me...

    Is there any way at all to exclude a workflow variable from normal persistence using a PersistenceParticipant or a PersistenceIOParticipant?

    Wednesday, July 28, 2010 10:16 AM
  • I'm looking for an answer to this one as well.
    Wednesday, September 8, 2010 7:12 AM