Passing IEnumerable variables to .NET from ColdFusion

I am working on integrating ColdFusion 10 with a custom .NET DLL that I do not have to configure. During this process, I was able to do everything I needed, except to create an IEnumerable data type, to go to one of the object's methods. Here is what I need to integrate with:

enter image description here

This is the Set_Events method I'm having problems with. I can create separate Events that should be part of this enumeration variable, but I cannot create what, apparently, expects as the correct type of the variable. I tried to do this:

 <cfset enum = createObject(".net", "System.Collections.Generic.IEnumerable__1") /> 

And this gives me a valid .NET object using the GetEnumerator() method:

enter image description here

When I try to call this method:

 <cfdump var="#enum.GetEnumerator()#"> 

It just gives me the following error:

 The GetEnumerator method was not found. 

Other things I've tried:

Creating a shared list and adding things:

 <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", "dotNetCoreProxy.jar") /> <cfset eventList.Add(javacast("bigdecimal", "30.1234" )) /> 

This gives me the following error:

 An exception occurred while instantiating a Java object. The class must not be an interface or an abstract class. If the class has a constructor that accepts an argument, you must call the constructor explicitly using the init(args) method. Error : System.Collections.Generic.List__1 

And this again gives me the same error:

 <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", "dotNetCoreProxy.jar") /> <cfset eventList.Add("foo") /> 

Attempt to initialize a list

This code from Leigh works:

 <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", "dotNetCoreProxy.jar") /> <cfset elemClass = createObject(".net", "System.String", "dotNetCoreProxy.jar") /> <cfset elemType = elemClass.getDotNetClass() /> <cfset eventList.init( elemType ) /> <cfset eventList.Add("foo") /> <cfdump var="#eventList#"> 

But this is not so:

 <cfobject type="dotnet" name="VideoWallEvent" class="Utilities.VideoWall.VideoWallEvent" assembly="#ExpandPath("Utilities.dll")#"> <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", "dotNetCoreProxy.jar") /> <cfset elemType = VideoWallEvent.getDotNetClass() /> <cfset eventList.init( elemType ) /> <cfdump var="#eventList#"> 

I get the following error:

 Unable to find a constructor for class System.Collections.Generic.List__1 that accepts parameters of type ( System.RuntimeType ). 

JNBProxyGUI.exe

It was completely useless. I can go to my Utilities.dll file and add the System.Collections assembly from the GAC, but the Build option in the Project menu is always disabled and nothing is displayed on any panel after adding these assemblies.

Test methods

I ran all of the following code on one page:

 <cfset UtilitiesProxy = ExpandPath("UtilitiesProxy.jar") /> <cfset DotNetCoreProxy = "dotNetCoreProxy.jar" /> <cfset CoStarUtilities = ExpandPath("CoStar.Utilities.dll") /> <h2>Try using base DLLs and DotNetCore</h2> <cftry> <cfobject type="dotnet" name="VideoWallEvent" class="CoStar.Utilities.VideoWall.VideoWallEvent" assembly="#CoStarUtilities#"> <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", DotNetCoreProxy) /> <cfdump var="#eventList.init(VideoWallEvent.getDotNetClass())#"> (error line) <cfcatch> <h3><cfoutput>#cfcatch.type#</cfoutput></h3> <p><cfoutput>#cfcatch.message#: #cfcatch.detail#</cfoutput></p> </cfcatch> </cftry> <h2>Try using the Utilities Proxy for Everything</h2> <cftry> <cfobject type="dotnet" name="VideoWallEvent" class="CoStar.Utilities.VideoWall.VideoWallEvent" assembly="#UtilitiesProxy#"> <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", UtilitiesProxy) /> (error line) <cfdump var="#eventList.init(VideoWallEvent.getDotNetClass())#"> <cfcatch> <h3><cfoutput>#cfcatch.type#</cfoutput></h3> <p><cfoutput>#cfcatch.message#: #cfcatch.detail#</cfoutput></p> </cfcatch> </cftry> <h2>Try using Utilities Proxy for VideoWall, and DotNetCore for List</h2> <cftry> <cfobject type="dotnet" name="VideoWallEvent" class="CoStar.Utilities.VideoWall.VideoWallEvent" assembly="#UtilitiesProxy#"> <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", DotNetCoreProxy) /> <cfdump var="#eventList.init(VideoWallEvent.getDotNetClass())#"> (error line) <cfcatch> <h3><cfoutput>#cfcatch.type#</cfoutput></h3> <p><cfoutput>#cfcatch.message#: #cfcatch.detail#</cfoutput></p> </cfcatch> </cftry> <h2>Try Initing Wall Event</h2> <cftry> <cfobject type="dotnet" name="VideoWallEvent" class="CoStar.Utilities.VideoWall.VideoWallEvent" assembly="#CoStarUtilities#"> <cfset VideoWallEvent.Init() /> <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", DotNetCoreProxy) /> <cfdump var="#eventList.init(VideoWallEvent.getDotNetClass())#"> (error line) <cfcatch> <h3><cfoutput>#cfcatch.type#</cfoutput></h3> <p><cfoutput>#cfcatch.message#: #cfcatch.detail#</cfoutput></p> </cfcatch> </cftry> <h2>Try Initing Wall Event</h2> <cftry> <cfobject type="dotnet" name="VideoWallEvent" class="CoStar.Utilities.VideoWall.VideoWallEvent" assembly="#CoStarUtilities#"> <cfset VideoWallEvent.Init() /> <cfset eventList = CreateObject(".net","System.Collections.Generic.List`1", DotNetCoreProxy) /> <cfdump var="#eventList.init(VideoWallEvent)#"> (error line) <cfcatch> <h3><cfoutput>#cfcatch.type#</cfoutput></h3> <p><cfoutput>#cfcatch.message#: #cfcatch.detail#</cfoutput></p> </cfcatch> </cftry> 

And I get the following error output (hint, each of which does not work).

 09:22:45.045 - Object Exception - in C:/inetpub/LandsofAmerica/scribble/VideoWall/index.cfm : line 9 Unable to find a constructor for class System.Collections.Generic.List__1 that accepts parameters of type ( System.RuntimeType ). 09:22:48.048 - coldfusion.runtime.dotnet.ProxyGenerationException - in C:/inetpub/LandsofAmerica/scribble/VideoWall/index.cfm : line 19 09:22:48.048 - Object Exception - in C:/inetpub/LandsofAmerica/scribble/VideoWall/index.cfm : line 31 Unable to find a constructor for class System.Collections.Generic.List__1 that accepts parameters of type ( System.RuntimeType ). 09:22:48.048 - Object Exception - in C:/inetpub/LandsofAmerica/scribble/VideoWall/index.cfm : line 43 Unable to find a constructor for class System.Collections.Generic.List__1 that accepts parameters of type ( System.RuntimeType ). 09:22:48.048 - Object Exception - in C:/inetpub/LandsofAmerica/scribble/VideoWall/index.cfm : line 55 Unable to find a constructor for class System.Collections.Generic.List__1 that accepts parameters of type ( CoStar.Utilities.VideoWall.VideoWallEvent ). 

I created a proxy with (as it seems to me) all the items that I need.

JNBProxyGUI

Conclusion

So now I'm stuck. I can create an instance and fill in all the necessary objects for this to work, I just cannot combine all the objects for them to work. What am I missing when trying to create the ENum object needed to populate the Set_Events method?

+6
source share
2 answers

As already mentioned, you need to use a specific class (not an interface). However, looking at the signature of a method, it should be one that implements System.Collections. Generic .Inumerable , not System.Collections.IEnumerable . The latter applies only to non-general collections. One example of a general collection that you can use is List<T> , where <T> is the type of object that the list has. (You will need to look at your API to determine the type of object).

Unfortunately ... there is a problem with common classes . In fact, the tool used by createObject cannot generate proxies for Generics. (The blog entry says that it was resolved in CF9, but in my tests I ran into the same problem with 9.0.1 - YMMV.) So you need to do this yourself using JNBProxyGUI.exe . Honestly, this is not the most intuitive tool. But after a little argument, I managed to get it to work under CF9. Fortunately, this is just a one-time event.

After exporting the generated proxies to the jar file, use the example in the blog post to create a generic List . Just add the proxy jar to the build list. Let's say the collection stores simple Strings :

  // create a generic list of strings ie new List<String>() path = "c:/path/yourGenericsProxy.jar,c:/path/yourApplication.dll"; list = createObject(".net", "System.Collections.Generic.List__1", path); elemClass = createObject(".net", "System.String", path); elemType = elemClass.getDotNetClass(); list.init( elemType ); 

After initializing the List you can add several elements to it:

  list.add( "foo" ); list.add( "bar" ); 

Then finally call GetEnumerator () and pass it to your method Then pass List to your method:

  // wrong: yourObject.Set_Events( list ); yourObject.Set_Events( list ); 

(If you have questions about creating a proxy, just ask.)


UPDATE:

As Dan pointed out, his method uses IEnumerable not IEnumerator. Therefore, the List itself should be passed to set_Event , not Get_Enumerator() (as in the previous example). In addition, when you call createObject , the collector MUST include both the proxy bank and the dll file . Otherwise, the method call may fail. It turns out that this caused later problems.

Below is the revised version that works with CF10.

Regenerated proxy byte

  • Open JNBProxyGUI.exe and select Create new Java -> .NET Project
  • Enter your local java settings (My Settings)
    • Remote host / port: localhost 6089
    • Java path: C: \ ColdFusion10 \ cfusion \ jetty \ jre \ bin \ java.exe
    • jnbcore.jar: C: \ ColdFusion10 \ cfusion \ lib \ jnbcore.jar
    • bcel.jar: C: \ ColdFusion10 \ cfusion \ lib \ becel-5.1-jnbridge.jar
  • Click Project > Edit Assembly List > Add . Find and select "mscorlib.dll"
  • Click Project > Add Classes from Assembly File . Locate and select "mscorlib.dll" (then gui generates a list of classes. This may take some time).
  • In the Environment list, select the System.Collections.Generic package and click Add .
  • Choose Edit > Check All in Exposed Proxies
  • Choose Project > Build and select the path and file name for the new proxy

Cleaning:

To be safe, I stopped CF and deleted all created proxy banks from WEB-INF\cfclasses\dotNetProxy except dotNetCoreProxy.jar. Then restarted CF and run the code. It worked great. Line. (See full code below).


Myclass.cs

 using System; using System.Text; using System.Collections.Generic; namespace MyLibrary { public class MyClass { private IEnumerable<CustomClass> events; public void set_Events(IEnumerable<CustomClass> evts) { this.events = evts; } public IEnumerable<CustomClass> get_Events() { return this.events; } } } 

CustomClass.cs

 using System; using System.Text; using System.Collections.Generic; namespace MyLibrary { public class CustomClass { private string title; public CustomClass(string title) { this.title = title; } public string getTitle() { return this.title; } public override string ToString() { return getTitle(); } } } 

CF Code:

 <cfscript> // MUST include both proxy jar and DLL file path = arrayToList([ExpandPath("./MyLibrary.dll"), ExpandPath("genericListAndEnumerator.jar")]); //initialize custom class customObj = createObject(".net", "MyLibrary.CustomClass", path).init("Blah, blah"); elemType = customObj.getDotNetClass(); // create generic list of custom class list = CreateObject(".net","System.Collections.Generic.List__1", path); list.init( elemType ); list.add( customObj ); // test setter mainObj = createObject(".net", "MyLibrary.MyClass", path); mainObj.set_Events( list ); // test getter enum = mainObj.get_Events().getEnumerator(); writeDump(enum); while (enum.MoveNext()) { WriteDump("Current="& enum.Get_Current().toString()); } </cfscript> 
+7
source

The problem is that you are trying to call an interface method. Instead, you need to instantiate a class that implements this interface in order to pass it to Set_Events() . The following is a list of .NET classes that implement IEnumerable: IEnumerable Interface .

+1
source

Source: https://habr.com/ru/post/945801/