When you deploy BizTalk assembly you must ensure that any referenced assembly is also deployed. Actually, BizTalk is enforcing that by not letting you applying adding resource action without deploying all its references.
When removing resource BizTalk doesn't uninstalling the actual assembly from the GAC, so, any application would work regardless to missing registration of the assembly in BizTalk database.
On the other hand, BizTalk is forcing dependencies when trying to delete (or modify) any resource that has other depended artifact in BizTalk. This means that if one application uses pipeline component or map from referenced BizTalk Application, BizTalk explorer object model (Microsoft.BizTalk.ExplorerOM) will throw an exception when trying to remove the assembly containing the pipeline component or map as long you'll not reset the pipeline to the default passthrough pipeline components (or some pipeline from other deployed assembly) and remove the mapping from the port.
The very same behavior is happens when it come to BizTalk resources. Any attempt to remove assembly that referenced by other assembly inside any BizTalk application will throw exception by BizTalk ApplicationDeployment API's. (Microsoft.BizTalk.ApplicationDeployment)
How does BizTalk detects references assemblies
BizTalk is actually uses System.Reflection to detect references. Each System.Reflection.Assembly object has References member of type List that contains the fully-qualified name. Inside BizTalk any resource has Resource.Luid property, when deleting (or modifying) assembly ApplicationDeployment API's loads each resource as an System.Reflection.Assembly object and checks the References member for this Luid. If BizTalk finds equal Luid an exception will be thrown.
Because of this reason, updating infrastructure artifacts is quite headache. The Process must be in the following order:
- Removing all referenced Applications.
- Remove infrastructures old version.
- Deploy infrastructures new version.
- Deploy all high level referenced Application.
Where can I search for BizTalk assembly for checking its referenced Assemblies
There are three places which BizTalk assembly can be found for checking its referenced assemblies:
- Local server where the assembly location specified by the sourceLocation property – this approach
- Remote server where the sourceLocation property specifies assembly on the local machine - this is the most bad option because you must be sure the assembly is the very same as the one on the remote computer. Unfortunately this is the actual way BizTalk Administration Console is using – see below.
- Local server on the GAC – this is the best option because it checks the actual running assembly.
How can I detect references in BizTalk assemblies
BizTalk 2006 MMC offers GUI for detecting references in BizTalk assemblies. Despite this, the MMC is running in a client mode, on a remote computer so, the GUI is not checking the remote machine GAC. As I wrote, this checking is Reflection based so; the BizTalk MMC must load the assembly into memory before being able to search for Luid equality – which is not possible from remote computer.
The way BizTalk MMC handles this issue is by using the property annotations list that each BizTalk resource contains:
Each Resource has Resource.Properties property of IDictionary<string,Object> type. the entry in this dictionary which points to the resource path is 'SourceLocation' so, BizTalk Administration console uses this entity for the parameters needed by System.Reflection.Assembly.Load method. By this, if the local machine contains the assembly on the same path as in the remote computer – BizTalk Administration console can load it into memory and search for references.
In the following, BizTalk administration console MMC was find the assembly in the path specified under the sourceLocation, so check for dependencies was preformed.
If the Administration Console can't find the assembly in the location specified in sourceLocation – the System.Reflection.Assembly.Load method failed and no further check is preformed.
In this insight, it would be much more advisable to run the check on the local server GAC. Unfortunately, BizTalk 2006 administration console is MMC snap-in and was designed to run on a remote computer so it does not support this approach.
The first step for extracting BizTalk Assembly from the GAC is by finding the path to the assmebly. this can be done by using the GetAssemblyPath(string assemblyDisplayName) static method of the GAC class under Microsoft.BizTalk.Gac namespace. you can find this namespace in Microsoft.BizTalk.Deployment.dll
The second step is to load the assembly. this can be done by using Microsoft.BizTalk.Deployment.Assembly namespace. use AssemblyBase property of the BtsAssemblyManager class.
Here's the code snippet:
public List<string> CheckDependencies(string btsAssemblyLuid, string btsSqlServerName)
{
List<string> dependeciesArr = new List<string>();
string[] btsAssemblyProps = btsAssemblyLuid.Split(',');
string btsAssemblyName = btsAssemblyProps[0];
string btsAssemblyVersion = btsAssemblyProps[1].Remove(1, "Version=".Length).Trim();
string btsAssemblyCulture = btsAssemblyProps[2].Remove(1, "Culture=".Length).Trim();
string btsAssemblyKey = btsAssemblyProps[3].Remove(1, "PublicKeyToken=".Length).Trim();
string btsAssemblyPath = Microsoft.BizTalk.Gac.Gac.GetAssemblyPath(btsAssemblyName);
Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager mgr = new Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager(btsAssemblyPath, null);
Microsoft.BizTalk.MetaDataOM.IAssemblyBase assembly = mgr.AssemblyBase;
Microsoft.BizTalk.ApplicationDeployment.Group group = new Microsoft.BizTalk.ApplicationDeployment.Group();
group.DBName = "BizTalkMgmtDb";
group.DBServer = btsSqlServerName;
foreach (Microsoft.BizTalk.ApplicationDeployment.Application app in group.Applications)
{
foreach (Microsoft.BizTalk.ApplicationDeployment.Resource res in app.ResourceCollection)
{
if (res.ResourceType.Equals("System.BizTalk:BizTalkAssembly") || res.ResourceType.Equals("System.BizTalk:Assembly"))
{
string[] props = res.Luid.Split(',');
string name = props[0];
string version = props[1].Remove(1, "Version=".Length).Trim();
string culture = props[2].Remove(1, "Culture=".Length).Trim();
string key = props[3].Remove(1, "PublicKeyToken=".Length).Trim();
string path = Microsoft.BizTalk.Gac.Gac.GetAssemblyPath(name);
Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager manager = new Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager(path, null);
Array references = manager.AssemblyBase.References as Array;
foreach (string refName in references)
{
if (assembly.DisplayName.Equals(refName))
{
dependeciesArr.Add(app.Name + " : " + res.Luid);
}
}
}
}
}
return dependeciesArr;
}