Cannot create/update a Rally TestCaseResult with a reference to a specified TestSet - rest

With Rally, I need to
Update a TestCaseResult with a new TestSet Ref.
OR
Create a new TestCaseResult by copying everything from a previous TestCaseResult and just changing the TestSet Ref.
I am trying to do the same through the Java REST toolkit from Rally. It uses the JSON REST API internally, it seems.
When I try to do this with CreateRequest or UpdateRequest, I get an error from the API "Could not set value for Test Set: null"
Is it not possible to update the TestSet of a TestCaseResult (whether existing or newly created)?
Here's some sample code I am using (showing create testcaseresult from existing by changing testset).
//get testcaseresult object
GetRequest tcrReq = new GetRequest("/testcaseresult/12345.js");
tcrReq.setFetch(new Fetch("FormattedID", "Name"));
GetResponse tcrResponse = restApi.get(tcrReq);
//update testcaseresult object with new testset
JsonObject tsRef = new JsonObject();
tsRef.addProperty("_ref", "https://rally1.rallydev.com/slm/webservice/1.39/testset/1029348.js");
tcrResponse.getObject().add("TestSet",tsRef);
tcrResponse.getObject().remove("_ref");
//Create API for new testcaseresult object
CreateRequest createRequest = new CreateRequest("testcaseresult", tcrResponse.getObject());
CreateResponse createResponse = restApi.create(createRequest);
if(createResponse.wasSuccessful()){
System.out.println(createResponse.getObject());
}else{
String[] ss = createResponse.getErrors();
for(int i=0; i<ss.length; i++){
System.out.println(ss[i]);
}
}
Can you please help to understand whether I am doing something wrong or is this a Rally limitation?

I believe the reason that you are getting the "Could not set value for Test Set: null" error message is that there is an "invisible" constraint on TestCaseResults whereby the TestCase to which they are associated must be scheduled into the TestSet of interest, before the TestCaseResult can be assigned that TestSet as an attribute.
Unfortunately, there's no TestSet attribute on TestCases, so you have to query the TestCases collection off of the TestSet, and then loop through that collection to check and see if the parent TestCase is a member of that collection. Once verified that the TestCase is in that TestSet's collection of TestCases, then you can proceed to successfully update a member TestCaseResult with that TestSet attribute of interest. I tested the below and it works as expected.
Here's a code snippet illustrating how to accomplish this:
// Create and configure a new instance of RallyRestApi
// Connection parameters
String rallyURL = "https://rally1.rallydev.com";
String wsapiVersion = "1.38";
String applicationName = "RestExample_UpdateTestSetOnTestCaseResult";
// Credentials
String userName = "user#company.com";
String userPassword = "password";
RallyRestApi restApi = new RallyRestApi(
new URI(rallyURL),
userName,
userPassword);
restApi.setWsapiVersion(wsapiVersion);
restApi.setApplicationName(applicationName);
// Ref to Test Case Result of Interest
String testCaseResultRef = "/testcaseresult/1234567891.js";
GetRequest testCaseResultRequest = new GetRequest(testCaseResultRef);
GetResponse testCaseResultResponse = restApi.get(testCaseResultRequest);
JsonObject testCaseResultObj = testCaseResultResponse.getObject();
// Get the Test Case Result's Parent Test Case
JsonObject testCase = testCaseResultObj.get("TestCase").getAsJsonObject();
String testCaseRef = testCase.get("_ref").getAsString();
GetRequest testCaseRequest = new GetRequest(testCaseRef);
GetResponse testCaseResponse = restApi.get(testCaseRequest);
JsonObject testCaseObj = testCaseResponse.getObject();
System.out.println(testCaseRef);
// Ref to Test Set of Interest
String testSetRef = "/TestSet/12345678910.js";
// Get the Test Set of interest
GetRequest testSetRequest = new GetRequest(testSetRef);
GetResponse testSetResponse = restApi.get(testSetRequest);
JsonObject testSetObject = testSetResponse.getObject();
// Grab the Test Cases in this Test Set
JsonArray testCasesInTestSet = testSetObject.get("TestCases").getAsJsonArray();
// Loop through and see if our Test Case of interest is a member
boolean testCaseIsInSet = false;
for (int i=0; i<testCasesInTestSet.size(); i++) {
JsonObject thisTestCase = testCasesInTestSet.get(i).getAsJsonObject();
String thisTestCaseRef = thisTestCase.get("_ref").getAsString();
if (thisTestCaseRef.equals(testCaseRef)) {
testCaseIsInSet = true;
}
}
if (testCaseIsInSet) {
// Update Test Set on Existing Test Case Result
try {
//Add Test Set
System.out.println("\nUpdating Existing Test Case Result's Test Set attribute...");
testCaseResultObj.addProperty("TestSet", testSetRef);
UpdateRequest updateExistTestCaseResultRequest = new UpdateRequest(testCaseResultRef, testCaseResultObj);
UpdateResponse updateExistTestCaseResultResponse = restApi.update(updateExistTestCaseResultRequest);
if (updateExistTestCaseResultResponse.wasSuccessful()) {
System.out.println("Updated Test Case Result with new Test Set");
String[] updateExistTestCaseResultWarnings;
updateExistTestCaseResultWarnings = updateExistTestCaseResultResponse.getWarnings();
System.out.println("Warning(s) occurred updating Test Case Result: ");
for (int i=0; i<updateExistTestCaseResultWarnings.length;i++) {
System.out.println(updateExistTestCaseResultWarnings[i]);
}
} else {
String[] updateExistTestCaseResultErrors;
updateExistTestCaseResultErrors = updateExistTestCaseResultResponse.getErrors();
System.out.println("Error occurred updating Test Case Result: ");
for (int i=0; i<updateExistTestCaseResultErrors.length;i++) {
System.out.println(updateExistTestCaseResultErrors[i]);
}
}
} catch (Exception e) {
System.out.println("Exception occurred while updating Tags on existing Test Case: ");
e.printStackTrace();
}
finally {
//Release all resources
restApi.close();
}
} else {
System.out.println("Unable to Update Test Case Result with specified Test Set");
System.out.println("Parent Test Case is not a member of this Test Set");
}
}

When updating the TestSet you can just set the value as its ref- you don't need the wrapper object.
tcrResponse.getObject().add("TestSet", "/testset/1029348.js");

Related

Hapi Fhir query with AND / OR with map

Using Hapi Fhir, I try to write a query to use a map to pass in where condition, as follow:
Map<String, List<IQueryParameterType>> hmParameter = criteria.getMapForWhere();
Bundle bundle = fhirClientR4Configuration.clientFhihrR4().search().forResource(ServiceRequest.class)
.where(hmParameter).returnBundle(Bundle.class).execute();
My criteria object has a method getMapForWhere() where I populate this map with information inside my wrapper front end object as follow:
public Map<String, List<IQueryParameterType>> getMapForWhere() {
Map<String, List<IQueryParameterType>> hmOut = new HashMap<String, List<IQueryParameterType>>();
// Adding STATUS
if (this.status != null && !this.status.equals("")) {
StringParam sp = new StringParam();
sp.setValue(this.status);
List<IQueryParameterType> lst = new ArrayList<IQueryParameterType>();
lst.add(sp);
hmOut.put(ServiceRequest.SP_STATUS, lst);
}
// Adding INTENT
if (this.intent != null && !this.intent.equals("")) {
StringParam sp = new StringParam();
sp.setValue(this.intent);
List<IQueryParameterType> lst = new ArrayList<IQueryParameterType>();
lst.add(sp);
hmOut.put(ServiceRequest.SP_INTENT, lst);
}
return hmOut;
}
This code works fine when I want to wirte a query with all AND.
But if I want to add another condition as follow:
List<IQueryParameterOr> lstOr = new ArrayList<IQueryParameterOr>();
StringOrListParam lstServices = new StringOrListParam();
StringParam sp = new StringParam();
StringParam sg = new StringParam();
sp.setValue(MEDICAL_SERVICE_PAI);
sg.setValue(SOCIAL_SERVICE_PAI);
lstServices.addOr(sp);
lstServices.addOr(sg);
lstOr.add(lstServices);
hmOut.put(ServiceRequest.SP_CATEGORY, lstOr);
Obviously hmOut goes in error because definition of that map is different. But I don't know how to convert IParameterOr with IParameterType.

Extracting the values of elements (XML) which is in an array and put it in the message property in Camel

As you can see the Availability Flag, BeamId is getting repeated. How do I traverse and set the property for Availability Flag1 and so on, so that I can later fetch it with velocity template?
Payload:<ns2:TransportFeasibilityResponse>
<ns2:Parameters>
<ns2:AvailabilityFlag>true</ns2:AvailabilityFlag>
<ns2:SatellitedID>H1B</ns2:SatellitedID>
<ns2:BeamID>675</ns2:BeamID>
<ns2:TransportName>Earth</ns2:TransportName>
</ns2:FeasibilityParameters>
<ns2:Parameters>
<ns2:AvailabilityFlag>true</ns2:AvailabilityFlag>
<ns2:SatellitedID>J34</ns2:SatellitedID>
<ns2:BeamID>111</ns2:BeamID>
<ns2:TransportName>Jupiter</ns2:TransportName>
</ns2:Parameters>
</ns2:TransportFeasibilityResponse>
</ns2:TransportFeasibilityResponseMsg>
Code: (Its not complete)
public static HashMap<String,String> extractNameValueToProperties(String msgBody, selectedKeyList, namelist) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setExpandEntityReferences(false);
factory.setNamespaceAware(true);
Document doc = null;
try{
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(new InputSource(new StringReader(msgBody)));
} catch(Exception ex) {
Exception actException = new Exception( "Exception while extracting tagvalues", ex);
throw actException;
}
HashMap<String,String> tagNameValueMap = new HashMap<String,String>();
NodeList nodeList = doc.getElementsByTagName("*");
// Trying to enter the TransportFeasibilityResponse element
for (int i = 0; i < nodeList.getLength(); i++) {
Node indNode = nodeList.item(i);
if (indNode.indexOf(String name)>-1);
//checking for Availability flag and similar namelist
dataKey = indNode.getTextContent();
message.setProperty(selectedKeyList[k], dataKey);
k++;
j++;
else
{
continue;
}
}
}
Here,
I am setting these values in my route:
<setProperty propertyName="namelist">
<constant>AvailabilityFlag,SatellitedID,BeamID</constant>
</setProperty>
<setProperty propertyName="selectedKeyList">
<constant>AvailabilityFlag1,SatellitedID1,BeamID1,AvailabilityFlag2,SatellitedID2,BeamID2 </constant>
</setProperty>
<bean beanType="com.gdg.dgdgdg.javacodename" method="extractNameValueToProperties"/>
Question: Please tell me how I can parse through the repeating elements and assign it to the property?
Thanks
I'm not sure if I understand your question correctly, but I think you could use the Splitter pattern to split your xml per Parameters tag and process each other separately and aggregate it later.
Take for example this input:
<TransportFeasibilityResponse>
<Parameters>
<AvailabilityFlag>true</AvailabilityFlag>
<SatellitedID>H1B</SatellitedID>
<BeamID>675</BeamID>
<TransportName>Earth</TransportName>
</Parameters>
<Parameters>
<AvailabilityFlag>true</AvailabilityFlag>
<SatellitedID>J34</SatellitedID>
<BeamID>111</BeamID>
<TransportName>Jupiter</TransportName>
</Parameters>
</TransportFeasibilityResponse>
A route to process this input could be something like this:
from("direct:start")
.split(xpath("/TransportFeasibilityResponse/Parameters"), new AggregationStrategy() {
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
List<String> beamIDs = null;
if (oldExchange == null) { // first
beamIDs = new ArrayList<String>();
} else {
beamIDs = oldExchange.getIn().getBody(List.class);
}
beamIDs.add(newExchange.getIn().getBody(String.class));
newExchange.getIn().setBody(beamIDs);
return newExchange;
}
})
.setBody(xpath("/Parameters/BeamID/text()"))
.end()
.log("The final body: ${body}");
First, we split the input per Parameters tag, and then extract the BeamID from it. After that, the AggregationStrategy aggregates each message into one, grouping by BeamID.
The final message should have the a body like this:
675,111
The data I put in the body just for an example, but you could set anywhere you want into the Exchange you are manipulating inside the AggregationStrategy implementation.

Error "The given key was not present in the dictionary" When Creating New Custom Entity on Update Event

On CRM 2013 on-premise, I'm trying to write a plugin that triggers when an update is made to a field on Quote. The plugin then creates a new custom entity "new_contract".
My plugin is successfully triggered when the update to that field is made. However I keep getting an error message "The given key was not present in the dictionary" when trying to create the new custom entity.
I'm using a "PostImage" in this code. I confirm that it's registered using the same name in Plugin Registration.
Here is the code
var targetEntity = context.GetParameterCollection<Entity>
(context.InputParameters, "Target");
if (targetEntity == null)
{throw new InvalidPluginExecutionException(OperationStatus.Failed,
"Target Entity cannot be null")}
var postImage = context.PostEntityImages["PostImage"];
if (postImage == null)
{throw new InvalidPluginExecutionException(OperationStatus.Failed,
"Post Image is required");}
var quote = context.GenerateCompositeEntity(targetEntity, postImage);
//throw new InvalidPluginExecutionException(OperationStatus.Failed, "Update is captured");
//Guid QuoteId = (Guid)quote.Attributes["quoteid"];
var serviceFactory = (IOrganizationServiceFactory)serviceProvider
.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(context.UserId);
var contractEntity = new Entity();
contractEntity = new Entity("new_contract");
if (quote.Attributes.Contains("portfolio"))
{
var quotePortfolio = (EntityReference)quote.Attributes["new_portfolio];
contractEntity[Schema.new_contract.PortfolioName] =
new EntityReference(quotePortfolio.LogicalName, quotePortfolio.Id);
}
if (quote.Attributes.Contains(Schema.Quote.QuoteName))
{
var quoteName = (string)quote.Attributes["name"];
contractEntity[Schema.new_contract.contractName] = quoteName;
}
var contractId = service.Create(contractEntity);
I think context does not contain "PostImage" attribute.You should check context to see whether it contains the attribute before getting the data.
Looking at this line in your post above:
var service = serviceFactory.CreateOrganizationService(context.UserId);
I am deducing that the type of your context variable is LocalPluginContext (since this contains the UserId value) which does not expose the images (as another answer states).
To access the images, you need to get to the Plugin Execution Context:
IPluginExecutionContext pluginContext = context.PluginExecutionContext;
Entity postImage = null;
if (pluginContext.PostEntityImages != null && pluginContext.PostEntityImages.Contains("PostImage))
{
postImage = pluginContext.PostEntityImages["PostImage"];
}
In the below code segment, you are checking for the attribute "portfolio" and using "new_portfolio". Can you correct that and let us know whether that worked.
if (quote.Attributes.Contains("portfolio"))
{
var quotePortfolio = (EntityReference)quote.Attributes["new_portfolio];
contractEntity[Schema.new_contract.PortfolioName] = new EntityReference(quotePortfolio.LogicalName, quotePortfolio.Id);
}
First, you don't say what line is throwing the exception. Put in the VS debugger and find the line that is throwing the exception.
I did see that you are trying to read from a dictionary here without first checking if the dictionary contains the key, that can be the source of this exception.
var postImage = context.PostEntityImages["PostImage"];
if (postImage == null)
throw new InvalidPluginExecutionException(OperationStatus.Failed,
"Post Image is required");
Try this:
if(!context.PostEntityImages.Contains("PostImage") ||
context.PostEntityImages["PostImage"] == null)
InvalidPluginExecutionException(OperationStatus.Failed, "Post Image is required");
var postImage = context.PostEntityImages["PostImage"];
Although, I don't think that a PostEntityImage Value will ever be null, if it passes the Contains test you don't really need the null check.

How to force the Plugin on Post-Operation to Submit?

I have a plugin in post-operation witch need to create a folder on sharepoint via webservice, to do that, my plugin calls a webservice to execute a FechXML to get the info from the entity, but the problem is that entity still not exist, and it give me Null.
How do i force the plugin to submit/save the data to my FechXml to work?
PLUGIN CODE:
try
{
Entity entity;
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName != "fcg_processos")
{
throw new InvalidPluginExecutionException("Ocorreu um erro no PlugIn Create Folder.");
}
}
else
{
throw new InvalidPluginExecutionException("Ocorreu um erro no PlugIn Create Folder.");
}
processosid = (Guid)((Entity)context.InputParameters["Target"])["fcg_processosid"];
string processoid2 = processosid.ToString();
PluginSharepointProcessos.ServiceReference.PrxActivityResult result = log.CreateFolderSP("Processo", processoid2);
string resultado = result.xmlContent;
if (result.retCode > 0)
{
throw new InvalidPluginExecutionException("Ocorreu um erro na criação do Folder do Processo.");
}
WEBSERVICE CODE:
{
//WEBSERVICE TO CALL XML FROM ENTITY
PrxActivityResult Processo = ProcessoFetch2("", "", guid);
string stxml;
stxml = Processo.XmlContent;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(stxml);
XmlNodeList nodeList = xmlDoc.SelectNodes("resultset/result");
List<string[]> lista = new List<string[]>();
string[] strs = new string[7];
if (nodeList.Count != 0)//verificar o numero de registos
{
foreach (XmlNode xmlnode in nodeList)
{
if (xmlnode.SelectSingleNode("//fcg_numero") != null)
strs[2] = xmlnode.SelectSingleNode("//fcg_numero").InnerText;
else
strs[2] = "";
if (xmlnode.SelectSingleNode("//Concurso.fcg_numero") != null)
strs[3] = xmlnode.SelectSingleNode("//Concurso.fcg_numero").InnerText;
else
strs[3] = "";
}
}
IwsspClient FmwSharepoint = new IwsspClient();
PrxActivityResult folderresult = new PrxActivityResult();
List<ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave> arrayfields = new List<ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave>();
ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave nprocesso = new ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave();
nprocesso.Key = "FCG_Numero_Processo";
nprocesso.value = strs[2];
arrayfields.Add(nprocesso);
ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave npconcurso = new ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave();
npconcurso.Key = "FCG_Numero_Concurso";
npconcurso.value = strs[3];
arrayfields.Add(npconcurso);
ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave npguid = new ws.fcg.sipp.svc.ServiceReferenceSharePoint.PareChave();
npguid.Key = "FCG_Guid_CRM";
npguid.value = guid;
arrayfields.Add(npguid);
folderresult = FmwSharepoint.CreateFolder("http://localhost/folder", "Processos", strs[2], arrayfields.ToArray());
res = folderresult;
}
When a plugin runs on the Post-Operation, it is still within the database transaction, and it hasn't actually been committed to the database. Any calls done with the service reference passed in as a part of the Plugin Context will be executed within the context on the database transaction and you will be able to retrieve the newly created/updated values. If you create a brand new OrganizationServiceProxy (Which I'm guessing is what you're doing), it will execute outside of the database transaction, and will not see the newly created / updated values.
As #AndyMeyers suggests in his comment (which really should be an answer IMHO), grabbing the data from the plugin context either via a pre/post image or the target is ideal since it eliminates another database call. If you're having to lookup records that may have been created by another plugin that fired earlier, you'll need to use the IOrganizationService that is included in the plugin context.
I had no option and I used this code to run webservice based on image and forget the FecthXml, as mentioned, i get all info from the Image on the post operation and send back to the WebService. Thanks, here is the code:
entity = (Entity)context.InputParameters["Target"];
concursid = (Guid)entity.Attributes["fcg_concursid"];
guid = concursid.ToString();
string npconcurs = (string)entity.Attributes["fcg_numer"];
nconcurs= npconcurs;
EntityReference nprograma = (EntityReference)entity.Attributes["fcg_unidadeorganica"];
program = nprogram.Name;
if (entity.LogicalName != "fcg_concurs")

Programmatically creating an Assign in a Flowchart Workflow

I need to programmatically define a serializable flowchart Windows Workflow that accepts input arguments and returns a result. I understand how to create these workflows using a designer, but I need to do it in code and have the flowchart workflow be serializable (so no lambda expressions).
I'm having trouble getting the "To" field of the Assign. The code below creates a flowchart workflow of a WriteLine followed by an Assign.
var ab = new ActivityBuilder<string> {
Name = "ActivityBuilt",
Implementation = new Flowchart {
StartNode = new FlowStep {
Action = new WriteLine { Text = new VisualBasicValue<string>("greeting") },
Next = new FlowStep {
Action = new Assign {
DisplayName = "Set result",
To = new OutArgument<string>(new VisualBasicReference<string> {
ExpressionText = "results"}),
Value = new VisualBasicValue<string>("bye")
}
}
}
}
};
ab.Properties.Add(new DynamicActivityProperty {
Name = "greeting",
Type = typeof (InArgument<string>),
Value = "hello"});
ab.Properties.Add(new DynamicActivityProperty {
Name = "results",
Type = typeof (OutArgument<string>),
Value = "bye"});
// Convert the ActivityBuilder to a callable activity
using (var sw = new StringWriter()) {
using (var xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(sw, new XamlSchemaContext()))) {
XamlServices.Save(xw, LastCreated);
}
using (var sr = new StringReader(sw.ToString())) {
var wf = ActivityXamlServices.Load(sr);
return wf;
}
}
The above code fails when I try to convert from ActivityBuilder to Activity saying "Failed to create a 'OutArgument' from the text 'bye'." This works fine if I don't use the OutArgument and just pass things in.
My question is what to put in the To property? How do I reference the OutArgument I add to the ActivityBuilder.Properties? A VisualBasicReference isn't an OutArgument. Am I making this more difficult than it needs to be?
Thanks for any hints!
Edit: I want to create a workflow programmatically. The workflow needs to have input arguments and return results (output arguments).
I understand how to create the workflow and how to declare and use input arguments. I'm using an ActivityBuilder to create the workflow and to set the InArgument via the ActivityBuilder's properties. I create the workflow from the ActivityBuilder by serializing to XAML and then deserializing using ActivityXamlServices.Load.
What I don't understand is how to get a result from the workflow. I assume it involves an OutArgument. How/where do I add an OutArgument to the workflow? I thought the code snippet I gave would assign a value to an OutArgument, but the call to ActivityXamlServices.Load fails with a complaint that it is unable to create the OutArgument.
Is the approach of using ActivityBuilder correct?
Is the "To" field of the Assign action properly referencing an OutArgument?
How do I make the ActivityBuilder aware of the OutArgument and still be able to convert to an Activity / workflow?
Hope this clarifies my problem.
There are atleast 3 problems with the code:
The Value of the Assign needs to be an InArgument().
The value you are trying to read from is named "greeting" not "bye".
The OutArgument named "results" can't have a default value.
Try the following code:
var ab = new ActivityBuilder<string>
{
Name = "ActivityBuilt",
Implementation = new Flowchart
{
StartNode = new FlowStep
{
Action = new WriteLine { Text = new VisualBasicValue<string>("greeting") },
Next = new FlowStep
{
Action = new Assign
{
DisplayName = "Set result",
To = new OutArgument<string>(new VisualBasicReference<string>
{
ExpressionText = "results"
}),
Value = new InArgument<string>(new VisualBasicValue<string>("greeting"))
}
}
}
}
};
ab.Properties.Add(new DynamicActivityProperty
{
Name = "greeting",
Type = typeof(InArgument<string>),
Value = "hello"
});
ab.Properties.Add(new DynamicActivityProperty
{
Name = "results",
Type = typeof(OutArgument<string>)
});
// Convert the ActivityBuilder to a callable activity
using (var sw = new StringWriter())
{
using (var xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(sw, new XamlSchemaContext())))
{
XamlServices.Save(xw, ab);
}
using (var sr = new StringReader(sw.ToString()))
{
var wf = ActivityXamlServices.Load(sr);
WorkflowInvoker.Invoke(wf);
}
}