9

I'm using structuremap with hangfire on asp.net core, no error on application but hangfire not process queue/schedule task even though data already on database. Here my snippet configuration

public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        // setup automapper
        var config = new AutoMapper.MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new AutoMapperProfileConfiguration());
        });

        var mapper = config.CreateMapper();
        services.AddSingleton(mapper);


        // Bind settings parameter
        services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
        services.AddDbContext<DefaultContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddHangfire(options => 
            options.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));

        services.AddMvc();
        // ASP.NET use the StructureMap container to resolve its services.
        return ConfigureIoC(services);
    }

    public IServiceProvider ConfigureIoC(IServiceCollection services)
    {
        var container = new Container();

        GlobalConfiguration.Configuration.UseStructureMapActivator(container);

        container.Configure(config =>
        {
            // Register stuff in container, using the StructureMap APIs...
            config.Scan(_ =>
            {
                _.AssemblyContainingType(typeof(Startup));
                _.WithDefaultConventions();
                _.AddAllTypesOf<IApplicationService>();
                _.ConnectImplementationsToTypesClosing(typeof(IOptions<>));
            });
            config.For<JobStorage>().Use(new SqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));
            config.For<IJobFilterProvider>().Use(JobFilterProviders.Providers);

            config.For<ILog>().Use(c => LoggerFactory.LoggerFor(c.ParentType)).AlwaysUnique();
            XmlDocument log4netConfig = new XmlDocument();
            log4netConfig.Load(File.OpenRead("log4net.config"));

            var repo = LogManager.CreateRepository(
                Assembly.GetEntryAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy));

            XmlConfigurator.Configure(repo, log4netConfig["log4net"]);
            //Populate the container using the service collection
            config.Populate(services);
        });

        return container.GetInstance<IServiceProvider>();
    }

Is there any better way using hangfire and structuremap on asp.net core? Am I missing something so hangfire not working properly?

My Hangfire structure map implementation

using Hangfire;
using StructureMap;

namespace Lumochift.Helpers
{
    /// <summary>
    /// Bootstrapper Configuration Extensions for StructureMap.
    /// </summary>
    public static class StructureMapBootstrapperConfigurationExtensions
    {
        /// <summary>
        /// Tells bootstrapper to use the specified StructureMap container as a global job activator.
        /// </summary>
        /// <param name="configuration">Bootstrapper Configuration</param>
        /// <param name="container">StructureMap container that will be used to activate jobs</param>
        public static void UseStructureMapActivator(this GlobalConfiguration configuration, IContainer container)
        {
            configuration.UseActivator(new StructureMapJobActivator(container));
        }
    }
}


using Hangfire;
using StructureMap;
using System;

namespace Lumochift.Helpers
{
    public class StructureMapJobActivator : JobActivator
    {
        private readonly IContainer _container;

        /// <summary>
        /// Initializes a new instance of the <see cref="StructureMapJobActivator"/>
        /// class with a given StructureMap container
        /// </summary>
        /// <param name="container">Container that will be used to create instances of classes during
        /// the job activation process</param>
        public StructureMapJobActivator(IContainer container)
        {
            if (container == null) throw new ArgumentNullException(nameof(container));

            _container = container;
        }

        /// <inheritdoc />
        public override object ActivateJob(Type jobType)
        {
            return _container.GetInstance(jobType)
        }

        /// <inheritdoc />
        public override JobActivatorScope BeginScope(JobActivatorContext context)
        {
            return new StructureMapDependencyScope(_container.GetNestedContainer());
        }


        private class StructureMapDependencyScope : JobActivatorScope
        {
            private readonly IContainer _container;

            public StructureMapDependencyScope(IContainer container)
            {
                _container = container;
            }

            public override object Resolve(Type type)
            {
                return _container.GetInstance(type);
            }

            public override void DisposeScope()
            {
                _container.Dispose();
            }
        }
    }
}

Example hangfire call on controller

    BackgroundJob.Enqueue<CobaService>((cb) => cb.GetCoba());
    BackgroundJob.Schedule<CobaService>((cb) => cb.GetCoba(), TimeSpan.FromSeconds(5) );

Screenshot: Hangfire queue Scheduled job

Moch Lutfi
  • 552
  • 10
  • 27
  • Have you looked at this? https://github.com/cocowalla/Hangfire.StructureMap – Paul Totzke Apr 12 '17 at 16:11
  • yes, I already rewrite those library to working with .net core. but unexpected result. no error and not working – Moch Lutfi Apr 13 '17 at 02:52
  • Yeah, I am unsure if your rewrite for that library is valid. "Package Hangfire.StructureMap 1.6.0 is not compatible with netcoreapp1.1 (.NETCoreApp,Version=v1.1). Package Hangfire.StructureMap 1.6.0 supports: net45 (.NETFramework,Version=v4.5)" – Paul Totzke Apr 13 '17 at 04:03
  • What version of Hangfire do you use? Do you have any output from Hangfire in logs? Maybe you could post some self-contained sample reproducing the problem? – starteleport Apr 16 '17 at 16:56
  • @PaulTotzke Yes, maybe I've implement it wrong. but at least can send job to hangfire. but hangfire stop fetch running jobs which already queue. – Moch Lutfi Apr 17 '17 at 01:56
  • @starteleport I'm using these version. "Hangfire.Core": "1.6.12", "Hangfire.AspNetCore": "1.6.12", "Hangfire.SqlServer": "1.6.12", There is no error, but there is common error after 2 or three running. cannot connect sql server. I will update question soon with error log and my implementation hangfire.structure map. – Moch Lutfi Apr 17 '17 at 01:56
  • @MochLutfi, you say "There is no error, but there is common error after 2 or three running. cannot connect sql server.". What do you mean by "common error"? Do you have logging set up in your application? Try to [set-up logging in Hangfire](http://docs.hangfire.io/en/latest/configuration/configuring-logging.html) and see, what's there. For example, here is what it writes to my logs when I start my app: https://gist.github.com/anonymous/3014f4e3d944d3998192facdb93e11a1 – starteleport Apr 19 '17 at 14:43
  • @starteleport already put logger. but only for `BackgroundJob.Enqueue((cb) => cb.GetCoba());` can be logged but for scheduled queue no log tracked. Take a look on comment gist. trying to figure out. https://gist.github.com/anonymous/3014f4e3d944d3998192facdb93e11a1 I will create full simple project on github. – Moch Lutfi Apr 19 '17 at 14:56
  • @MochLutfi, seems like your logging framework doesn't get picked up by Hangfire. Could you try configuring it manually? See link "set-up logging in Hangfire" above. – starteleport Apr 19 '17 at 15:05
  • @starteleport I'm trying to setup manually on empty project. I will inform if already done. – Moch Lutfi Apr 19 '17 at 15:11

3 Answers3

3

You should use Hangfire.AspNetCore nuget package.

It uses AspNetCoreJobActivator as the default job activator, so you don't have to create your own.

AspNetCoreJobActivator uses IServiceScope.ServiceProvider.GetRequiredService(type) method to resolve dependencies. The same ServiceProvider, that you return from ConfigureServices, with all the services configured by structuremap.

Implementation of AddHangfire method

sabvente
  • 144
  • 8
2

You should return StructureMapDependencyScope with nested container from BeginScope:

public override JobActivatorScope BeginScope(JobActivatorContext context)
{
    return new StructureMapDependencyScope(_container.GetNestedContainer());
}

Also, I would not mix container with Activator.CreateInstance and just go with _container.GetInstance(jobType) in ActivateJob

starteleport
  • 1,231
  • 2
  • 11
  • 21
  • I change my code to use your advice. and remove mixed container `Activator.CreateInstance` with `container.GetInstance(jobType) in `ActivateJob` but the problem still occur. Just can enqueue the job but never activated. Even though using fire and forget job `BackgroundJob.Enqueue(() => Console.WriteLine("Simple!"));` – Moch Lutfi Apr 18 '17 at 01:36
  • @MochLutfi, is your db ok? Can you verify, where the connection timeout exception comes from? Is it Hangfire or IoC container initialization code? – starteleport Apr 18 '17 at 16:33
  • no error exception occur. everything running without error but job not activated. – Moch Lutfi Apr 19 '17 at 01:32
  • I gave bounty to your answer because you pointing my wrong code before, but the main problem is on SQL SERVER version that I used.thanks for helping. – Moch Lutfi Apr 20 '17 at 01:46
0

The main problem is not on implementation structure map with hangfire. at first though I think this is because SQL server version but after downgrade downgrade the problem still same. only queue but no job activated. After change the configuration setting I found the problem.

"DefaultConnection": "Server=127.0.0.1,1434;Database=db;User Id=sa;Password=pass;MultipleActiveResultSets=true"

this is the problem, I use port 1434 after I changed using without specified port or using port 1433 everything running normally. no job stuck on queue anymore.

Moch Lutfi
  • 552
  • 10
  • 27