Pretty embarassing result for me. I misinterpretted how ObjectScript treats equals(=) operator when used with a write and thus lost a lot of marks.  Its never something I've come across but now I am well and truely informed. Good article. From a support perspective, I often have to evaluate expressions in the terminal to check my understanding is correct. It can be mighty fustrating if you spend all day assuming an expression is interpretted one way, only to find your assumption to be incorrect! Agree with Avoid ambiguous or easily misinterrepted code. Also use brackets, they are your friends.

Another great resource is the REST and Relaxation webinar.  If your external service returns the above JSON body, you can work with the JSON directly. 

Get the JSON from the HTTP Request Body

    ClassMethod GetJSONData() As %DynamicObject
    {
        // Throw our own exception if the request body does not contain valid JSON.
        Try {
            Set obj = ##class(%DynamicObject).%FromJSON(%request.Content)
        } Catch ex {
            Throw ##class(MyProject.Exception.BadRequest).%New("Invalid JSON")
        }
        Quit obj
    }

Call the method from within your %CSP.REST class. The URL and HTTP method will be your event trigger.

Class MyProject.Patient Extends %CSP.REST

 <Routes>
            <Route Url="/postPatient" Method="Post" Call="PostPatient" />
 </Routes>

ClassMethod PostPatient() As Status
{
   Set formData = ##class(MyProject.API.Service).GetJSONData()   
  
   // Get the tag element
  Set tag = formData.tag

  // Loop through a JSON Array
  Set siteIter = formData.patient.site.%GetIterator()
 
  if ( $GET(siteIter)'=""
  {
            while(siteIter.%GetNext(.key, .value)
            {
                  Set code = value.code
                  Set mrn = value.mrn
            }
  }
return $$$OK
}


The syntax here may not be 100% correct but I hope this gives you some ideas. Try to keep your JSON object as simple as possible

When you get the information from the JSON body you can decide whether to create a new object or modify an existing object using the %Persistent methods %OpenId() %ExistsId() and %New(). I would model your classes based on how you wish to store the data contained within the HTTP request rather than the request itself.  If you are able to obtain information from the request itself eg. "tag: post" then you probably do not need to store that in the database.

You can set the response status you wish to return by

Set %response.Status = ..#HTTP400BADREQUEST   

 so you may not need to encode things in JSON that are part of the request.
 

I use AutoMapper and NewtownsoftJson .NET libraries for mapping JSON to C# objects/models and work with the JSON and ObjectScript classes directly when saving JSON data to the database.

Update: There's some useful documentation in the Using JSON in Caché Guide, including JSON Serialization and de-serialization to dynamic entities using %FromJSON and %ToJSON methods

. They are two different platforms and is most likely why you could not find ConfigureServices in the Intersystems docs.  For a Micosoft SQL Server, your ConfigureServices method could have

            services.AddDbContext<MyMSDBContext>(cfg =>
            {

                cfg.UseSqlServer(_config.GetConnectionString("MyConnectionString"));
            });

but I haven't seen any examples in Caché! Perhaps there is a development opportuntiy to write a NuGet package? Here's a Microsoft link to other popular database providers supported within .NET Core.

PS. If you use RESTful HTTP methods to talk to the database then you don't need a database provider as the data can be returned as part of the HTTP Response.

My understanding is that the Globals API namespace reference using Intersystems.Globals; is bundled  with the InterSystems.CacheExtreme.dll which relies on the traditional .NET Framework ie. not .NET Core.  Some Global API documentation recommends you use both DLLs mentioned in the question description above.

I am currently developing a .NetCore 2.1 MVC Web Application that uses Bootstrap 4, Datatables.net,  Newtownsoft Json Serailizer (AutoMapper is also good), Moment.js, Tempus Dominus Bootstrap 4 Themed Datepicker,  Caché ObjectScript %Persistent classes mapped to Globals (see The Art of Mapping Globals Series), Cache ObjectScript %CSP.REST classes for mapping web api calls (see Rest and Relatation Demo) and basic authentication over HTTPS. Using this development stack, I do not need to generate proxy classes using the Caché Object Binding Wizard and I have no need for either DLL but I do not have direct access to globals from my .NET classes. The backend classmethods work at the class/object level in response to HTTP requests (Web API calls) to manipulate Global values in the database.

If you have never used Wireshark before or haven't a deep understanding of the TCP/IP suite of protocols then Wireshark might be overkill for your needs.  I have only used it in a Lab or Development environment. You also have to consider the disk storage requirements and governance issues around full-packet capture eg. HPIAA, PCI-DSS. You might want to check  Eduard Lebedyuk's article on Debugging Web for additional tools and tips.

For troubleshooting these issues, we enable a monitoring global that records every character recieved within the TCP Stream

START 
  do USE read *CHAR:2 else  do WAIT
  do CHARMONITOR
 goto START
CHARMONITOR
 if ^AC=1 CHAR'=-1 set X=^AC1,^AC1(X+1)="*"_$char(CHAR)_"*"_CHAR_"*",^AC1=X+1 ; Monitor character by character
 set ^CALLED("CHARMON")=$ZD(+$H,4)_"*"_$ZT($P($H,",",2))_"*"_CHAR
 quit

You can try a re-connect using $ZTRAP or try-catch. In this example, we do a maximum of 10 re-connect attempts

 set $ZT="ERROR"
ERROR
 if ($ZE["<READ>")
 {
     set ERRORCOUNT=ERRORCOUNT+1
     if (ERRORCOUNT<11)
     {
         set ^TCPLOG("ERROR",$ZD(+$H,4),$ZT($P($H,",",2)),ERRORCOUNT)=$ZE
         close "|TCP|"_PORT
         hang 30 goto OPEN 
      }
      else do ^%ET }
 }
 else  do ^%ET }
 quit

You might also look at extending the TCP timeout value to see if that makes a difference to the volume of errors. Check out this Ensemble Example of a SOAP Web Service

It's a real-world scenario where you can have something as true, false or undefined. In C# .NET you might use bool? type for this.  If you are populating a checkbox HTML element, you will have to consider  the three possible outcomes. Since I'm mapping classes to pre-existing global structures it's important to understand how the object properties map to these pre-existing global structures.

Ok so lets kill off the AKI node in the global and instansiate the object by passing in the IDKEY

k ^CODE("TNO","BIO",291,"AKI")
set myobject= ##class(Code.TestDetails).%OpenId("BIO||291")

If you do if (myobject.AKI="") what are you really asking:

1) if ($GET(^CODE("TNO","BIO",291,"AKI"))="")
2) if ($DATA(^CODE("TNO","BIO",291,"AKI"))=0)

I think it is option 1 and I was wondering if there was a $DATA operation that would work at the object level. I see %DynamicObject has a %IsDefined("propertyName") method but this is more often seen in parsing JSON objects and used to detect { "AKI": null }

Perhaps this is what I'm looking for:

Set isNullBool = ##class(Code.TestDetails).%ObjectIsNull(myobject.AKI)
zwrite isNullBool

Any other thoughts?

Rather than pass the traverseRelationships Boolean to the instance method toJSON(traverseRelationships) in User.Widget, could you change URL map/URL route to use a QueryString parameter? 
eg 1. http://{{SERVER}}:{{WEB_PORT}}/widgetsdirect/rest/Stephen?traverseRelationships=true
eg2.
http://{{SERVER}}:{{WEB_PORT}}/widgetsdirect/rest/Stephen?traverseRelationships=false

How do you map URL querystring parameters to the %CSP.REST class in HTTP Get Requests?

I think Kerberos is only required if you a connecting to a MS SQL Server database from a non-Windows environment using the JDBC driver.  If you are in a Windows environment you will probably use NTLMv2. SQL Squirrel can be used for troubleshooting connection strings or you can write a simple console based Java application. You might also check the account outside of using JDBC using SQL Server Management Studio. You need to verify your system requirements are correct for the JRE/JDK/JDBC version and the MS SQL Server version. Depending on your version requirements the connection string will vary.    

Consider the line in C#

CACHEObject.ContainerImco containerImco = new CACHEObject.ContainerImco(cacheConnection);

Does this 'ContainerImco' object have a constructor that takes in a CacheConnection object?  Have you opened the connection?

From the error it looks like its failing when trying to get the connection. It seems a bit weird to have a collection of CacheConnection objects.

Your code is quite difficult to read without proper styling. I recommend the 'Special Container'. We created a DLL from a c# class library generated from the .NET Object Binding Wizard and placed the DLL in our bin/ folder.

Assuming lcontainerImco.Connection is a CacheConnection object from 'Intersystems.Data.CacheClient'  and you have imported 'Intersystems.Data.CacheTypes'. The following should work.

CacheConnection CacheConnect = new CacheConnection();
CacheConnect.ConnectionString = server + "Port = " + connectionPort + "; " + "Namespace = USER; "
+ "Password = " + password + ";" + "User ID = " username + "; " + "pooling = false;";
CacheConnect.Open();

There is also a little known issue about certain special characters in the password causing parsing problems, which is identifiable from the following stack trace:

at InterSystems.Data.CacheClient.CacheADOConnection.createConnectionKeyString()\r\n   at
InterSystems.Data.CacheClient.CacheADOConnection.ParseConnectionStringInternal(String connectionStri
ng)\r\n   at InterSystems.Data.CacheClient.CacheADOConnection.set_ConnectionString(String value)\r\n

Bad characters in the password include pairing and delimiting characters eg.  equals sign, single-quotes and backslashes, pound sign symbol and not symbol. This was reported to WRC two years ago but never resolved, so we created a work-a-round solution.  

Refer to the documentation relevant to your Caché version for all the valid connection string parameters (I have linked the latest release) and be sure to wrap your connection in a try-catch structure.  

You would probably get a syntax error if LD resolved to an empty string ("").  I would put a trace/watchpoint on LD or alternatively log its contents to a global to verify it is not null. You might also want to check the definition #define LDAPServer $Get(^OSUMCLDAP("Server")) to see if it is correct and verify whether it should or should not contain a port number.

Here's another sample SimpleBinds() operation using server ldapserver1.mycoolcompany.com port 51000

/// Return 1 if LDAP SimpleBind successful.
/// Return 0 if LDAP SimpleBind unsuccessful
ClassMethod Connect(ByRef Username As %String, ByRef Password As %String) As %Boolean {
   set userContext = "uid="_Username_",ou=aixuser,cn=aixsecdb,cn=aixdata,o=mycoolcompany_aix"
   set ldapConnection = ##class(%SYS.LDAP).Init("ldapserver1.mycoolcompany.com",51000)
   set status =##class(%SYS.LDAP).SimpleBinds(ldapConnection,userContext,Password)
   if (status=$$$LDAPSUCCESS)
   {
      write !, "LDAP SimpleBind Successful!"
      return 1
    }
   else
   {
       write !,"LDAP SimpleBind failed!"
       write !,"Error code : ",status
       write !,"Error message : ",##Class(%SYS.LDAP).Err2String(status)
       return 0
    }
}

The Init() method documentation has some good troubleshooting tips under 'Error Codes' for connecting using SSL/TLS