dapr / dapr/dotnet-sdk

F# Invoke Timer doesn't work due to reflection w/ System.Threading.Tasks.Task

Open
#585 2 comments 0 reactions 0 assignees View on GitHub
help wanted kind/bug
Dominant language
C#
Stars
1.2k
Forks
378
Avg merge
1d 22h
Merged PRs (30d)
5

Description

## Expected Behavior

I should be able to register a Timer in an F# Actor and have it call the appropriate method.

## Actual Behavior

The dotnet sdk throws an error on `ValidateTimerCallback`.

The first error occurs on [`Actor.cs ln:335`](https://github.com/dapr/dotnet-sdk/blob/master/src/Dapr.Actors/Runtime/Actor.cs#L335) is:
`Timer callback method: {callback} does not exist in the Actor class: {actorTypeName}`
This can be worked around by prefixing the method name (callback argument) with "get_". For example the `Tick` method must be registered to the timer as `get_Tick`.

The second error is when validating that the return type is a `Task`. Even if the method just returns `Task.CompletedTask` [this line ](https://github.com/dapr/dotnet-sdk/blob/master/src/Dapr.Actors/Runtime/Actor.cs#L348) will throw an error saying that the return type is not a task.

When I inspect the ReturnType while debugging I see that the F# `Task` is: `Microsoft.FSharp.Core.FSharpFunc``2[[Microsoft.FSharp.Core.Unit, FSharp.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Threading.Tasks.Task, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], FSharp.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`

If I remove the Validation function I run into an exception on [this line](https://github.com/dapr/dotnet-sdk/blob/master/src/Dapr.Actors/Runtime/ActorManager.cs#L220) when it tries to cast the method call into a Task:

The tick method can be invoked via the dotnet sdk without issues via:
(POST {{daprUrl}}/v1.0/actors/MyActor/myactorid/method/Tick)

Maybe the dotnet sdk can fall back to calling the method the same way the InvokeMethod does without trying to cast the method to a `Task`?
[DispatchInternalAsync does work when invoking the method](https://github.com/dapr/dotnet-sdk/blob/master/src/Dapr.Actors/Runtime/ActorManager.cs#L321)
[DispatchWithoutRemoting](https://github.com/dapr/dotnet-sdk/blob/master/src/Dapr.Actors/Runtime/ActorManager.cs#L128)

## Steps to Reproduce the Problem

From an F# actor call the Start method:
(POST {{daprUrl}}/v1.0/actors/MyActor/myactorid/method/Start)
`
member this.Start() =
task {
do!
this.RegisterTimerAsync(
"Tick",
"get_Tick",
null,
TimeSpan.FromMilliseconds(float 1000),
TimeSpan.FromMilliseconds(float 1000)
)
|> Async.AwaitTask
|> Async.Ignore
} :> Task
`

The tick method can be:
`member this.Tick() = Task.CompletedTask`

## Release Note

RELEASE NOTE:

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.