从ColdFusion将IEnumerable变量传递给.NET

Dan*_*ort 6 .net coldfusion ienumerable coldfusion-10

我正在尝试使用我无法调整的自定义.NET DLL进行一些ColdFusion 10集成.在这个过程中,除了创建一个IEnumerable数据类型以传递给对象的一个方法之外,我已经能够做我需要做的所有事情.这是我需要集成的内容:

在此输入图像描述

Set_Events是我遇到麻烦的方法.我可以创建应该属于该枚举变量的单个事件,但是我无法创建它显然期望的适当变量类型.我试过这样做:

<cfset enum = createObject(".net", "System.Collections.Generic.IEnumerable__1") />
Run Code Online (Sandbox Code Playgroud)

这给了我一个有效的.NET对象,GetEnumerator()方法是:

在此输入图像描述

当我尝试调用该方法时:

<cfdump var="#enum.GetEnumerator()#">
Run Code Online (Sandbox Code Playgroud)

这只是给我以下错误:

The GetEnumerator method was not found.
Run Code Online (Sandbox Code Playgroud)

我试过的其他事情:

创建通用列表并添加内容:

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

这给了我以下错误:

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
Run Code Online (Sandbox Code Playgroud)

这再次给了我同样的错误:

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

试图初始化列表

Leigh的这段代码作品:

<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#">
Run Code Online (Sandbox Code Playgroud)

但这不是:

<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#">
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

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

JNBProxyGUI.exe

这完全没用.我可以浏览到我的Utilities.dll文件,并从GAC添加System.Collections程序集,但是项目菜单中的"构建"选项始终处于禁用状态,并且一旦添加了这些程序集,任何窗格中都不显示任何窗格.

测试方法

我在一个页面上运行以下所有代码:

<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>
Run Code Online (Sandbox Code Playgroud)

我得到以下错误输出(提示,其中每一个都失败).

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 ).
Run Code Online (Sandbox Code Playgroud)

我已经生成了一个代理(我认为是)我需要的所有项目.

JNBProxyGUI

结论

所以现在我被卡住了.我可以实例化并填充所有必要的对象来使这个东西工作,我只是无法将所有对象推到一起使它们工作.尝试创建填充Set_Events方法所需的ENum对象时,我错过了什么?

Lei*_*igh 7

如前所述,您需要使用具体类(而不是接口).但是,查看方法签名,它必须是实现System.Collections的方法签名.Generic .IEnumerable,而不是System.Collections.IEnumerable.后者仅适用于非泛型集合.您可以使用的通用集合的一个示例是List<T>,<T>列表所包含的对象类型在哪里.(您需要检查您的API以确定对象类型).

不幸的是...... Generic类存在问题.本质上,使用的工具createObject无法为泛型生成代理.(博客条目说它已在CF9中解决,但在我的测试中我遇到了与9.0.1 - YMMV相同的问题.)所以你需要自己使用JNBProxyGUI.exe来完成.坦率地说,它不是最直观的工具..但经过一番争吵后,我设法让它在CF9下工作.幸运的是,这只是一次性事件.

将生成的代理导出到jar文件后,使用博客条目中的示例创建泛型List.只需将代理jar添加到程序集列表中即可.假设Collection存储简单的字符串:

    // 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 );
Run Code Online (Sandbox Code Playgroud)

一旦List被初始化,您可以添加一些元素吧:

    list.add( "foo" );
    list.add( "bar" );
Run Code Online (Sandbox Code Playgroud)

然后最后调用GetEnumerator()并将其传递给您的方法然后传递给List您的方法:

    // wrong: yourObject.Set_Events( list );
    yourObject.Set_Events( list );
Run Code Online (Sandbox Code Playgroud)

(如果您对生成代理有任何疑问,请询问.)


更新:

正如丹指出,他的方法是使用IEnumerable IEnumerator的.因此,List本身应该被传入set_Event,而不是Get_Enumerator()(如前面的例子中那样).此外,当您调用时createObject,assemblyList必须包括代理jar和dll文件.否则方法调用可能会失败.事实证明,这是后来问题的原因.

以下是适用于CF10的更正版本.

重新生成代理jar

  1. 打开JNBProxyGUI.exe并选择 Create new Java -> .NET Project
  2. 输入本地java设置(我的设置)
    • 远程主机/端口:本地主机6089
    • Java路径: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
  3. 点击Project > Edit Assembly List > Add.找到并选择"mscorlib.dll"
  4. 点击Project > Add Classes from Assembly File.找到并选择"mscorlib.dll"(然后gui生成一个类列表.可能需要一段时间).
  5. 在"环境"列表中,选择System.Collections.Generic包并单击Add.
  6. 选择 Edit > Check All in Exposed Proxies
  7. 选择Project > Build并选择新代理jar的路径和文件名

清理:

为了安全起见,我停止了CF并删除了WEB-INF\cfclasses\dotNetProxy dotNetCoreProxy.jar 之外的所有生成的代理jar .然后重新启动CF并运行代码.它工作得很好.串.(参见下面的完整代码).


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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

CF代码:

<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>
Run Code Online (Sandbox Code Playgroud)