使用来自 C# 库的 Akka.Net 个参与者调用 F# 库导致 TypeInitializationException
Calling F# Library with Akka.Net actors from C# library leads to TypeInitializationException
我的测试解决方案中有 3 个项目:
- f# class 库(我们将其命名为 F#Lib),
- f# 控制台应用程序(我们将其命名为 F#Console)和
- c# 控制台应用程序 (C#Console)。
在 class 库中,我定义了一个 akka.net 演员:
namespace Just.Test.Project
open Akka.Actor
open Akka.FSharp
open Akka.Configuration
module Actors =
let system = System.create "WaveNetSystem" (Configuration.defaultConfig())
let simple = spawn system "simple" (fun mailbox ->
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "%A" message
return! loop()
}
loop()
)
type MainClass(msg) = let x = Actors.simple <! msg
如果我从 f# 控制台应用程序实例化 MainClass
,我会得到预期的结果:
[<EntryPoint>]
let main argv =
Just.Test.Project.MainClass("bugaga!") |> ignore
System.Threading.Thread.Sleep(1000)
0 // return an integer exit code
输出:
"bugaga!!!"
但是。如果我从 C# 控制台应用程序执行相同的操作:
static void Main(string[] args)
{
new Just.Test.Project.MainClass("bugaga!");
System.Threading.Thread.Sleep(1000);
}
我遇到异常:
System.TypeInitializationException: The type initializer for '<StartupCode$SampleFSharpAkkaNet>.$Test' threw an exception. ---> System.MissingMethodException: Method not found: 'Akka.Actor.IActorRef Akka.FSharp.Spawn.spawn(Akka.Actor.IActorRefFactory, System.String, Microsoft.FSharp.Core.FSharpFunc`2<Actor`1<!!0>,Cont`2<!!0,!!1>>)'.
at <StartupCode$SampleFSharpAkkaNet>.$Test..cctor()
--- End of inner exception stack trace ---
at Just.Test.Project.Actors.get_simple()
at Just.Test.Project.MainClass..ctor() in d:\prj\research\AkkaFSharpTest\SampleFSharpAkkaNet\Test.fs:line 20
我应该如何解释这个异常?可能出了什么问题?
更新
正如评论和 Tomas Petricek 的回答中所述,该问题与版本不匹配有关。 bindingRedirect
完成工作,一切开始工作。但是我还是有些误会。
首先。 Akka.FSharp
引用 FSharp.Core 4.3.1
.
其次。 F#Lib 和 F#Console 引用 FSharp.Core 4.4.0
而 C#Console 根本没有引用 FSharp.Core
。
第三。 F#Console 的工作就像一个魅力,如果我打印加载的程序集,我得到:
FSharpConsole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
FSharpLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Akka, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
Akka.FSharp, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
FsPickler, Version=1.2.21.0, Culture=neutral, PublicKeyToken=null
System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
所以,没有 FSharp.Core 4.3.1.0
加载,一切都很好!
第四。 C#Console 不工作。加载的程序集列表看起来有点奇怪:
CSharpConsole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
FSharpLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Akka, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
Akka.FSharp, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
表示FSharp.Core, Version=4.4.0.0
和FSharp.Core, Version=4.3.1.0
都加载了。为什么?为什么在 F#Console 案例中没有发生?
解决方案
问题(通常 ))在我的脑海中。
首先,我从未查看过最终的 F#Console 配置。初始的app.config
很简单:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
但是在构建之后它得到了(感谢AutoGenerateBindingRedirects MSBuild flag)所需的构建重定向:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.4.0.0" newVersion="4.4.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
第二个。 C# 编译器试图通过在构建输出中写入这样的消息来通知我这个问题:
No way to resolve conflict between "FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" and "FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". Choosing "FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" arbitrarily.
Consider app.config remapping of assembly "FSharp.Core, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" from Version "4.3.1.0" [C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp.NETFramework\v4.0.3.1.0\FSharp.Core.dll] to Version "4.4.0.0" [C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp.NETFramework\v4.0.4.0.0\FSharp.Core.dll] to solve conflict and get rid of warning.
C:\Program Files (x86)\MSBuild.0\bin\Microsoft.Common.CurrentVersion.targets(1820,5): warning MSB3276: Found conflicts between different versions of the same dependent assembly. Please set the "AutoGenerateBindingRedirects" property to true in the project file. For more information, see http://go.microsoft.com/fwlink/?LinkId=294190.
但我认为 C# 编译器只是比 F# 编译器的限制多一点。我不知道在 F# 项目中,标志是自动设置的。是什么阻止我检查它?我不知道! )
这听起来像是 FSharp.Core.dll
版本不匹配问题。
您引用的 FSharp.Core.dll
版本很可能比 Akka.net 的 F# 包装器库引用的版本更新。您可以引用相同的版本,也可以添加带有 bindingRedirect
的配置文件。参见 Mark Seemann's blog post。
这里的关键字是 MethodMissingException
- 你得到它是因为方法的签名涉及来自 FSharp.Core.dll
的类型和已加载的版本(基于 C# 项目中的引用)与包装器公开的版本不匹配(基于编译包装器时使用的参考)。由于版本不匹配,类型被视为不同并且 "method is not found".
我的测试解决方案中有 3 个项目:
- f# class 库(我们将其命名为 F#Lib),
- f# 控制台应用程序(我们将其命名为 F#Console)和
- c# 控制台应用程序 (C#Console)。
在 class 库中,我定义了一个 akka.net 演员:
namespace Just.Test.Project
open Akka.Actor
open Akka.FSharp
open Akka.Configuration
module Actors =
let system = System.create "WaveNetSystem" (Configuration.defaultConfig())
let simple = spawn system "simple" (fun mailbox ->
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "%A" message
return! loop()
}
loop()
)
type MainClass(msg) = let x = Actors.simple <! msg
如果我从 f# 控制台应用程序实例化 MainClass
,我会得到预期的结果:
[<EntryPoint>]
let main argv =
Just.Test.Project.MainClass("bugaga!") |> ignore
System.Threading.Thread.Sleep(1000)
0 // return an integer exit code
输出:
"bugaga!!!"
但是。如果我从 C# 控制台应用程序执行相同的操作:
static void Main(string[] args)
{
new Just.Test.Project.MainClass("bugaga!");
System.Threading.Thread.Sleep(1000);
}
我遇到异常:
System.TypeInitializationException: The type initializer for '<StartupCode$SampleFSharpAkkaNet>.$Test' threw an exception. ---> System.MissingMethodException: Method not found: 'Akka.Actor.IActorRef Akka.FSharp.Spawn.spawn(Akka.Actor.IActorRefFactory, System.String, Microsoft.FSharp.Core.FSharpFunc`2<Actor`1<!!0>,Cont`2<!!0,!!1>>)'.
at <StartupCode$SampleFSharpAkkaNet>.$Test..cctor()
--- End of inner exception stack trace ---
at Just.Test.Project.Actors.get_simple()
at Just.Test.Project.MainClass..ctor() in d:\prj\research\AkkaFSharpTest\SampleFSharpAkkaNet\Test.fs:line 20
我应该如何解释这个异常?可能出了什么问题?
更新
正如评论和 Tomas Petricek 的回答中所述,该问题与版本不匹配有关。 bindingRedirect
完成工作,一切开始工作。但是我还是有些误会。
首先。 Akka.FSharp
引用 FSharp.Core 4.3.1
.
其次。 F#Lib 和 F#Console 引用 FSharp.Core 4.4.0
而 C#Console 根本没有引用 FSharp.Core
。
第三。 F#Console 的工作就像一个魅力,如果我打印加载的程序集,我得到:
FSharpConsole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
FSharpLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Akka, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
Akka.FSharp, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
FsPickler, Version=1.2.21.0, Culture=neutral, PublicKeyToken=null
System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
所以,没有 FSharp.Core 4.3.1.0
加载,一切都很好!
第四。 C#Console 不工作。加载的程序集列表看起来有点奇怪:
CSharpConsole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
FSharpLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Akka, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
Akka.FSharp, Version=1.0.6.16, Culture=neutral, PublicKeyToken=null
表示FSharp.Core, Version=4.4.0.0
和FSharp.Core, Version=4.3.1.0
都加载了。为什么?为什么在 F#Console 案例中没有发生?
解决方案
问题(通常 ))在我的脑海中。
首先,我从未查看过最终的 F#Console 配置。初始的app.config
很简单:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
但是在构建之后它得到了(感谢AutoGenerateBindingRedirects MSBuild flag)所需的构建重定向:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.4.0.0" newVersion="4.4.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
第二个。 C# 编译器试图通过在构建输出中写入这样的消息来通知我这个问题:
No way to resolve conflict between "FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" and "FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". Choosing "FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" arbitrarily. Consider app.config remapping of assembly "FSharp.Core, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" from Version "4.3.1.0" [C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp.NETFramework\v4.0.3.1.0\FSharp.Core.dll] to Version "4.4.0.0" [C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp.NETFramework\v4.0.4.0.0\FSharp.Core.dll] to solve conflict and get rid of warning. C:\Program Files (x86)\MSBuild.0\bin\Microsoft.Common.CurrentVersion.targets(1820,5): warning MSB3276: Found conflicts between different versions of the same dependent assembly. Please set the "AutoGenerateBindingRedirects" property to true in the project file. For more information, see http://go.microsoft.com/fwlink/?LinkId=294190.
但我认为 C# 编译器只是比 F# 编译器的限制多一点。我不知道在 F# 项目中,标志是自动设置的。是什么阻止我检查它?我不知道! )
这听起来像是 FSharp.Core.dll
版本不匹配问题。
您引用的 FSharp.Core.dll
版本很可能比 Akka.net 的 F# 包装器库引用的版本更新。您可以引用相同的版本,也可以添加带有 bindingRedirect
的配置文件。参见 Mark Seemann's blog post。
这里的关键字是 MethodMissingException
- 你得到它是因为方法的签名涉及来自 FSharp.Core.dll
的类型和已加载的版本(基于 C# 项目中的引用)与包装器公开的版本不匹配(基于编译包装器时使用的参考)。由于版本不匹配,类型被视为不同并且 "method is not found".