简体   繁体   中英

How can I declare a function in F# that can be invoked via WebJob's JobHost.CallAsync?

In a C# WebJob , I'm able to manually invoke a public static class method like this:

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;

namespace foo
{
    public class Program
    {

        [NoAutomaticTrigger]
        public static void Go(TraceWriter log) { ... }

        static void Main()
        {
           var host = new JobHost();
           var methodInfo = typeof(Program).GetMethod("Go");
           host.Call(methodInfo);
           host.RunAndBlock();
        }

methodInfo is a System.Reflection.MethodInfo , and in the debugger I can see that it has Attributes Public | Static | HideBySig Public | Static | HideBySig Public | Static | HideBySig and CustomAttributes Microsoft.Azure.WebJobs.NoAutomaticTriggerAttribute .

I'm trying to do this in F#. Here's what I have so far:

type Foo() =

    [<NoAutomaticTrigger>]
    static member Go (log:TraceWriter) =
        log.Info "hello!"

[<EntryPoint>]
let main argv =
        let theType = typedefof<Foo>
        let methodInfo = theType.GetMethods() |> Seq.find(fun t -> t.Name = "Go")
        host.Call(methodInfo)
        host.RunAndBlock()

The WebJobs runtime doesn't like it:

System.InvalidOperationException
  HResult=0x80131509
  Message='Void Go(Microsoft.Azure.WebJobs.Host.TraceWriter)' can't be invoked from Azure WebJobs SDK. Is it missing Azure WebJobs SDK attributes?
  Source=Microsoft.Azure.WebJobs.Host
  StackTrace:
   at Microsoft.Azure.WebJobs.JobHost.Validate(IFunctionDefinition function, Object key)
   at Microsoft.Azure.WebJobs.JobHost.<CallAsyncCore>d__37.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.WebJobs.JobHost.Call(MethodInfo method)
   at Program.main(String[] argv) in C:\path\to\project\Program.fs:line 110

My F# methodInfo does have the NoAutomaticTrigger attribute. It also has Public and Static, but it's missing HideBySig . Is that likely to matter? Are there other parts of the MethodInfos that I should be comparing?

Here's the relevant source from the webjobs sdk: https://github.com/Azure/azure-webjobs-sdk/blob/v2.2.0/src/Microsoft.Azure.WebJobs.Host/JobHost.cs#L306

For what it's worth, I've been able to use TimerTrigger and ServiceBusTrigger from F# successfully; it's just this manual invocation pattern that I'm struggling with.

Next I'm planning to sift through the webjobs source and try to figure out exactly what it's looking for, but I'm hoping there's something obvious someone more experienced with F# and/or webjobs can tell me.

Debugging through the WebJobs source, I eventually wound up at the DefaultTypeLocator , which picks up classes that are marked IsPublic . I experimented with my F# declarations, but couldn't seem to achieve that; I only managed to produce IsNestedPublic .

So I tried another approach: instead of trying to write an F# function discoverable by the existing WebJobs runtime, I overrode the discovery logic:

type myTypeLocator() =
    interface ITypeLocator with
        member this.GetTypes () =
            new System.Collections.Generic.List<Type>([ typedefof<Foo> ]) :> IReadOnlyList<Type>

...

let config = new JobHostConfiguration (
                    DashboardConnectionString = dashboardConnectionString,
                    StorageConnectionString = storageConnectionString,
                    TypeLocator = new myTypeLocator()
                )

let host = new JobHost(config)

This worked: my functions are discovered and I'm able to JobHost.Call them.

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