Hangfire Background Job with Return Value

2019-04-20 08:06发布

I'm switching from Task.Run to Hangfire. In .NET 4.5+ Task.Run can return Task<TResult> which allows me to run tasks that return other than void. I can normally wait and get the result of my task by accessing the property MyReturnedTask.Result

Example of my old code:

public void MyMainCode()
{
    List<string> listStr = new List<string>();
    listStr.Add("Bob");
    listStr.Add("Kate");
    listStr.Add("Yaz");

    List<Task<string>> listTasks = new List<Task<string>>();

    foreach(string str in listStr)
    {
        Task<string> returnedTask = Task.Run(() => GetMyString(str));
        listTasks.Add(returnedTask);
    }

    foreach(Task<string> task in listTasks)
    {
        // using task.Result will cause the code to wait for the task if not yet finished.
        // Alternatively, you can use Task.WaitAll(listTasks.ToArray()) to wait for all tasks in the list to finish.
        MyTextBox.Text += task.Result + Environment.NewLine;
    }
}
private string GetMyString(string str)
{
    // long execution in order to calculate the returned string
    return str + "_finished";
}

As far as I can see from the Quick Start page of Hangfire, your main guy which is BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget")); perfectly runs the code as a background job but apparently doesn't support jobs that have a return value (like the code I presented above). Is that right? if not, how can I tweak my code in order to use Hangfire?

P.S. I already looked at HostingEnvironment.QueueBackgroundWorkItem (here) but it apparently lacks the same functionality (background jobs have to be void)

EDIT

As @Dejan figured out, the main reason I want to switch to Hangfire is the same reason the .NET folks added QueueBackgroundWorkItem in .NET 4.5.2. And that reason is well described in Scott Hanselman's great article about Background Tasks in ASP.NET. So I'm gonna quote from the article:

QBWI (QueueBackgroundWorkItem) schedules a task which can run in the background, independent of any request. This differs from a normal ThreadPool work item in that ASP.NET automatically keeps track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.

1条回答
一纸荒年 Trace。
2楼-- · 2019-04-20 08:23

One simple solution would be to poll the monitoring API until the job is finished like this:

    public static Task Enqueue(Expression<Action> methodCall)
    {
        string jobId = BackgroundJob.Enqueue(methodCall);
        Task checkJobState = Task.Factory.StartNew(() =>
        {
            while (true)
            {
                IMonitoringApi monitoringApi = JobStorage.Current.GetMonitoringApi();
                JobDetailsDto jobDetails = monitoringApi.JobDetails(jobId);
                string currentState = jobDetails.History[0].StateName;
                if (currentState != "Enqueued" && currentState != "Processing")
                {
                    break;
                }
                Thread.Sleep(100); // adjust to a coarse enough value for your scenario
            }
        });
        return checkJobState;
    }

Attention: Of course, in a Web-hosted scenario you cannot rely on continuation of the task (task.ContinueWith()) to do more things after the job has finished as the AppDomain might be shut down - for the same reasons you probably want to use Hangfire in the first place.

查看更多
登录 后发表回答