giraffe-fsharp / Giraffe

A native functional ASP.NET Core web framework for F# developers.
https://giraffe.wiki
Apache License 2.0
2.11k stars 267 forks source link

SignalR AddSignalR() throwing exception #295

Closed graforlock closed 6 years ago

graforlock commented 6 years ago

I am basically trying to replicate this tutorial https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-2.1&tabs=visual-studio-code.

In Program.fs I have:

let configureServices (services : IServiceCollection) =
    services.AddCors()    |> ignore
    services.AddGiraffe() |> ignore
    services.AddSignalR() |> ignore
let configureApp (app : IApplicationBuilder) =
    let env = app.ApplicationServices.GetService<IHostingEnvironment>()
    (match env.IsDevelopment() with
    | true  -> app.UseDeveloperExceptionPage()
    | false -> app.UseGiraffeErrorHandler errorHandler)
        .UseCors(configureCors)
        .UseStaticFiles()
        .UseSignalR(fun routes -> routes.MapHub<ChatHub>(PathString("/chatHub")) |> ignore)
        .UseGiraffe(webApp)

And in Services/ChatHub.fs:

module Hubs

open Microsoft.AspNetCore.SignalR
open FSharp.Control.Tasks.ContextInsensitive

type ChatHub() =
  inherit Hub()
  member x.SendMessage user msg =
    task {
      do! x.Clients.All.SendAsync("ReceiveMessage", user, msg)
    }

Upon the attempt of running the app, the exception I am getting is:

Application startup exception: System.ArgumentException: Type a contains generic parameters
Parameter name: type
   at System.Dynamic.Utils.TypeUtils.ValidateType(Type type, String paramName, Int32 index)
   at System.Dynamic.Utils.TypeUtils.ValidateType(Type type, String paramName, Boolean allowByRef, Boolean allowPointer)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type, MethodInfo method)
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo)
   at Microsoft.Extensions.Internal.ObjectMethodExecutor..ctor(MethodInfo methodInfo, TypeInfo targetTypeInfo, Object[] parameterDefaultValues)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.DiscoverHubMethods()
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1..ctor(IServiceScopeFactory serviceScopeFactory, IHubContext`1 hubContext, IOptions`1 hubOptions, IOptions`1 globalHubOptions, ILogger`1 logger)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.Internal.ActivatorUtilities.GetServiceOrCreateInstance[T](IServiceProvider provider)
   at Microsoft.AspNetCore.Connections.ConnectionBuilderExtensions.UseConnectionHandler[TConnectionHandler](IConnectionBuilder connectionBuilder)
   at Microsoft.AspNetCore.SignalR.HubRouteBuilder.<>c__3`1.<MapHub>b__3_0(IConnectionBuilder builder)
   at Microsoft.AspNetCore.Http.Connections.ConnectionsRouteBuilder.MapConnections(PathString path, HttpConnectionDispatcherOptions options, Action`1 configure)
   at Microsoft.AspNetCore.SignalR.HubRouteBuilder.MapHub[THub](PathString path, Action`1 configureOptions)
   at Microsoft.AspNetCore.SignalR.HubRouteBuilder.MapHub[THub](PathString path)
   at GiraffeSample.App.configureApp@96-2.Invoke(HubRouteBuilder routes) in /Users/maciej.sitko/Documents/Tut/GiraffeSample/src/GiraffeSample/Program.fs:line 96
   at Microsoft.AspNetCore.Builder.ConnectionsAppBuilderExtensions.UseConnections(IApplicationBuilder app, Action`1 configure)
   at Microsoft.AspNetCore.Builder.SignalRAppBuilderExtensions.UseSignalR(IApplicationBuilder app, Action`1 configure)
   at GiraffeSample.App.configureApp(IApplicationBuilder app) in /Users/maciej.sitko/Documents/Tut/GiraffeSample/src/GiraffeSample/Program.fs:line 93
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()

Unhandled Exception: System.ArgumentException: Type a contains generic parameters
Parameter name: type
   at System.Dynamic.Utils.TypeUtils.ValidateType(Type type, String paramName, Int32 index)
   at System.Dynamic.Utils.TypeUtils.ValidateType(Type type, String paramName, Boolean allowByRef, Boolean allowPointer)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type, MethodInfo method)
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo)
   at Microsoft.Extensions.Internal.ObjectMethodExecutor..ctor(MethodInfo methodInfo, TypeInfo targetTypeInfo, Object[] parameterDefaultValues)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.DiscoverHubMethods()
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1..ctor(IServiceScopeFactory serviceScopeFactory, IHubContext`1 hubContext, IOptions`1 hubOptions, IOptions`1 globalHubOptions, ILogger`1 logger)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.Internal.ActivatorUtilities.GetServiceOrCreateInstance[T](IServiceProvider provider)
   at Microsoft.AspNetCore.Connections.ConnectionBuilderExtensions.UseConnectionHandler[TConnectionHandler](IConnectionBuilder connectionBuilder)
   at Microsoft.AspNetCore.SignalR.HubRouteBuilder.<>c__3`1.<MapHub>b__3_0(IConnectionBuilder builder)
   at Microsoft.AspNetCore.Http.Connections.ConnectionsRouteBuilder.MapConnections(PathString path, HttpConnectionDispatcherOptions options, Action`1 configure)
   at Microsoft.AspNetCore.SignalR.HubRouteBuilder.MapHub[THub](PathString path, Action`1 configureOptions)
   at Microsoft.AspNetCore.SignalR.HubRouteBuilder.MapHub[THub](PathString path)
   at GiraffeSample.App.configureApp@96-2.Invoke(HubRouteBuilder routes) in /Users/maciej.sitko/Documents/Tut/GiraffeSample/src/GiraffeSample/Program.fs:line 96
   at Microsoft.AspNetCore.Builder.ConnectionsAppBuilderExtensions.UseConnections(IApplicationBuilder app, Action`1 configure)
   at Microsoft.AspNetCore.Builder.SignalRAppBuilderExtensions.UseSignalR(IApplicationBuilder app, Action`1 configure)
   at GiraffeSample.App.configureApp(IApplicationBuilder app) in /Users/maciej.sitko/Documents/Tut/GiraffeSample/src/GiraffeSample/Program.fs:line 93
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
   at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
   at GiraffeSample.App.main(String[] _arg1) in /Users/maciej.sitko/Documents/Tut/GiraffeSample/src/GiraffeSample/Program.fs:line 112

Do you know what is the possible cause for this?

dustinmoris commented 6 years ago

Did you figure out what the problem was?

graforlock commented 6 years ago

Ah yes, it was not Giraffe per se but how F# plays with SignalR. Stack traces seems very vague and doesn't explain why is it happening, but adding type annotations here:

member x.SendMessage (user: string, msg: string) =
    task {
      do! x.Clients.All.SendAsync("ReceiveMessage", user, msg)
    }

Fixes the issue.

HelloKitty commented 5 years ago

I am encountering this same issue. No solution for it yet on my end and this is with C#.

HelloKitty commented 5 years ago

I found the root issue in my case. You cannot seem to have generic public methods on SignalR hubs. SignalR will attempt to do some internal registration for public methods it seems and this will fail when they are generic.

In my case, the solution was to just make my interface method implementation explicit. If you have generic method members just make them private or something. That should work too.

Better warnings/errors should be thrown when this occurs in my opinion!

Obviously this isn't an issue with Giraffe. But with ASP Core SignalR itself. However, seemed relevant to post here since this is where Google brings you.

I'll open an issue on the ASP Core github about this too.

Thorium commented 4 years ago

Just a note here (because FSharp-Giraffe-SignalR combination is not well documented)... You can also to strongly typed hub:

type IMessageToClient = 
    abstract NotifyDeal : string -> Task

type SignalHub() as this =
    inherit Hub<IMessageToClient>()

    member __.BuyStocks (company:string) (amount:int) =
        //...
        this.Clients.All.NotifyDeal "New deal was done!"

Sadly I have not found support to EnableJavaScriptProxies or how to generate proxies manually with a tool like signalr.exe was.