orleans icon indicating copy to clipboard operation
orleans copied to clipboard

F# host: Could not find an implementation for interface OrleansDashboard.IDashboardGrain - C# host ok

Open fwaris opened this issue 3 years ago • 17 comments

Unable to host silo in F# console project. C# console project works fine. Please see attached minimal repo.

FsGrains (2).zip

Need to host in F# due to other F# frameworks (e.g. for web dev) required for the application.

Issue is with the 7.x version. This was not an issue with the 3.x.x version of Orleans.

F# hosting works if 'UseDashboard' is commented out.

Error seen:

fail: Orleans.Runtime.SiloLifecycleSubject[100450]
      Orleans.Hosting.SiloBuilderStartupExtensions+StartupTask failed to start due to errors at stage Active (20000)
      System.ArgumentException: Could not find an implementation for interface OrleansDashboard.IDashboardGrain
         at Orleans.GrainInterfaceTypeToGrainTypeResolver.GetGrainType(GrainInterfaceType interfaceType) in /_/src/Orleans.Core/Core/GrainInterfaceTypeToGrainTypeResolver.cs:line 149
         at Orleans.GrainFactory.GetGrain(Type interfaceType, IdSpan grainKey, String grainClassNamePrefix) in /_/src/Orleans.Core/Core/GrainFactory.cs:line 214
         at Orleans.GrainFactory.GetGrain[TGrainInterface](Int64 primaryKey, String grainClassNamePrefix) in /_/src/Orleans.Core/Core/GrainFactory.cs:line 52
         at OrleansDashboard.Dashboard.ActivateDashboardGrainAsync()
         at OrleansDashboard.Dashboard.Execute(CancellationToken cancellationToken)
         at Orleans.Runtime.SiloLifecycleSubject.MonitoredObserver.OnStart(CancellationToken ct) in /_/src/Orleans.Runtime/Lifecycle/SiloLifecycleSubject.cs:line 134
f

fwaris avatar Dec 20 '22 11:12 fwaris

Update: I set <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> in the C# host project and found that there are 'generated' files for Dashboard grain serialization.

No source is generated for F# so maybe that's the reason for the error. However, not sure what is the solution other than maybe support for F# code generation.

fwaris avatar Dec 20 '22 11:12 fwaris

After a little more digging, I found that adding parts of C# generated code - namely assembly attributes - to F# host, fixed this issue. 'Program.fs' is now as follows:

open Microsoft.Extensions.Hosting
open Orleans
open Orleans.Hosting

Host.CreateDefaultBuilder()
    .UseOrleans(fun siloBuilder -> 
        siloBuilder
          .UseDashboard()
          .UseLocalhostClustering() 
          .AddMemoryGrainStorage("store") |> ignore)
    .RunConsoleAsync()
|> Async.AwaitTask
|> Async.RunSynchronously

module Load =

    //[<assembly: Orleans.ApplicationPartAttribute("SiloCS")>]
    [<assembly: Orleans.ApplicationPartAttribute("GrainsCodeGen")>]
    [<assembly: Orleans.ApplicationPartAttribute("Orleans.Core.Abstractions")>]
    [<assembly: Orleans.ApplicationPartAttribute("Orleans.Serialization")>]
    [<assembly: Orleans.ApplicationPartAttribute("Orleans.Core")>]
    [<assembly: Orleans.ApplicationPartAttribute("Orleans.Persistence.Memory")>]
    [<assembly: Orleans.ApplicationPartAttribute("Orleans.Runtime")>]
    [<assembly: Orleans.ApplicationPartAttribute("Orleans.Reminders")>]
    [<assembly: Orleans.ApplicationPartAttribute("OrleansDashboard.Core")>]
    [<assembly: Orleans.ApplicationPartAttribute("OrleansDashboard")>]
    //[<assembly: global::Orleans.Serialization.Configuration.TypeManifestProviderAttribute(typeof(OrleansCodeGen.SiloCS.Metadata_SiloCS))]
    ()

I will use this as a workaround while waiting for the 'official' fix or recommendation.

Also not sure what the TypeManifestProvider attribute does and will a lack of it cause issues later on?

fwaris avatar Dec 20 '22 13:12 fwaris

linking reminders issue #8125. The workaround solution will likely work for the reminder table issue also

fwaris avatar Dec 20 '22 13:12 fwaris

Thanks legend. Nice one for this

WesleySkeen avatar Aug 02 '23 14:08 WesleySkeen

I spent past three days looking for even a trivial end-to-end F#-only example of how to use Orleans with F#, and I am hitting several issues including this one. Seems like F# is low priority for Orleans. What would be recommended solution - shall I set up mixed C# / F# project, with host written in C#? @fwaris what are your experiences?

DejanMilicic avatar Apr 01 '25 21:04 DejanMilicic

The code generator that Orleans uses only knows how to output C# code, so you need C# in your app somewhere. The host process is a good place for it since it's the top level that references all assemblies, but it's not strictly required that it's C#. An alternative is to do what @fwaris showed above, where you add an attribute(s) pointing to the C# assembly/assemblies. With that, you can follow the rest of the approach outlined in the F# sample: https://github.com/dotnet/samples/tree/main/orleans/FSharpHelloWorld. Having a fully F# sample, where all app code is written in F# and the only C# project is the one receiving the generated code, would be very welcome.

ReubenBond avatar Apr 01 '25 22:04 ReubenBond

I had to put a minimal C# library in the solution for the code gen to work.

Below is a sample (prob. now out-of-date) that might help.

https://github.com/fwaris/OrleansKafkaSample

fwaris avatar Apr 01 '25 22:04 fwaris

Thanx @fwaris ! I took some time to clean up your project and to strip it down to something that would be basic sample project for people who would like to use Orleans with F#. If you have a minute or two, I would be grateful if you could glance over the code to verify I am not making anything stupid - I am yet starting with Orleans.

@ReubenBond if you can glance over the code and verify this is the correct / best-practices way of setting up Orleans, that would be awesome.

Thanx!

https://github.com/DejanMilicic/OrleansFsharp

DejanMilicic avatar Apr 02 '25 20:04 DejanMilicic

@DejanMilicic sure I can take a quick look at it

fwaris avatar Apr 03 '25 21:04 fwaris

looks good. nice and clean.

fwaris avatar Apr 03 '25 21:04 fwaris

@fwaris I am now proceeding further (in my private repo) and refactoring code to be more F# style. As part of that refactoring, I am introducing messages in the form of discriminated union. Call to grain succeeds, but received message is null. Clearly, it is a serialization problem. I tried various things in past few days, but I did not manage to fix it.

Can you point me to a working example where F# type is successfully serialized and deserialized in the context of Orleans? Thanx!

DejanMilicic avatar Apr 05 '25 13:04 DejanMilicic

in my prod app I think I side-stepped the problem by not using discriminated unions, as I was running into serialization issues also.

(This seriously affects F# programming style!)

@ReubenBond hopefully we can get F# discriminated unions working again see https://github.com/dotnet/orleans/issues/9258

fwaris avatar Apr 05 '25 13:04 fwaris

The thread you linked provides a workaround, @fwaris - adding the following to the F# project fixes it:

[<InternalsVisibleTo("Host")>]
do ()

I tested the fix using the repro project. I'm not sure why it's occurring, or what changed. The code generator should at least error out instead of ignoring the internal members silently as it seems to be doing today.

ReubenBond avatar Apr 05 '25 14:04 ReubenBond

thanks @ReubenBond, I was not aware of this. @DejanMilicic can you please try the fix and let us know

fwaris avatar Apr 05 '25 14:04 fwaris

I'm not sure we can fix this without mandating InternalsVisibleTo, which should be documented in the sample. We could check for it and error if it's not set. I debugged through the case, and we cannot even see the underlying field from the source generator:

// Without InternalsVisibleTo:
Length = 2
    [0]: {UnitTests.FSharpTypes.HelloWorldResult.Completed.Item.get}
    [1]: {UnitTests.FSharpTypes.HelloWorldResult.Completed.Item}
    Results View: Expanding the Results View will enumerate the IEnumerable

// With InternalsVisibleTo:
Length = 4
    [0]: {UnitTests.FSharpTypes.HelloWorldResult.Completed.item}
    [1]: {UnitTests.FSharpTypes.HelloWorldResult.Completed.Completed(string)}
    [2]: {UnitTests.FSharpTypes.HelloWorldResult.Completed.Item.get}
    [3]: {UnitTests.FSharpTypes.HelloWorldResult.Completed.Item}

ReubenBond avatar Apr 05 '25 15:04 ReubenBond

I managed to get everything working with latest version of .NET and latest version of Orleans. Project is updated with detailed instructions in README https://github.com/DejanMilicic/OrleansFsharp

@fwaris I will be grateful for any suggestions you might have @ReubenBond what would be the best way to help fellow F# developers? Would you be okay with expansion of Samples with this project and also with polished up version of https://github.com/DejanMilicic/orleans-fsharp-dotnet9 that showcases similar scenario, but with whole Host project in C# (essentially, interoperability of C# and F# on Orleans project where some or all of the grains can be in F#)?

DejanMilicic avatar Apr 05 '25 16:04 DejanMilicic

@DejanMilicic

Would you be okay with expansion of Samples with this project and also with polished up version of https://github.com/DejanMilicic/orleans-fsharp-dotnet9 that showcases similar scenario, but with whole Host project in C# (essentially, interoperability of C# and F# on Orleans project where some or all of the grains can be in F#)?

That would be fantastic. If you open a PR against the dotnet/samples repo, we'll merge it. A docs page would also be very useful for F# developers, if you're up for it, maybe under the 'quickstarts' section: https://github.com/dotnet/docs/tree/main/docs/orleans/quickstarts

ReubenBond avatar Apr 05 '25 23:04 ReubenBond

Hello everyone, is there any documentation that explains this C# code generation process? And were there any updates regarding F# support?


Update:

I created a project that implements the quickstart UrlShortener project in F# based on @DejanMilicic's sample project:

  • https://github.com/64J0/orleans-fsharp--url-shortener

64J0 avatar Aug 02 '25 00:08 64J0