Hangfire: Do you plan to support ASP.NET 5 and .NET Core?

Created on 12 May 2015  Â·  33Comments  Â·  Source: HangfireIO/Hangfire

Do you plan to support ASP.NET 5 and .NET Core?

Most helpful comment

.NET Core support is almost here, see #604 :tada:

All 33 comments

Yes, I'm planning to fully support ASP.NET 5. You can look at corresponding forum topic to learn how to plug in Hangfire dashboard and server into ASP.NET 5 application, but it works only with full CLR currently. Core CLR will be supported approximately in Hangfire 1.6, but there are a lot of work to implement this.

The main confusion is package casing. I've renamed HangFire to Hangfire approximately one year ago, but NuGet package identifiers were not renamed. It wasn't an issue before ASP.NET 5, but package names in project.json are case-sensitive:

  "dependencies": {
    "HangFire": "1.4.2",
    "Hangfire.Autofac": "1.1.0"
  },

I've seen some related issues in other projects, but when I wrote a support request to the NuGet Team asking to rename some related packages, nothing happened. Maybe @jeffhandley can help us to fix the NuGet package casing.

This is great! I am going to plug Hangfire into a ASP.NET 5 app (I'm currently on beta-6; with 4.5.2 runtime).
I'll let you know if I find any issue.

@jods4 Have you managed to get Hangfire working with beta-6?

@dzolnjan Yes I did. It seems to work for me, although I haven't done extensive tests yet. I can start the background worker and see the dashboard. I even managed to mix that up with the Oracle provider.

I don't remember if I had to do anything extra (than what is in the forum post above) to make it work. Do you have any issue getting it to work?

I found the forum link, copied that class in and yes it just works. Thanks for quick reply.

Anyone know how to use Dependency Injection of ASP.NET 5 in Hangfire. I can run Hangfire server, run simple task such as write log and see it in dashboard but cannot inject my service into and execute it, even I use default DI or Autofac.

@hieupious you need to configure Hangfire with a custom activator that relies on your DI (or ASP.NET's one for that matter).

More info here:
http://docs.hangfire.io/en/latest/background-methods/using-ioc-containers.html

@jods4 actuall, I tried but it didn't work.
I see in the documentation, they only override one method ActivateJob but I see in other library such as Hangfire.Autofac, they implement a lot. So I am not sure the correct way to do it.

@hieupious I use this:

using System;

namespace Hangfire.AspNet5
{
  public class AspNetJobActivator : JobActivator
  {
    private IServiceProvider provider;

    public AspNetJobActivator(IServiceProvider provider)
    {
      this.provider = provider;
    }

    public override object ActivateJob(Type jobType)
    {
      return provider.GetService(jobType) ?? Activator.CreateInstance(jobType);
    }
  }
}

Of course, the DI cannot work if you haven't registered your types into the IServiceProvider (typically in ConfigureServices().
That's why I added a fallback to simple .NET instanciation Activator.CreateInstance(jobType) so that you can create jobs that don't require DI without having to register them in the service container.

@jods4 thank you so much. And one more thing:
Where should we put the configuration for Hangfire like:
GlobalConfiguration.Configuration.UseActivator(new AspNetJobActivator(provider));
I intend to put in ConfigureServices(IServiceCollection services) after I registered my dependencies. But I don't know the provider instance of IServiceProvider come from? How can I get it?

I do this during Configure() it kinda make more sense I think.
The IServiceCollection is app.ApplicationServices (app is the IApplicationBuilder passed in parameter to Configure).

Thank you a lot. It worked for some kind of dependencies.
But in my case, it's not perfect. My service needs to inject another dependency (ApplicationDbContext from EntityFramework) via the constructor of the service:

private ApplicationDbContext dbContext; 
public ImportDataService(ApplicationDbContext dbContext)
        {
            this.dbContext = dbContext;
        }

But Hangfire only works with default constructor so I cannot inject entity framework although I've registered it before in ConfigureServices.

@hieupious Sorry I can't help you further without debugging your code.

I can tell you that injecting stuff in ctor does work for me if you have registered the classes properly in the service container before. I see no reason that it wouldn't work with EF DbContext.

Note that you need to register ImportDataService in ConfigureServices, not just ApplicationDbContext, as this is what you'll try to get out of the DI container.

I tried to debug but still cannot solve problem. Give your these information about my code, hope you can help.
In public void ConfigureServices(IServiceCollection services), I register the services:

services.AddEntityFramework().AddSqlServer().AddDbContext<ApplicationDbContext>(option => options.UseSqlServer(connection)); // EF DbContext
services.AddTransient<IImportDataService, ImportDataService>();

Then I configure Hangfire

GlobalConfiguration.Configuration.UseSqlServerStorage(Configuration["Data:DefaultConnection:ConnectionString"]);
GlobalConfiguration.Configuration.UseActivator(new ServiceJobActivator(app.ApplicationServices)); /// ServiceJobActivator is same with your implementation

After that, I add background job
BackgroundJob.Enqueue<ImportDataService>(i => i.CopyFileFromExternal());

Here is the code of my service:

public class ImportDataService : IImportDataService
{

private ApplicationDbContext dbContext; 

public ImportDataService(ApplicationDbContext dbContext)
        {
            this.dbContext = dbContext;
        }
public ImportDataService() 
        {
            // Hangfire require default constructor. 
            // When it executes my method, the default constructor is initialized. So I cannot assign value for dbContext 
        }
}

When I use without access dbContext, it can work but when access dbContext, it throw exception NullReferenceException. It means I still cannot inject EF DbContext into my ImportDataService.

That's my information. Hope you could get the idea and point out the problem. Thank you in advance

Wild guess: you register ImportDataService as IImportDataService.
I am not sure it will work, as I think Hangfire will request ImportDataService (at least that's how I did it, I added the implementation to DI).

Incredible! It works when I register services.AddTransient<ImportDataService, ImportDataService>();
Actually, I still don't understand why and how the problem could be solved with this solution.

I believe @jods4 is correct, your enqueue call should be:

BackgroundJob.Enqueue<IImportDataService>(i => i.CopyFileFromExternal());

... and you should then just need to register ImportDataService for IImportDataService:

services.AddTransient<IImportDataService, ImportDataService>();

Hangfire will then see that it should request an implementation of IImportDataService when it wants to activate the job and will request this from the AspNetJobActivator. If you place a breakpoint inside the AspNetJobActivator.ActivateJob implementation, you should then see IImportDataService being passed in as the type.

If you call the BackgroundJob.Enqueue method with ImportDataService instead of the interface, Hangfire will request ImportDataService as it doesn't know about the interface.

I should point out that this won't work with the default JobActivator instance, as it uses the .Net Activator.CreateInstance method to resolve the types. Activator.CreateInstance does not know that the interface is implemented by your class and therefore can't resolve it.

So glad I found this. Just adding a keyword to help others find:

DNX

I had similar problems with injecting a service that had a dependency on a DbContext, except in my case it was throwing a ObjectDisposedException. I think I read somewhere that EF manages its own scope internally? Anyway, as of beta8, I was able to ensure jobs run in a "request" scope by creating a new scope each time:

public override object ActivateJob(Type jobType)
{
    // Ensure jobs run in their own "request" scope.
    var scopeFactory = _serviceProvider.GetService<IServiceScopeFactory>();
    var scope = scopeFactory.CreateScope();
    var job = scope.ServiceProvider.GetService(jobType);

    if (job == null)
        throw new Exception($"Could not activate a job of type '{jobType.FullName}'.");

    return job;
}

I was able to get Hangfire to work with ASP.NET 5 RC1 using some code I found from @odinserj. I don't use dnxcore, only DNX 4.5.1.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseIISPlatformHandler();
    app.UseStaticFiles();

    SqlServerStorage sjs = new SqlServerStorage("Server=(localdb)\\mssqllocaldb;Database=TestDatabase;Trusted_Connection=True;MultipleActiveResultSets=true");
    JobStorage.Current = sjs;
    BackgroundJobServerOptions backgroundJobServerOptions = new BackgroundJobServerOptions()
    {
        Queues = new[] { "critical", "normal", "low" }
    };
    app.UseHangfireDashboard("/hf", new Hangfire.DashboardOptions(), sjs);
    app.UseHangfireServer(new Hangfire.BackgroundJobServerOptions(), sjs);

    app.UseMvc();
}

I am able to access the Dashboard when I use IIS Express, however with DNX (dnx web) I can't get to the Hangfire Dashboard. Am I missing something? Is this a known issue?

Hosting environment: Development
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
Request starting HTTP/1.1 GET http://localhost:5000/hf
info: Microsoft.AspNet.Hosting.Internal.HostingEngine[2]
Request finished in 0ms 401

I have it running under dnx46. I can't help with your particular scenario (my usage is self-hosting inside a Windows Service), but thought you would like to know it is in fact possible to use with at least one flavor of DNX.

@margagn Can't help much, I also have it running in IIS Express on top of .net 4.6.

That said, the ASP.NET 5 RC is not really running on top of IIS, it's basically running dnx web in a separate process that is spawn by IIS. So I really don't see why it wouldn't work when doing that directly.

I am still not able to get the Hangfire Dashboard to show with DNX 4.5.1 and Kestrel (1.0.0-rc1-final). For some reason I get a 401 Unauthorized when I try to access the dashboard. With IIS Express, I can browse the Dashboard.

Any other idea what could be wrong? Is there an official way to integrate Hangfire with ASP.NET 5?

Ah, you need to allow anonymous requests. Implement something like this:

public class HangfireAuthorizationFilter : IAuthorizationFilter
{
    public bool Authorize(IDictionary<string, object> owinEnvironment)
    {
        //// In case you need an OWIN context, use the next line,
        //// `OwinContext` class is the part of the `Microsoft.Owin` package.
        //var context = new OwinContext(owinEnvironment);

        //// Allow all authenticated users to see the Dashboard (potentially dangerous).
        //return context.Authentication.User.Identity.IsAuthenticated;

        return true;
    }
}
app.UseHangfireDashboard("/admin/hangfire", new DashboardOptions
{
    AuthorizationFilters = new[] { new HangfireAuthorizationFilter() },
    AppPath = "/admin"
});

@stajs It works! Thanks! :)

Are you planning to get the IApplicationBuilder extension methods released as part of some NuGet package, so people can just add that NuGet package and call UseHangFireDashboard() in the their startup classes for ASP.NET 5? At the moment I imagine people are copying and pasting the same bit of code around! :)

Yep I'm sitting here wasting time on this trying to use Hangfire for the first time.

@odinserj Can you please update on current status of Hangfire for ASP.NET Core?
You stated that in v1.6 you will add support for .NET Core application.
I see you released v1.6 beta.

@radenkozec, I said _approximately_ in 1.6 :wink: But what is the status of .NET Core itself? DNX, dotnet-cli, project.json, MSBuild again. All these changes require converting project and build tools left and right.

For smaller projects with fewer packages, like Dapper, Newtonsoft.Json it makes sense, but with Hangfire I prefer to wait at least for RTM to not to waste the time, hoping that they make the project.jsonvs .csproj decision in that release . And I'm not alone.

So when exactly?

I don't know

FYI, RTM date is one week away (27 June):

https://github.com/aspnet/Home/wiki/Roadmap

18 minutes in to this video, they reveal the RTM will be made at a RedHat conference:

https://channel9.msdn.com/Events/dotnetConf/2016/NET-Conf-Day-1-Keynote-Scott-Hunter

Yes, the _runtime_ will be RTM on Jun, 27. But the final version of _tooling_ will be available later.

The tooling that supports .NET Core and ASP.NET Core, including the new command line tools and bits that plug into Visual Studio & Visual Studio Code, aren’t there yet. It’s going to change before it stabilizes. We’re going to call this tooling release Preview 1.

Since they are planning to move back to *.csproj files again, it makes sense to convert only subset of projects before the final release. The tooling is the hardest part, it is relatively easy to patch the source files for .NET Core.

Yes, you read it right. Project.JSON for ASP.NET Core is going away. One of the biggest change with ASP.NET Core 1.0 was moving towards JSON over XML for configuration settings. And Project.json is the way to define dependencies, managing your runtime frameworks, compilation settings, adding scripts to execute at different events (Prebuild, Postbuild etc.).

.NET Core Schedule
Earlier, .NET team released the schedule and roadmap for .NET Core RC 2. And as per the schedule, .NET Core and ASP.NET Core 1.0 RC2 runtime and libraries will be available in mid-May and tooling will be Preview 1 and bundled with this release. By end of June, we should have .NET Core and ASP.NET Core 1.0 RTM (release) runtime and libraries and tooling will be Preview 2 and bundled with this release. Just to let you know that tooling includes .NET Core command line tools (dotnet cli), Visual Studio and Visual Studio Code that enable you to work with .NET Core projects.

Till tooling preview 1 release, nothing will change with respect to Project.json. But with tooling preview 2 release, following change is likely to happen.

The extension for project file will be changed to .csproj from .xproj. And this transition will be automatic and will not require you to change your existing projects. So after installing tooling preview 2, when you open your .xproj with VS 2015, then Visual studio will prompt you and convert that for you. But Project.json will stay next to .csproj.
Project.json for ASP.NET Core is going away
Microsoft will continue to make changes and stabilize the tooling until it RTMs with Visual Studio “15”. So during this phase, project related settings (compilation, building, publishing) will move out of Project.json and it will be part of .csproj file. Project.json may stay or get renamed as it will be still having nuget package dependencies.

Few things to note,

.csproj file will be XML based, not JSON based.
It is not going to have list of files that we have as of now. So it will be small and easier to manage.
There will be a new dotnet cli command which will allow to edit .csproj. So you don’t need any IDE to edit it.
Please watch the ASP.NET Community Standup video for more details.

Source: http://www.talkingdotnet.com/news-project-json-asp-net-core-going-away/

.NET Core support is almost here, see #604 :tada:

Was this page helpful?
0 / 5 - 0 ratings