title: send SignalR client message from background thread
body: <p>I'm trying to track the progress of my Hangfire background jobs guided by article <a href="http://docs.hangfire.io/en/latest/background-processing/tracking-progress.html" rel="noreferrer">http://docs.hangfire.io/en/latest/background-processing/tracking-progress.html</a></p>
  
  <p>Unfortunately the example given in the article is not working. I downloaded and run the sample web application (<a href="https://github.com/HangfireIO/Hangfire.Highlighter" rel="noreferrer">https://github.com/HangfireIO/Hangfire.Highlighter</a>) locally. It's ok when client subscribes after the job is complete. In this case message was sent directly from the Hub and has been received by the client. Otherwise message call invoked from Hangfire job and then just nothing happens. No exceptions, no result. What could be causing this behavior? Just in case: hangfire jobs could not work asynchronously...</p>
  
  <p>Code from the sample:</p>
  
  <p><strong>Hangfire Job (see Highlight method)</strong></p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Net.Http;
  using System.Threading.Tasks;
  using HangFire.Highlighter.Hubs;
  using HangFire.Highlighter.Models;
  using Microsoft.AspNet.SignalR;
  
  namespace HangFire.Highlighter.Jobs
  {
      public class SnippetHighlighter : IDisposable
      {
          private readonly IHubContext _hubContext;
          private readonly HighlighterDbContext _dbContext;
  
          public SnippetHighlighter()
              : this(GlobalHost.ConnectionManager.GetHubContext&lt;SnippetHub&gt;(), new HighlighterDbContext())
          {
          }
  
          internal SnippetHighlighter(IHubContext hubContext, HighlighterDbContext dbContext)
          {
              if (hubContext == null) throw new ArgumentNullException("hubContext");
              if (dbContext == null) throw new ArgumentNullException("dbContext");
  
              _hubContext = hubContext;
              _dbContext = dbContext;
          }
  
          public void Highlight(int snippetId)
          {
              var snippet = _dbContext.CodeSnippets.Find(snippetId);
              if (snippet == null) return;
  
              snippet.HighlightedCode = HighlightSource(snippet.SourceCode);
              snippet.HighlightedAt = DateTime.UtcNow;
  
              _dbContext.SaveChanges();
  
              _hubContext.Clients.Group(SnippetHub.GetGroup(snippet.Id))
                  .highlight(snippet.HighlightedCode);
          }
  
          public void Dispose()
          {
              _dbContext.Dispose();
          }
  
          private static async Task&lt;string&gt; HighlightSourceAsync(string source)
          {
              using (var client = new HttpClient())
              {
                  var response = await client.PostAsync(
                      @"http://hilite.me/api",
                      new FormUrlEncodedContent(new Dictionary&lt;string, string&gt;
                  {
                      { "lexer", "c#" },
                      { "style", "vs" },
                      { "code", source }
                  }));
  
                  response.EnsureSuccessStatusCode();
  
                  return await response.Content.ReadAsStringAsync();
              }
          }
  
          private static string HighlightSource(string source)
          {
              // Microsoft.Net.Http does not provide synchronous API,
              // so we are using wrapper to perform a sync call.
              return RunSync(() =&gt; HighlightSourceAsync(source));
          }
  
          private static TResult RunSync&lt;TResult&gt;(Func&lt;Task&lt;TResult&gt;&gt; func)
          {
              return Task.Run&lt;Task&lt;TResult&gt;&gt;(func).Unwrap().GetAwaiter().GetResult();
          }
      }
  }
  </code></pre>
  
  <p><strong>Hub</strong></p>
  
  <pre><code>using System.Data.Entity;
  using System.Linq;
  using System.Threading.Tasks;
  using HangFire.Highlighter.Models;
  using Microsoft.AspNet.SignalR;
  
  namespace HangFire.Highlighter.Hubs
  {
      public class SnippetHub : Hub
      {
          public async Task Subscribe(int snippetId)
          {
              await Groups.Add(Context.ConnectionId, GetGroup(snippetId));
  
              // When a user subscribes a snippet that was already 
              // highlighted, we need to send it immediately, because
              // otherwise she will listen for it infinitely.
              using (var db = new HighlighterDbContext())
              {
                  var snippet = await db.CodeSnippets
                      .Where(x =&gt; x.Id == snippetId &amp;&amp; x.HighlightedCode != null)
                      .SingleOrDefaultAsync();
  
                  if (snippet != null)
                  {
                      Clients.Client(Context.ConnectionId)
                          .highlight(snippet.Id, snippet.HighlightedCode);
                  }
              }
          }
  
          public static string GetGroup(int snippetId)
          {
              return "snippet:" + snippetId;
          }
      }
  }
  </code></pre>
  
  <p>SingnalR configured using OWIN Startup class. </p>