Thursday, January 17, 2008

Related Activities in Different BAM Views Do Not Appear in the Portal - Part II

So really the title of this post should be Adding Activities to Different BAM Views. The answer is actually simple - deploy the activity in both views. This may seem obvious when you are defining multiple views in a single deployment, but if you are trying to reference an activity that has already been deployed (or was deployed by a seperate Department or Business Unit) it is not obvious at all.

The HR group has already deployed an activity called Hiring that tracks new hire information being propagated to all the relevant systems. When our system gets the new hire message, we want to create a related activity for our own process. Our activity is called SetSystemAccess. We call the AddRelatedActivity method to create the relationship. When we create our BAM definition file we also have a view called SetAccessView.

The HR group has already deployed their BAM activity and it is already being used in their HiringProcessView. If you have read Part I, you know why the activity relationship will not appear in the BAM Portal when we deploy our BAM definition file. So the first question is: Which view should show the activity relationship, SetAccessView or HiringProcessView? Most likely the HR group does not care about your SetAccessView or they would create and deploy it.

To get the relationship to show up in the SetAccessView, we will need the definition file for HR's Hiring activity. Once we have the HR definition file, we will copy the Hiring activity into our definition file (probably better to use xml rather than Excel for this) . Now we add an ActivityView to our SetAccessView for the Hiring activity. When we deploy this definition file to BAM, the Hiring activity will show up in our view and also in the related activities. Of course, we would have to do the opposite to get our SetSystemAccess activity to show up in HR's HiringProcessView.

Of course, there are some problems with this. Is the HR department willing to give you their definition file? Will your BizTalk or BAM admin allow you to deploy an activity like this? You may say "Why not?". What happens if you were to add a new checkpoint to the Hiring activity when you deploy your definition file? You would be changing the BAM database table structure for the activity, not your own copy but THE copy! Even if you only make that mistake in the Test environment, you will drive your DBA mad. What happens when HR changes the activity definition? I would only recommend sharing activities like this if there is a very close relationship between you and the HR (or whomever) group. If you are in a small company this probably is not a very big deal. If you are in a large company and dealing with shared BizTalk environments this is BAD. Can it really be that bad? If the word SOX makes you get the chills - you probably already understand.

Tuesday, December 11, 2007

Related Activities in Different BAM Views Do Not Appear in the Portal

For the solution - jump ahead to Part II

Maybe everyone already knows this. I didn't.

It appears that BAM Activities must be in the same View to get links in Related Activities.

Not sure what I mean? Suppose Activity AA adds a reference to Activity BB:

DirectEventStream es = new DirectEventStream();
es.BeginActivity("AA", "someGUID");
es.BeginActivity("BB", "someotherGUID");
es.AddRelatedActivity("AA", "someGUID", "BB", "someotherGUID");
es.EndActivity("AA", "someGUID");
es.EndActivity("BB", "someotherGUID");

Of course we have already deployed the activities AA and BB to BAM using bm.exe. While we were deploying we created a View AAView that contained AA and a View BBView that contained BB.

Go to the BAM portal and do an Activity Search for AA. No related activities. Damn. If you look in the Primary Import Tables you will see that the related activity did get added. It simply is not showing in the portal. You might wonder why? I did.

Get out the Ole Reflector.

In the BamManagementService assembly we find a class called BamManagementService. If we look at its GetRelatedActivityInstances(String, String, String) : RelatedActivities we find this:

infoArray[i] = EndGetRelatedActivityInstances(result);
foreach (ActivityInfo info in infoArray[i].ActivitiesInTheSameView)
{
if (!dictionary.ContainsKey(info.ActivityName))
{
dictionary.Add(info.ActivityName, info);
}
}

This would imply that when we get back from the asynchronous call, the activities have already been filtered.

Digging a little further we find in the ActivityModule class the methods GetRelatedActivityInstances(String, String, String) and GetRelatingActivityInstances(String, String, String). These call, respectively, the following SP: bam_Metadata_GetRelatedActivityInstances and bam_Metadata_GetActivityInstancesForRelatedActivity.

If we look at bam_Metadata_GetActivityInstancesForRelatedActivity we can see that this SP is getting all the activities in the current view and checking their Related Activities for the current Activity Instance. Ok, this will very obviously only return items in the current view.

Lets look at bam_Metadata_GetRelatedActivityInstances. This SP will return all Related Activities for the current Activity Instance regardless of whether they are in the same view. In fact, it even includes a note "
-- Instances which don't belong to the view are filtered out in the code". So back to the code we go. Let's go to the Class GetRelatedInstancesAsyncResult. This is the asynchronous call I mentioned before. If we look at the method CompleteGetRelatedInstances() you can see this is where the code calls the GetRelatedActivityInstances and GetRelatingActivityInstances methods. A little further down you'll see a call to GetActivitiesForView, which amazingly enough will return all the Activities in the current View. But I still don't see where the filtering is.

Or do we... Lets take a look at the foreach loop that is above. The code is storing a dictionary of Activity names that are in the same view. For each Activity name, it is storing the info object. A little further down we hit gold:

foreach (RelatedActivitiesInfo info2 in infoArray)
{
foreach (ActivityInstances instances in info2.RelatedInstances)
{
ActivityInfo info3 = null;
if (dictionary.TryGetValue(instances.ActivityName, out info3))
{
.......
}
}
}

That ..... will only be reached when the ActivityName is in the view. Don't believe me? Reflect on it :).

Stay tuned. In the next post I'll be talking about the easy way to resolve this (and why the easy way can be bad).

Tuesday, December 4, 2007

Linq to Google

Introducing Linq to Google! This implemention of the .NET Linq interface will allow easy access to items in Google Base. The project currently supports queries using almost all of the supported syntax (no crowding yet). As the project goes on I will be extending it to do inserts, updates, and deletes. Then I will be taking it to the next level - All the other Google Data sources. Thats right. You will be able to use Linq to Google, or glinq as I have affectionately dubbed it, to query YouTube, Calendar, Email, Docs, etc.

Stay tuned as I will be posting some articles here explaining what the source code does. For now you can get the source at CodePlex

Monday, November 12, 2007

Calling a Web Service from an Orchestration using Direct Bound ports - Part 1

In a previous post I discussed the problems that can occur when adding webservice references directly to your Orchestrations assembly. Here is what I have come up with as a best practice. The general flow is:


  • Create a seperate .NET assembly and add a Web Reference


  • Create a multipart message in your orchestration


  • Create a property schema for routing


  • Create a correlation set and a correlation type


  • Add a send-receive direct bound port to your orchestration


  • Create send and receive shapes in your orchestration


  • Add a SOAP Solicit-Response send port in the Admin console


Without further ado, let's get started!



Create the Proxy Assembly



Once you have identified the web service you are going to call (we will use http://services.aonaware.com/DictService/DictService.asmx which provides dictionary services), create a new .NET Class Library Project called WebServicesProxy. Add a web reference to http://services.aonaware.com/DictService/DictService.asmx. Here is a simple walk through on creating a web reference just in case.



Create Multi-part Messages



Next we need to create two multi-part messages. One for the Request and one for the Response. This is the tricky part. We are going to use the request multi-part message as the request schema for the web method we are calling, so it needs to match the method's signature. The multi-part message type needs to have a part for each parameter on the web service. The part's name and type must match the parameter name and type.


Request Multi-part Message
For this sample we are going to call the Define method. The Define method has a single parameter - word, of Type string.

Create a new multi-part message type called DefineRequest (this name is arbitrary). Add a message part called "word" and set the type to System.String. Since this will be the only message part it will also be the Message Body Part. It is critical that the message part be called word and have type System.String.




Now we need to create a message of this type called msgDefineRequest. Next create a Construct Message shape containing a MessageAssignment shape. The message assignment could look something like this:





Response Multi-part Message

The response message needs to match the return signature of Define. In this case, the response contains a complex element called DefineResult. Since this is not a simple type, we need to define a schema for it.


Be carefull when looking at the response! Many web services return multiple simple types rather than wrapping them in a complex element. In that case you will have to create a message similar to the Request multi-part message created above, where each return type has a message-part.


After creating a schema for the DefineResult type, create a new multi-part message type called DefineResponse. Name the message-part DefineResult and set its type to the schema that was created. It is critical that the message-part is named DefineResult!



Create a Property Schema for Routing



Since the multipart message we have created does not have any unique routable properties yet, we need to create one. There are many ways this could be accomplished, but here is an easy one. Create a new property schema called DictionaryRouting. Rename the root node to DictionaryMethod and set its PropertySchemaBase to MessageContextPropertyBase (explanation of property schemas and Property Schema Base). In the orchestration's Message Assignment shape created earlier, set the DictionaryMethod context property for msgDefineRequest to "Define".








This will be the filter criteria we use for routing. The idea is that we could have a filter criteria for each port equivalent to the web service method being called.





Create a Correlation Set



For the property we created earlier to be available for routing we need to promote it. This is the slickest way I know of for promoting properties. Create a new correlation type called DictionaryCorrelation using the DictionaryMethod property we created before. Next create a new Correlation Set called PromoteDictionaryMethod, and set it to use the DictionaryCorrelation.






When we create the Send shape in a minute we will add this correlation set to promote the DictionaryMethod property.


To be continued in part 2.

Friday, October 26, 2007

Adding SOAP Headers in BizTalk

To add SOAP headers to a web service call you need to create context properties.

In a pipeline component:
inmsg.Context.Write("SOAPHeaderName", "http://schemas.microsoft.com/BizTalk/2003/SOAPHeader", "<headerxml/>");

When the SOAP Adapter creates the outbound message, it looks for context properties that match header names. If the wsdl you generated the WebReference from does not have the header (it may be hidden or some other trickery on the receiving side) the context property is ignored.

The context properties do not have to be set in a pipeline component, you could also set them in an orchestration.

System.String Is Not Serializable - BizTalk WebReferences

The title of this blog may appear to be a joke, unfortunately it is not. That is the compile error that I received recently when adding a WebReference to my Orchestrations assembly. The problem occurs under a specific set of circumstances:


  • The Orchestrations assembly is delay signed
  • Assembly verification has been turned off for the assembly rather than the key
  • A WebReference is added to the Orchestrations assembly and a WebPort is added to an orchestration

Under these circumstances you will receive a series of compile time errors to the effect of _SomeType_ is not Serializable. Generally the types I have seen are System.Int32 and System.String. Of course, anyone that knows about serialization would immediately question the validity of System.String not being serializable. But if you try to compile again, you will get an error that the assembly ~OrchestrationAssemblyName.dll is in use. Hmmm.

After beating my head against the keyboard, monitor(s), telephone, cubicle wall, etc... I called Microsoft Premier Support - it's nice to have. While waiting for my callback (the helpdesk has an expert call you) I realized that the ~OrchestrationAssemblyName.dll being created by Visual Studio in the background is being delay signed. Unfortunately this is preventing it from being loaded by Visual Studio. So I went ahead and turned off verification for the ~OrchestrationAssemblyName.dll (it only shows up for a quick second unless you have this problem) and poof, problem solved.

Microsoft was able to reproduce the problem and agreed that it is a bug in Visual Studio. Unfortunately there is no guarantee when it will be fixed since I was able to find a workaround (turn off assembly verification for the ~Assembly or at the key level). Unfortunately I still get this message every once in a while. Usually if I close and reopen Visual Studio everything is fine.

But what is the real solution? I don't really want to bind to a logical port in an Orchestration anyway. Check out my post on Direct Binding and Web Services.