我正在尝试使用我无法调整的自定义.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 ).
这完全没用。我可以浏览到我的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对象时我缺少什么?
答案 0 :(得分:7)
如前所述,您需要使用具体类(而不是接口)。但是,查看方法签名时,它必须是实现System.Collections.Generic.IEnumerable而不是System.Collections.IEnumerable的方法。后者仅适用于非泛型集合。您可以使用的通用集合的一个示例是List<T>
,其中<T>
是列表所包含的对象类型。 (您需要检查您的API以确定对象类型)。
不幸的是......有一个issue with Generic classes。基本上createObject
使用的工具无法为泛型生成代理。 (博客文章说它在CF9中得到了解决,但在我的测试中我遇到了与9.0.1 - YMMV相同的问题。)所以你需要自己使用JNBProxyGUI.exe。坦率地说,它不是最直观的工具..但经过一番争吵后,我设法让它在CF9下工作。幸运的是,这只是一次性事件。
将生成的代理导出到jar文件后,使用博客条目中的示例创建通用List
。只需将代理jar添加到程序集列表中即可。假设Collection存储简单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所指出的,他的方法是使用IEnumerable
而不是 IEnumerator。因此,List
本身应该传递到set_Event
,而不是Get_Enumerator()
(如上例所示)。此外,当您调用createObject
时, assemblyList必须同时包含代理jar和dll文件。否则方法调用可能会失败。事实证明这是后来问题的原因。
以下是适用于CF10的更正版本。
重新生成代理jar
Create new Java -> .NET Project
Project > Edit Assembly List > Add
。找到并选择“mscorlib.dll”Project > Add Classes from Assembly File
。找到并选择“mscorlib.dll”
(然后gui会生成一个类列表。可能需要一段时间。)System.Collections.Generic
个包,然后点击Add
。 Edit > Check All in Exposed Proxies
Project > Build
并选择新代理jar的路径和文件名<强>清理:强>
为了安全起见,我停止了CF并从WEB-INF\cfclasses\dotNetProxy
删除了所有生成的代理jar,除了 dotNetCoreProxy.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>
答案 1 :(得分:1)
问题是您正在尝试调用接口的方法。相反,您需要创建一个实现该接口的类的实例,以便将其传递给Set_Events()
。以下是实现IEnumerable的.NET类列表:IEnumerable Interface。