1. Arcady Abramov
  2. PowerBuilder
  3. Sunday, 12 April 2020 05:35 AM UTC

Hello, support

In my previous questions I was wondering how to utilize 3rd party .NET assemblies, which require returning and passing complex data types.

In the responses I saw suggestions to create .NET wrappers for these assemblies. I am not sure I understand what it means.

If in order to send and receive HTTP request/response I need to first create a request class, which contains headers dictionary and receive a response with a complex class, how am I supposed to "wrap" it? Into what, exactly.

Currently, it looks like the new .NET consumption functionality contains less options that OLE object. In the OLE, I could pass and receive complex classes, here I cannot.

Thank you

Arcady

Ricardo Jasso Accepted Answer Pending Moderation
  1. Tuesday, 14 April 2020 17:42 PM UTC
  2. PowerBuilder
  3. # 1

Arcady,

This is an example of a .NET wrapper for a class that has a complex property (the code is in VB.NET):

Public Class Class1 ' Third party class

Property Prop1 As Integer
Property Prop2 As Integer

Public Function Sum() As Integer

Return Prop1 + Prop2

End Function

End Class

Public Class Class2 ' Third party class

Property Prop3 As Integer
Property Prop4 As New Class1

Public Function Sum() As Integer

Return Prop3 + Prop4.Sum()

End Function

End Class

Public Class Class2W ' Wrapper class for Class2

Public C2 As New Class2

Property Prop3 As Integer

Get

Return C2.Prop3

End Get

Set(value As Integer)

C2.Prop3 = value

End Set

End Property

Property Prop4_Prop1 As Integer

Get

Return C2.Prop4.Prop1

End Get

Set(value As Integer)

C2.Prop4.Prop1 = value

End Set

End Property

Property Prop4_Prop2 As Integer

Get

Return C2.Prop4.Prop2

End Get

Set(value As Integer)

C2.Prop4.Prop2 = value

End Set

End Property

Public Function Sum() As Integer

Return Prop3 + C2.Prop4.Sum()

End Function

End Class

This is how Class1 and Class2W (the wrapper class for Class2) are used in PowerBuilder:

DotNetAssembly ldn_assembly
ldn_assembly = Create DotNetAssembly

String ls_dll
ls_dll = "C:\Datos\VB.NET2017\TEST2\bin\Debug\Test.dll"

// Load assembly
Long ll_return
ll_return = ldn_assembly.LoadWithDotNetFrameWork(ls_dll)

// Check for errors
If ll_return < 0 then
	Messagebox("Load Assembly", "Return: " + string(ll_return) + "~r~rLoad of "+ls_dll+" failed.~r~r" + ldn_assembly.ErrorText)
	Return
End if

// Declare and create DotNetObject #1
DotNetObject ldn_obj1
ldn_obj1 = Create DotNetObject

// Create instance of Class1 and bind it to DotNetObject #1
ll_return = ldn_assembly.CreateInstance ("Test.Class1", ldn_obj1)

// Check for errors
If ll_return < 0 then
	Messagebox("Create Instance", "Return " + string(ll_return) + "~r~rInstance creation of Test.Class1 failed.")
	Return
End if

// Assign values to properties
ldn_obj1.Prop1 = 10
ldn_obj1.Prop2 = 20

// Perform sum
long ll_s1
ll_s1 = ldn_obj1.Sum()
MessageBox("Sum1", ll_s1) // 30

// Declare and create DotNetObject #2
DotNetObject ldn_obj2
ldn_obj2 = Create DotNetObject

// Create instance of Class2W and bind it to DotNetObject #2
ll_return = ldn_assembly.CreateInstance ("Test.Class2W", ldn_obj2)

// Check for errors
If ll_return < 0 then
	Messagebox("Create Instance", "Return " + string(ll_return) + "~r~rInstance creation of Test.Class2W failed.")
	Return
End if

// Assign values to properties
ldn_obj2.Prop3 = 50
ldn_obj2.Prop4_Prop1 = 100
ldn_obj2.Prop4_Prop2 = 200

// Perform sum
long ll_s2
ll_s2 = ldn_obj2.Sum()
MessageBox("Sum2", ll_s2) // 350
Comment
  1. Arcady Abramov
  2. Wednesday, 15 April 2020 04:21 AM UTC
Thank you, Ricardo

This would work for very simple classes, but our interfaces (which currently work with OLE object) are much more complex. We use them for fiscalzation and credit card processing, so they have dozens of complex properties and functions. So, the approach you stated is not cost-effective. Unless we find a simple way to convert a generic class into a JSON string and vice-versa, we will have to stay with OLE.
  1. Helpful
  1. Ricardo Jasso
  2. Wednesday, 15 April 2020 16:12 PM UTC
The example I provided answers you question about a .NET wrapper for a simple class. Remember that a simple class, that is a class with one or two properties, is still considered a complex data type, one that cannot be handled by the new PowerBuilder DotNetAssembly and DotNetObject objects, and in consequence nor by the .NET Library importer. That the solution might or might not be feasible, it depends of course on the complexity of the class you want to handle which if big will require a long .NET wrapper.

Now, the proposal of serializing a class to a XML or JSON string and deserializing it back to an NVO does not make much sense to me IF you still need to access the methods or functions within the class. You will solve the "data" part but not the "methods or functions" part. We are not talking here about Web APIs but COM/.NET Interop which is different.

So, my advise is that for now and until Appeon extends the functionality of the DotNet objects and .NET Importer, you should continue using the OLE path, that is, the COM Callable Wrapper path. Bruce Armstrong has an excelent and very detailed video loaded in PowerBuilder TV that you can watch to understand better this subject. Here is the link: https://www.powerbuildertv.com/index.php/en/component/content/article/9-recording/206-using-net-assemblies-with-powerbuilder-recording
  1. Helpful
  1. Ricardo Jasso
  2. Thursday, 16 April 2020 17:05 PM UTC
I just added an enhacement request at the support portal to add the ability to handle complex data types for properties and arguments to functions. Hope it gets included in 2019 R3.
  1. Helpful
There are no comments made yet.
Arcady Abramov Accepted Answer Pending Moderation
  1. Tuesday, 14 April 2020 10:33 AM UTC
  2. PowerBuilder
  3. # 2

After several people answered in several threads I still do not understand:

If there is no way to serialize a generic class to JSON string and unserialize a string to a generic class, then what does everyone mean when they say ".NET wrappers"

 

Can anyone provide and example to a wrapper for the simplest function:

Result1 = DotNessClass1.function1(Request1)

where request 1 is a class with the following properties: string prop1, string prop2  

and Result1 is a class with the following properties: string prop3, string prop4

 

??

 

Comment
There are no comments made yet.
Bruce Armstrong Accepted Answer Pending Moderation
  1. Tuesday, 14 April 2020 05:35 AM UTC
  2. PowerBuilder
  3. # 3

Until such time as they address that by adding support for structs or classes in function argument/returns, what I might suggest is packaging the data in a format such as XML or JSON and passing it as a string.

Comment
  1. Bruce Armstrong
  2. Tuesday, 14 April 2020 14:13 PM UTC
There is a JSONParser and JSONGenerator in PowerBuilder, but it's not going to do it for you automagically. You'd have to code it.
  1. Helpful
  1. Arcady Abramov
  2. Tuesday, 14 April 2020 14:41 PM UTC
All I saw in help are DW examples.

I saw nothing about NVOs. Are there any examples for NVO?
  1. Helpful
  1. Bruce Armstrong
  2. Tuesday, 21 April 2020 01:39 AM UTC
Here's an example that uses a rather simple class. Note that you have to put the JSON together and parse it manually in PowerBuilder. As far as I know, PowerBuilder doesn't have the reflection capability necessary to dynamically access the variables off a non visual class.



C# Code:



public class ComplexTypeDemo

{



ComplexType complexType;



public int PassIn(string json)

{

this.complexType = JsonConvert.DeserializeObject<ComplexType>(json);

return 1;

}



public String PassOut()

{

return JsonConvert.SerializeObject(this.complexType);

}



}



public class ComplexType

{

public string stringValue;

public int intValue;

public DateTime dateTimeValue;

}



PowerBuilder Code:



PASSIN:



string ls_json

long ll_objecthandle

JsonGenerator jg

nvo_complextype cp



cp.stringValue = 'This is a string'

cp.intValue = -7

cp.dateTimeValue = DateTime ( Today(), Now() )





jg = Create JsonGenerator



ll_objecthandle = jg.createjsonobject( )

jg.additemstring( ll_objecthandle, 'stringValue', cp.stringValue )

jg.additemnumber( ll_objecthandle, 'intValue', cp.intValue)

jg.additemdatetime( ll_objecthandle, 'dateTimeValue', cp.dateTimeValue)



ls_json = jg.getjsonstring( )



Destroy jg



inv_complextypedemo.of_passin( ls_json )



PASSOUT:



string ls_json

long ll_objecthandle

JsonParser jp

nvo_complextype cp



ls_json = inv_complextypedemo.of_passout()



jp = Create JsonParser

jp.loadstring( ls_json )

ll_objecthandle = jp.getrootitem( )

cp.stringValue = jp.GetItemString ( ll_objecthandle, 'stringValue' )

cp.intValue = jp.getitemnumber( ll_objecthandle, 'intValue' )

cp.dateTimeValue = jp.getitemdatetime( ll_objecthandle, 'dateTimeValue' )



Destroy jp







  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.