【问题标题】:Passing IEnumerable Variables into .NET from ColdFusion将 IEnumerable 变量从 ColdFusion 传递到 .NET
【发布时间】:2013-05-30 02:07:22
【问题描述】:

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

这是我遇到问题的Set_Events 方法。我可以创建应该是该枚举变量一部分的单个事件,但我无法创建它显然期望的正确变量类型。我试过这样做:

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

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

当我尝试调用该方法时:

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

这只是给我以下错误:

The GetEnumerator method was not found.

我尝试过的其他事情:

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

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

这给了我以下错误:

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

这又给了我同样的错误:

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

尝试初始化列表

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#">

但这不是:

<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#">

我收到以下错误:

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

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>

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

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 ).

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

结论

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

【问题讨论】:

    标签: .net coldfusion ienumerable coldfusion-10


    【解决方案1】:

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

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

    将生成的代理导出到 jar 文件后,使用博客条目中的示例创建通用 List。只需将代理 jar 添加到程序集列表中即可。说典藏店简单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 );
    

    List 初始化后,您可以向其中添加一些元素:

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

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

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

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


    更新:

    正如 Dan 指出的,他的方法是使用 IEnumerablenot IEnumerator。所以List 本身应该传递给set_Event,而不是Get_Enumerator()(如前面的例子)。此外,当您调用 createObject 时,assemblyList 必须同时包含代理 jar 和 dll 文件。否则方法调用可能会失败。原来这是导致后来出现问题的原因。

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

    重新生成代理 jar

    1. 打开 JNBProxyGUI.exe 并选择Create new Java -&gt; .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 &gt; Edit Assembly List &gt; Add。找到并选择“mscorlib.dll”
    4. 点击Project &gt; Add Classes from Assembly File。找到并选择“mscorlib.dll” (然后 gui 会生成一个类列表。这可能需要一段时间)。
    5. 在“环境”列表中,选择System.Collections.Generic 包并单击Add
    6. 选择Edit &gt; Check All in Exposed Proxies
    7. 选择Project &gt; Build并选择新代理jar的路径和文件名

    清理:

    为了安全起见,我停止了 CF 并从 WEB-INF\cfclasses\dotNetProxy except 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;
            }
        }
    }
    

    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 代码:

    <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>
    

    【讨论】:

      【解决方案2】:

      问题在于您正在尝试调用接口的方法。相反,您需要创建一个实现该接口的类的实例,以便将其传递给Set_Events()。以下是实现 IEnumerable 的 .NET 类的列表:IEnumerable Interface

      【讨论】:

      • 其实仔细看截图,需要实现generic IEnumerable接口。 System.Collections.IEnumerable 用于非泛型集合。
      • 好眼光!我肯定错过了。
      • 是的,第一次也错过了。不过很遗憾,因为 CF/jnbridge 似乎对“通用”品种有一些问题..
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-14
      • 1970-01-01
      • 2013-06-25
      • 1970-01-01
      • 2011-11-14
      • 2017-06-03
      相关资源
      最近更新 更多