Hangfire server unable to pick job in case of stra

2019-01-26 21:39发布

问题:

I am having following application :

1) Mvc app : Hangfire client from where i will just enqueue jobs and host dashboard.This app will contains reference of my class library.

2) Console App : Hangfire server will live in this console application.

3) Class library : Shared library between hangfire server(Console app) and hangfire client(asp.net mvc) where my long running code will reside.Hangfire server will execute code of this library.

I am having structure like below in Class library following strategy design pattern.

Code taken from following : Reference:

Interfaces:

public interface IOperation
{
    Output Process(Input input);
    bool AppliesTo(Type type);
}

public interface IOperationStrategy
{
    Output Process(Type type,Input input);
}

Operations:

public class Add : IOperation
{
    public bool AppliesTo(Type type)
        {
            return typeof(Add).Equals(type);
        }

    public Output Process(Input input)
    {
        // Implementation
        return new Output();
    }
}

public class Multiply : IOperation
{
     public bool AppliesTo(Type type)
        {
            return typeof(Multiply).Equals(type);
        }

    public Output Process(Input input)
    {
        // Implementation
        return new Output();
    }
}

Strategy:

public class OperationStrategy : IOperationStrategy
{
    private readonly IOperation[] operations;

    public OperationStrategy(params IOperation[] operations)
    {
        if (operations == null)
            throw new ArgumentNullException(nameof(operations));
        this.operations = operations;
    }

    public Output Process(Type type, Input input)
    {
        var op = operations.FirstOrDefault(o => o.AppliesTo(type));
        if (op == null)
            throw new InvalidOperationException($"{operation} not registered.");

        return op.Process(input);
    }
}

Usage:

// Do this with your DI container in your composition 
// root, and the instance would be created by injecting 
// it somewhere.
var strategy = new OperationStrategy(
    new Add(), // Inject any dependencies for operation here
    new Multiply()); // Inject any dependencies for operation here

// And then once it is injected, you would simply do this.
var input = new Input { Value1 = 2, Value2 = 3 };
BackgroundJob.Enqueue(() => strategy.Process(typeof(Add), input));

But hangfire server is unable to pick job and when i check state table,i get to see error like below :

"FailedAt": "2018-03-21T13:14:46.0172303Z", "ExceptionType": "System.MissingMethodException", "ExceptionMessage": "No parameterless constructor defined for this object.", "ExceptionDetails": "System.MissingMethodException: No parameterless constructor defined for this object.\r\n at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)\r\n at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)\r\n at System.Activator.CreateInstance(Type type, Boolean nonPublic)\r\n at System.Activator.CreateInstance(Type type)\r\n at Hangfire.JobActivator.SimpleJobActivatorScope.Resolve(Type type)\r\n at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context)\r\n at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass8_0.b__0()\r\n at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func 1 continuation)\r\n at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable`1 filters)\r\n at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext context)\r\n at Hangfire.Server.Worker.PerformJob(BackgroundProcessContext context, IStorageConnection connection, String jobId)"

I am not getting what should be the hangfire set up for this structure as i dont have any IOC container involved here.

Can anybody please help me with this?

Demo project : https://www.dropbox.com/s/bfjr58y6azgmm3w/HFDemo.zip?dl=0

回答1:

The answer is right there in the exception

No parameterless constructor defined for this object.

Your OperationStrategy doesn't have a parameterless constructor, so when Hangfire tries to create this object, it can't - it does so through reflection. Hangfire does not have access to the instance that you used when scheduling the job, it attempts to recreate it. Which it can't do.

You could add a parameterless constructor, and make Operations a public collection.

This will enable you to use your ctor exactly as you currently are, but also allow the object to be serialized, stored by Hangfire, then deserialized and created when it tries to run.

public class OperationStrategy : IOperationStrategy
{
    // paramaterless ctor
    public OperationStrategy()
    {
        Operations = new List<Operation>(); 
    }

    public OperationStrategy(params IOperation[] operations)
    {
        if (operations == null)
            throw new ArgumentNullException(nameof(operations));

        Operations = operations;
    }

    public Output Process(Type type, Input input)
    {
        var op = Operations.FirstOrDefault(o => o.AppliesTo(type));

        if (op == null)
            throw new InvalidOperationException($"{operation} not registered.");

        return op.Process(input);
    }

    //property - this can be deserialized by Hangfire
    public List<IOperation> Operations {get; set;}
}