简体   繁体   中英

Calling F# Library with Akka.Net actors from C# library leads to TypeInitializationException

I have in my test solution 3 projects:

  • f# class library (let's name it F#Lib),
  • f# console application (let's name it F#Console) and
  • c# console application (C#Console).

In the class library I define an 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

If I instantiate the MainClass from f# console application, I get expected result:

[<EntryPoint>]
let main argv = 
    Just.Test.Project.MainClass("bugaga!") |> ignore
    System.Threading.Thread.Sleep(1000)
    0 // return an integer exit code

output:

"bugaga!!!"

But. If I do the same from c# console app:

static void Main(string[] args)
{
    new Just.Test.Project.MainClass("bugaga!");
    System.Threading.Thread.Sleep(1000);
}

I get an exception:

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

How should I interpret this exception? What might be wrong?

UPDATE

As already noted in comments and in Tomas Petricek answer, the issue is related to the version mismatch. The bindingRedirect does the job and everything starts working. But I still have some misunderstanding.

First. The Akka.FSharp references FSharp.Core 4.3.1 .

Second. The F#Lib and F#Console references FSharp.Core 4.4.0 and C#Console has no references to FSharp.Core at all.

Third. The F#Console works like a charm and if I print loaded assemblies I get:

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

so, no FSharp.Core 4.3.1.0 loaded and everything is tip-top!

Fourth. The C#Console does not work. The list of loaded assemblies looks a bit strange:

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

It means both FSharp.Core, Version=4.4.0.0 and FSharp.Core, Version=4.3.1.0 are loaded. Why? And why it does not happened in F#Console case?

SOLUTION

The problem (as usually )) was in my head.

First of all, I never looked into the final F#Console config. The initial app.config is quite simple:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

but after the build it gets (thanks to AutoGenerateBindingRedirects MSBuild flag ) the required buinding redirects:

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

The second one. C# compiler was trying to notify me about the problem by writing such messages in the build output:

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 http://go.microsoft.com/fwlink/?LinkId=294190 .

But I decided that C# compiler is just a little more restrictive as F# compiler. I didn't know that in F# projects the flag is set automatically. What stoped me from checking it? I do not know! )

This sounds like an FSharp.Core.dll version mismatch issue.

Most likely, you are referencing a newer version of FSharp.Core.dll than the one that has been referenced by the F# wrapper library for Akka.net. You can either reference the same version, or add a config file with bindingRedirect . See Mark Seemann's blog post .

The keyword here is MethodMissingException - you are getting that because the signature of the method involves types from FSharp.Core.dll and the version that has been loaded (based on the reference in the C# project) does not match the version exposed by the wrapper (based on the reference used when compiling the wrapper). Because of the version mismatch, the types are treated as different and "method is not found".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM