Sometimes you need the full assembly name, properly formatted to use for various reasons
(such as a WCF Custom Behavior file that is imported into BizTalk adapter).

Here are a couple of ways to get it:

DOS Comamnd Window (GacUtil /L)


C:\Windows\system32>gacutil /l | find "bLogical"
bLogical.BizTalk.RESTBehavior, Version=1.0.0.0, Culture=neutral, PublicKeyToke
n=8b7f8ad23140b57b, processorArchitecture=MSIL
bLogical.RESTSchemas, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fef5a20
096b53fc0, processorArchitecture=MSIL

C# using reflection


//using System.Reflection -- add this using statement
Assembly assembly = Assembly.LoadFrom(@"d:\GitAtlasCore\RestErrorHandling\bLogical.BizTalk.RESTBehavior\bin\Debug\bLogical.BizTalk.RESTBehavior.dll");
foreach (Type type in assembly.GetTypes())
{
Console.WriteLine(type.FullName);
//Console.WriteLine(type.AssemblyQualifiedName);
}

Console.WriteLine("-----");
Type myType = assembly.GetType("bLogical.BizTalk.RESTBehavior.BizTalkRESTRequestHandlerExtensionElement");
Console.WriteLine(myType.AssemblyQualifiedName);

Console.ReadLine();

Output of C#


bLogical.BizTalk.RESTBehavior.BizTalkRESTResponseHandler
bLogical.BizTalk.RESTBehavior.RequestContext
bLogical.BizTalk.RESTBehavior.BizTalkRESTRequestHandlerExtensionElement
bLogical.BizTalk.RESTBehavior.BizTalkRESTRequestHandlerBehavior
bLogical.BizTalk.RESTBehavior.BizTalkRESTRequestHandler
bLogical.BizTalk.RESTBehavior.BizTalkRESTTransmitHandlerExtensionElement
bLogical.BizTalk.RESTBehavior.BizTalkRESTTransmitHandlerEndpointBehavior
bLogical.BizTalk.RESTBehavior.BizTalkRESTTransmitHandler
bLogical.BizTalk.RESTBehavior.BizTalkRESTTransmitErrorHandler
bLogical.BizTalk.RESTBehavior.CustomJsonErrorBodyWriter
bLogical.BizTalk.RESTBehavior.MessageHelper
-----
bLogical.BizTalk.RESTBehavior.BizTalkRESTRequestHandlerExtensionElement, bLogica
l.BizTalk.RESTBehavior, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8b7f8ad
23140b57b


#Biztalk Kill Process associated with BizTalk Host Instance
$processID = Get-WmiObject Win32_Service -Filter "Name='BTSSvc`$BizTalkServerApplication'" | Select-Object -Expand ProcessID
Write-Host "processID=$processID"
stop-process -id $processID -Force
Write-Host "Completed"

This can be used to kill a BizTalk Host Instance that is in the stop-pending state.

Other commands you can run to see how the Get-WmiObject Win32_Service works:

#get-wmiobject Win32_service
#get-wmiobject Win32_service | where {$_.Name -match "BizTalkServerApplication"}
#get-wmiobject Win32_service | where {$_.Name -eq "BTSSvc`$BizTalkServerApplication"}

The following two queries return all send and receive ports/locations that use a given pipeline component.
You can filter on the friendly name or the fully qualified name. This query can be very useful when you need to change a pipeline component,
and you need to “roll off” the applications that use it, make the change, then “roll them back on.

use BizTalkMgmtDb
SELECT app.nvcName AS [Application],
ass.nvcName AS [Assembly],
rp.nvcName AS [Receive Port],
rl.Name AS [Receive Location],
rppl.Name AS [Receive Pipeline],
rppl.FullyQualifiedName as [RecievePipelineFQ]
FROM bts_application app WITH(NOLOCK)
LEFT OUTER JOIN bts_assembly ass WITH(NOLOCK) ON app.nID = ass.nApplicationId
LEFT OUTER JOIN bts_receiveport rp WITH(NOLOCK) ON rp.nApplicationID = ass.nApplicationID
LEFT OUTER JOIN adm_ReceiveLocation rl WITH(NOLOCK) ON rp.nID = rl.receiveportId
LEFT OUTER JOIN bts_pipeline rppl WITH(NOLOCK) ON rl.ReceivePipelineId = rppl.Id
WHERE
rppl.Name like '%shared%'
and rppl.FullyQualifiedName like '%V2%'
ORDER BY 1,2,3

SELECT app.nvcName AS [Application],
ass.nvcName AS [Assembly],
sp.nvcName AS [Send Port],
sppl.Name AS [Send Pipeline],
sppl.FullyQualifiedName as [Send PipelineFQ]
FROM bts_application app WITH(NOLOCK)
LEFT OUTER JOIN bts_assembly ass WITH(NOLOCK) ON app.nID = ass.nApplicationId
LEFT OUTER JOIN bts_sendport sp WITH(NOLOCK) ON sp.nApplicationID = ass.nApplicationID
LEFT OUTER JOIN bts_pipeline sppl WITH(NOLOCK) ON sp.nSendPipelineID = sppl.Id
WHERE
sppl.Name like ‘%shared%’
and sppl.FullyQualifiedName like ‘%V2%’
ORDER BY 1,2,3

You could make it fancier and come up with one query that does a union of the two, but then you probably would need to add another column to indicate if it’s a receive or send.

If you have orchestrations involved, there is another blog post that shows how to xref orchestrations to pipelines.

Today, I was trying to find if a JSON pipeline we had was being used on a given BizTalk system.
I found the SQL command from here: https://gallery.technet.microsoft.com/Show-BizTalk-Applications-8f73d753
and just modified it to add an additional condition to the where clause

and rppl.Name like ‘%json%’

Here is the full command:


SELECT app.nvcName AS [Application],
ass.nvcName AS [Assembly],
orc.nvcName AS [Orchestration],
orc.nOrchestrationStatus AS [Status],
orp.nvcName AS [Orchestration Port],
pt.nvcName AS [Port Type],
pto.nvcName AS [Port Type Operation],
rp.nvcName AS [Receive Port],
rl.Name AS [Receive Location],
rppl.Name AS [Receive Pipeline],
sp.nvcName AS [Send Port],
sppl.Name AS [Send Pipeline],
spg.nvcName AS [Send Port Group]
FROM bts_application app WITH(NOLOCK)
LEFT OUTER JOIN bts_assembly ass WITH(NOLOCK) ON app.nID = ass.nApplicationId
LEFT OUTER JOIN bts_orchestration orc WITH(NOLOCK) ON ass.nID = orc.nAssemblyId
LEFT OUTER JOIN bts_orchestration_port orp WITH(NOLOCK) ON orc.nID = orp.nOrchestrationId
LEFT OUTER JOIN bts_porttype pt WITH(NOLOCK) ON orp.nPortTypeID = pt.nID
LEFT OUTER JOIN bts_porttype_operation pto WITH(NOLOCK) ON pt.nID = pto.nPortTypeID
LEFT OUTER JOIN bts_orchestration_port_binding opp WITH(NOLOCK) ON orp.nID = opp.nOrcPortID
LEFT OUTER JOIN bts_receiveport rp WITH(NOLOCK) ON opp.nReceivePortID = rp.nID
LEFT OUTER JOIN adm_ReceiveLocation rl WITH(NOLOCK) ON rp.nID = rl.receiveportId
LEFT OUTER JOIN bts_pipeline rppl WITH(NOLOCK) ON rl.ReceivePipelineId = rppl.Id
LEFT OUTER JOIN bts_sendport sp WITH(NOLOCK) ON opp.nSendPortID = sp.nID
LEFT OUTER JOIN bts_pipeline sppl WITH(NOLOCK) ON sp.nSendPipelineID = sppl.Id
LEFT OUTER JOIN bts_sendportgroup spg WITH(NOLOCK) ON opp.nSpgID = spg.nID
WHERE orc.nvcName IS NOT NULL
and rppl.Name like '%json%'
ORDER BY 1,2,3

— Status
— 1 = Unenlisted
— 2 = Stopped
— 3 = Started

NOTE: – above only works with orchestrations

If you don’t have an orchestration involved, I have created another a pair of queries to find send and receive ports that use a given pipeline.

When using SOAPUI to call a BizTalk published ochestration or schema, you might get this error response/XML from SOAP-UI:

Error

Main error text is:

The message could not be processed. This is most likely because
the action ‘http://Neal.Temp.WCFTargetNamespace/Outbound214Service/Send214Request’
is incorrect or because the message contains an invalid or expired security context token or because
there is a mismatch between bindings. The security context token would be invalid if the service aborted
the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase
the Receive timeout on the service endpoint’s binding

Solution Steps

Assuming no security is needed when first testing on your local machine.  You can tighten up the security after you get the basic test working.

  1. Modify the web.config file of the published webservice and set the authentication mode to “None”.
  2. Modify the BizTalk Receive Location created by the WCF publish utility, on the Security tab, set the “Security mode” to none.
  3. In SOAP-UI, click on the WS-A words (tab) below your request xml message.  Set the options as shown below:

References: https://adventuresinsidethemessagebox.wordpress.com/2012/10/22/calling-a-wcf-wshttp-service-from-soapui/

How do you find the hidden map name in the BiztalkMgmtDB (BizTalk Database)?

If you look at the table names, you will obviously find bt_MapSpec, but it doesn’t contain the map name. The map itself is hidden in the bts_Item table, which you have to join to. I found this on Jeroen Maes Integration Blog. He has a more complex query that finds a map based on the input/output target namespace. He also joins to the bts_Assembly table.

My goal was just to list all maps containing some sequence of letters (such as a customer name or abbreviation).


use BizTalkMgmtDb
select i.name, * from bt_MapSpec m
inner join bts_item i on m.itemid = i.id
where i.Name like '%ABC%' -- just the map name
-- where i.FullName like '%ABC%" -- optionally use the fully qualified name

I’m surprised there the Type column doesn’t seem to be populated with some number that indicates that the bts_Item is a map, or a schema, or whatever.

You can turn on message tracking in BizTalk at the Receive Port and Send Port level (as well as various levels in the orchestration).

Here are the views provided by the BizTalk install.


use biztalkDtaDb
--select * from TrackingData
--select 'btsv_Tracking_Fragments', * from btsv_Tracking_Fragments
select 'btsv_Tracking_Parts', * from btsv_Tracking_Parts
select 'btsv_Tracking_Spool', * from btsv_Tracking_Spool

To get the Send/Receive port or more info about the message, you need to join to dtav_MessageFacts. The idea for this join came from sample code here.


SELECT top 200
a.[Event/Direction],
a.[Event/Port],
a.[Event/URL],
CONVERT(VARCHAR(10), a.[Event/Timestamp], 111) as [date],
DATEPART(HOUR,a.[Event/Timestamp]) as [Hour],
imgPart,
imgPropBag
FROM [dbo].[dtav_MessageFacts] a
inner join dbo.btsv_Tracking_Parts b on a.[MessageInstance/InstanceID] = b.uidMessageID

Note: Later I added this line (not shown in picture). You have to use DataLength() instead of Len() to get the size of an image field.

DataLength(imgPart) as Length,

The body of the message is stored in imgPart of the btsv_Tracking_Parts view, but unfortuantely it's in Hex.
The following site is one that can convert the hex to ascii (might also need one to go to Unicode). https://www.rapidtables.com/convert/number/hex-to-ascii.html. Paste the hex data in the top, click convert, and your text will appear in the bottom half. You won't be able to see it all, but you can copy/paste to NotePad++ or some other editor.

While it's nice to use the built-in features of BizTalk when possible, they will typically have limitations compared to custom options. In a few places where I worked, we implemented our own "Trace" that writes data to a SQL trace table. We had our own concept of promoted fields to identify the trace, such as the location where the trace was capture (pipeline, orchestrations, before/after map, etc), a user type key, and a correlation token that can tie together traces across an entire business process.

See also: 3 ways of programmatically extracting a message body from the BizTalk tracking database (Operations DLL, SQL, and WMI). This article explains how Biztalk compresses the data with BTSDBAccessor.dll.

You get the following error when you do a BTDF deploy.

Error:

x does not belong to the same application as “Y” or its references.

Full example of error


Information: Importing bindings "C:\Users\...\Projects\MyEDIProject\MyEDIProject.Deployment\PortBindings.xml" into application "MyEDIProject" in BizTalk configuration database (server="MyServer1", database="BizTalkMgmtDb")...
Error: Failed to update binding information.
"Microsoft.BizTalk.Edi.DefaultPipelines.EdiSend" could not be bound to "sp_MyProj_Process_EDI_File". The artifact "Microsoft.BizTalk.Edi.DefaultPipelines.EdiSend" does not belong to the same application as "sp_MyProj_Process_EDI_File" or its references.

Solution:

The pipeline is in a different project. When BTDF deletes your project and redploys it, it loses the project references. You must add the project cross references in the BTDFProj file, as shown below.

<code>
<ItemGroup>
<AppsToReference Include=?MyProj2? />
</ItemGroup>
</code>

The above shows you how to add a “Project Reference” to your BTDF configuration file.

The one thing I didn’t account for (yet), is the case where different files can have different EDI delimiters. Technically, you should look for the end of the ISA segment to find the delimiters, and use those in the RegEx match. For now, I’m assuming the field delimiter is * and the segment delimiter is the tilda (~).

Requirement

I was archiving the EDI files in BizTalk with the filename set to “%datetime%_%MessageID%_EDI.txt”. I decided it would be better to name the files COMPANYNAME_DOCTYPE_ORDERNO_ORDERDATE_%datetime%_%MessageID%_EDI.txt.
NOTE: I could have done this logic in a custom C# BizTalk Pipeline, but decided to do it after the fact with a more simple Powershell than would be easier for administrative staff to maintain and update.

Sample 1 – Just test the parsing

With this sample, you can copy the contents of a file into the $ediText string, and test.

cls
#Note subsituted " with `" in the string to escape the quotes within quotes issue 
$ediText = "ISA*00*          *00*          *ZZ*MYCUSTOMER*ZZ*MYCOUNTRY*170823*1610*U*00401*000000117*0*T*:~GS*PO*BTS-SENDER*RECEIVE-APP*170823*1610*117*T*00401~ST*850*0117~BEG*00*NE*391949**20170828~N1*BY*DELIVERY-ADDRESS~N1*ST*DELIVERY-ADDRESS~N3*1420 MAINSTREET DR~N4*DALLAS*TX*12345~PO1*1*5.00*EA*4.350**IN*106889~PID*F****SAND MIX ( SSM80 )~PO1*2*1.00*etc...~"; 

$CompanyID  = [regex]::match($ediText,'.*ISA\*.*?\*.*?\*.*?\*.*?\*.*?\*(.*?)\*.*').Groups[1].Value
$OrderNum   = [regex]::match($ediText,'.*BEG\*.*?\*.*?\*(.*?)\*.*').Groups[1].Value
$OrderDate  = [regex]::match($ediText,'.*BEG\*.*?\*.*?\*.*?\*.*?\*(.*?)[~\*].*').Groups[1].Value
$EdiDocType = [regex]::match($content,'.~ST\*(.*?)[~\*].*').Groups[1].Value

Write-Host "CompanyID = $CompanyID"; 
Write-Host "OrderNum = $OrderNum"; 
Write-Host "OrderDate= $OrderDate"; 
Write-Host "EdiDocType= $EdiDocType"; 

Sample 2 – Renaming Files Based on EDI Key Fields

cls

$DirName = "d:\BizTalk\EDIHorizon\Archive\EDI850Order\"

#only rename files that start with the year, 2017, 2018, etc...  thus 20*.txt 
Get-ChildItem $Dirname -Filter 20*.txt | 
Foreach-Object {

    $fullname = $_.FullName.ToString();  
    $dirname = $_.Directory.ToString(); 
    $filename = $_.Name.ToString(); 

    Write-Host "OldName $fullname"
    $content = Get-Content $_.FullName

    $CompanyID  = [regex]::match($content,'.*ISA\*.*?\*.*?\*.*?\*.*?\*.*?\*(.*?)\*.*').Groups[1].Value
    $OrderNum   = [regex]::match($content,'.*BEG\*.*?\*.*?\*(.*?)\*.*').Groups[1].Value
    $OrderDate  = [regex]::match($content,'.*BEG\*.*?\*.*?\*.*?\*.*?\*(.*?)[~\*].*').Groups[1].Value
    $EdiDocType = [regex]::match($content,'.~ST\*(.*?)[~\*].*').Groups[1].Value
    Write-Host "$OrderNum $OrderDate"

    Write-Host "Filename=$filename"
    $newFileName = $dirname + "\" + $CompanyID + "_" + $EdiDocType + "_" + $OrderNum + "_" + $OrderDate  + "_" + $filename
    Write-Host "NewName $newFileName`n" 
    Rename-Item $fullname $newFileName 
}

Having a filename like this will make it faster to search the archives for certain types of orders or files from a certain partner, or do do quick counts, based on the filename alone. For example, how many files did we get from XYZ company yesterday and today? This could be done in BizTalk with BAM as well, but my current client opted out of the overhead and complexity of BAM, especially since BizTalk was (for the most part), just passing the files around, not creating them.

The variable $EdiDocType above represents something like and 850, 855, 856, 810, 997, etc…

I might add one more feature. Many of the trading partner don’t use name, but some Dun number, phone number, or other ID number. I might have a lookup table to translate the code to a shortname that represents that trading partner.

The underlying connection was closed: The connection was closed unexpectedly.

I’m sure there are numerous reasons for this error, but here’s one I encountered today.

First the background. We have BizTalk server connecting to an internal Open/AS2 server running on Linux. That Linux server was re-installed on a Virtual Machine; and I had agreed with the Linux guy to change the AS2-ID (defined in the Trading Parties) from BiztalkTest to BizTalkProd. However, he didn’t make the change on his side.

So when he looked at the logs, he could see that it was receiving data, but as he described it “the server didn’t know what to do with it.” That means, it didn’t generate the MDN and send it back to BizTalk.

I had defined my SendPort was a two-way (Static Solicit-Reponse) and was using content-based routing. So we sent the message to the AS2 server, and didn’t get a response back, giving the “underlying connection closed unexpectedly” error.