Question
· Sep 2, 2022

Using arrays in $system.external .NET gateway

I'm converting a .NET gateway interface (call .NET code from IRIS) from the old "Caché style" that use imported proxy class to the new $system.external .NET gateway as documented in:

https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?...

For simple case it works and I'm fine, unfortunately I don't understand how to implement/access .NET arrays from IRIS and unfortunately the documentation has no info whatsoever on how to work with arrays.

What I'm looking for is how to implement the same code as described in the old .NET gateway example (TestArrays() method (part 4) — insert array of Address objects) for arrays:

https://docs.intersystems.com/ens201815/csp/docbook/DocBook.UI.Page.cls?...

In particular, how this "old" code from the old documentation should be implemented using the new $system.external gateway:

Set addressArray=##class(%ListOfObjects).%New()
Do addressArray.Insert(home)
Do addressArray.Insert(home2)

Do test.setAddressArray(addressArray)

How .NET array can be populated from IRIS using the new $system.external .NET gateway?  
How .NET array can be accessed from IRIS using the new $system.external .NET gateway?

It would be nice and helpful to have the same %Net.Remote.DotNet.Test code implemented using the new gateway, as it is now the documentation is scarce....at best.

Has anyone ever used the new "InterSystems External Servers" .NET gateway?

Thank you,

Enrico

Product version: IRIS 2022.1
Discussion (11)2
Log in or sign up to continue

The "old code" you've posted should work without changes.  The main difference between the Object gateway and $system.external is in how you create the proxy. For example, to connect and create a "test" object:

set gateway = $system.external.getDotNetGateway()
do gateway.addToPath(myPath_"\DotNetGatewaySamples.dll")
set test = gateway.new("remote.test.Person")

Once you've created the proxy, it should work just as it always has.

Just thought I should mention that there is a way to use legacy gateway code completely as-is, by using the Recast proxy generator rather than $system.external syntax. This was developed for Ensemble productions, but it should work for any code that uses the old proxy generator (he says, sticking his foot in his mouth again. No, I haven't actually tried it, but...)

To generate Recast proxy classes, set the following global:
  set ^%SYS("Gateway","Remote","Recast",namespace)=1
or
  set ^%SYS("Gateway","Remote","Recast")=1
then re-import all the proxy classes. The first global governs Recast behavior for one namespace only while the second global governs Recast behavior for all namespaces that don't have their own setting. The import utility will automatically mark all the old-style proxy classes as out-of-date and re-import all the classes in the inheritance hierarchy.
All of the proxy classes have to be regenerated together. The Recast generator creates drop-in replacements for the old proxies, but it actually uses the new dynamic proxy technology under the hood, so they can't be mixed with the old Caché-style proxy classes.

This still begs the question of how to deal with arrays using $system.internal. I think you can round-trip just about any object by using inverse proxies on the .NET side. Can't go into that now because I'm supposed to be enjoying my vacation and my wife is giving me that look.

Hi Stuart,

thank you so much for your answer and effort, I feel sorry for your vacation!!

As mentioned, to keep things simple in my test instead of using my "real stuff" (somewhat more complex) I'm using the "old" test code %Net.Remote.DotNet.Test and corresponding .NET project that I have compiled with .NET Framework 4.5, this provides a simple testbed to test basic functionality.

Using the now undocumented legacy gateway the test works fine

Then, when I:

set ^%SYS("Gateway","Remote","Recast",namespace)=1

and reimport proxy classes, I can see that the generated proxy classes are different, so the "recast" had some effect.

Unfortunately running the test code....fail in the very first line:

USER>do ##class(%Net.Remote.DotNet.Test).Test(58555)
 
Errore #5023: Errore del gateway di Java: <ZJGTW>%dispatch+17^%Net.Remote.Base.1

It fails in the line (the first call to .NET class):

Set student=##class(remote.test.Student).%New(gateway,29,"976-01-6712")

So...neither this approach works...at all.

Thank you again,

Enrico

I realized the problem using "Recast", the gateway needs to be instantiated using the new gateway.

I had to fix a couple of things related to how the datatypes are casted and now it works.

However, this is not exactly what I was looking for, as mentioned my goal is to convert existing code that use the no longer documented "legacy gateway" to "new code" using the current (poorly) documented $system.external.

My goal is to move "forward" my code for current and future releases of IRIS.
Removing the need of the imported proxy classes also simplify the IRIS upgrade process.

Thank you again and enjoy your vacation!

Enrico
 

The solution in simple....if you know it:

 set addressArray=netGate.new("remote.test.Address[2]")
 do addressArray.%set(0, home)
 do addressArray.%set(1, home2)  set person = netGate.new("remote.test.Person")
 do person.setAddressArray(addressArray)
 set addressArray2=person.getAddressArray()
 for i=0:1:1 {
      set addr = addressArray2.%get(i)
      !, addr.city
 }
 

In addition to %set() and %get() method for arrays there are also %setall(), %getall() methods.

Hopefully these methods will be documented sometime in the future.

Enrico

Thank you! It looks like %set and %get were documented at one time, but got lost in the move to $system.external.

These methods work differently in each language. For example, here's how to build and access a 2-dimensional string array in DotNet, Java, and Python (you can run each example in the Terminal):

 //================================================================
 // DOTNET
 //================================================================
 set arr = $system.external.getDotNetGateway().new("string[2,3]")
 for x=0:1:1 {for y=0:1:2 {set chr=$CHAR(65+(x*3+y)) do arr.%set(x,y,chr) }}
 do arr.%set(1,1,"x") // change value(1,1) from E to X
 for x=0:1:1 {for y=0:1:2 {write !,"("_x_","_y_")="_arr.%get(x,y)_" " }}
 kill arr  

//================================================================
 // JAVA
 //================================================================
 set arr = $system.external.getJavaGateway().new("String[2][3]")
 for x=0:1:1 {for y=0:1:2 {set chr=$CHAR(65+(x*3+y)) do arr.%get(x).%set(y,chr) }}
 do arr.%get(1).%set(1,"X") // change value(1,1) from E to X
 for x=0:1:1 {for y=0:1:2 {write !,"("_x_","_y_")="_arr.%get(x).%get(y)_" " }}
 kill arr

 //================================================================
 // PYTHON
 //================================================================
 set gw = $system.external.getPythonGateway()
 set arr = gw.new("list")
 for x=0:1:1 {do arr.append(gw.new("list")) for y=0:1:2 {set chr=$CHAR(65+(x*3+y)) do arr.%get(x).append(chr) }}
 do arr.%get(1).%set(1,"X") // change value(1,1) from E to X
 for x=0:1:1 {for y=0:1:2 {write !,"("_x_","_y_")="_arr.%get(x).%get(y)_" " }}
 kill arr

Hi Enrico! Apologies for the late reply, but I wanted to add that if you are looking to use %ListOfObejcts on the IRIS side with an array on the .NET side, that's still possible. Here's a snippet of code as an example:

    set addressArray = ##class(%ListOfObjects).%New()
    do addressArray.Insert(home)
    do addressArray.Insert(home2)
	// create an array of Address on the .NET side, populated with values from addressArray
    set test.AddressArray = gateway.new("Samples.InterSystems.Gateway.Address[*]", addressArray)
	// get Address array from .NET, and populate %ListOfObjects with its values
    set addressArray = test.AddressArray.%getall()
    for i=1:1:addressArray.Count() {
        set home = addressArray.GetAt(i)
    }

I'm working on a larger example to demo the new gateway and will be posting that soon. It's the same one that Dan Pasco mentioned in This Comment. Hope this helps!

Hi Summer,

thank you for the information, since then I solved my issue and have used %setall()/getall() "magic" (secret? 😉 ) methods in a couple of cases (like streams), although I'm not sure I discovered all the magic! 

The real issue is the lack of documentation and samples, for me as well as for all the community.

In addition, InterSystems use (used?) to say that what is not documented is considered not (officially) supported....

Enrico