编程语言
首页 > 编程语言> > 从C#库中使用Akka.Net actor调用F#库会导致TypeInitializationException

从C#库中使用Akka.Net actor调用F#库会导致TypeInitializationException

作者:互联网

我的测试解决方案中有3个项目:

> f#类库(我们将其命名为F#Lib),
> f#控制台应用程序(将其命名为F#Console),然后
> c#控制台应用程序(C#Console).

在类库中,我定义了akka.net actor:

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#控制台不起作用.加载的程序集列表看起来有些奇怪:

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版本= 4.4.0.0和FSharp.Core版本= 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),所需的buinding重定向:

  <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\4.3.1.0\FSharp.Core.dll] to Version “4.4.0.0” [C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp.NETFramework\v4.0\4.4.0.0\FSharp.Core.dll] to solve conflict and get rid of warning.
C:\Program Files (x86)\MSBuild\14.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 07001.

但是我认为C#编译器比F#编译器更具限制性.我不知道在F#项目中该标志是自动设置的.是什么阻止了我进行检查?我不知道! )

解决方法:

这听起来像是FSharp.Core.dll版本不匹配的问题.

最有可能的是,您引用的FSharp.Core.dll版本比Akka.net的F#包装库所引用的版本更高.您可以引用相同的版本,也可以使用bindingRedirect添加配置文件.参见Mark Seemann’s blog post.

这里的关键字是MethodMissingException-因为该方法的签名涉及FSharp.Core.dll中的类型,并且已加载的版本(基于C#项目中的引用)与包装程序公开的版本不匹配,因此您将明白这一点. (基于编译包装器时使用的参考).由于版本不匹配,这些类型被视为不同,并且“未找到方法”.

标签:akka-net,f,net-4-5,c
来源: https://codeday.me/bug/20191118/2031206.html