在采用策略设计模式的情况下,Hangfire服务器无法接班 [英] Hangfire server unable to pick job in case of strategy design pattern

查看:69
本文介绍了在采用策略设计模式的情况下,Hangfire服务器无法接班的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下申请:

1) Mvc应用:Hangfire客户端,我将从这里开始排队工作并托管 仪表板.此应用程序将包含我的班级库的引用.

2)控制台应用程序:Hangfire服务器将驻留在此控制台应用程序中.

3)类库:hangfire服务器(控制台应用程序)和hangfire客户端(asp.net mvc)之间的共享库,我的长期运行代码将驻留在该库中. 将执行该库的代码.

我在类库中具有以下结构,遵循策略设计模式.

代码摘自以下内容:参考:

接口:

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

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

操作:

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();
    }
}

策略:

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);
    }
}

用法:

// 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));

但是hangfire服务器无法选择工作,当我检查state table时,我看到了如下错误:

"FailedAt":"2018-03-21T13:14:46.0​​172303Z","ExceptionType": "System.MissingMethodException","ExceptionMessage":没有无参数 为此对象定义的构造函数.," ExceptionDetails: "System.MissingMethodException:未定义无参数构造函数 为此对象.\ r \ n位于 System.RuntimeTypeHandle.CreateInstance(RuntimeType类型,布尔 publicOnly,布尔值noCheck,布尔值& canBeCached, RuntimeMethodHandleInternal& ctor,布尔值和放大器; bNeedSecurityCheck)\ r \ n位于 System.RuntimeType.CreateInstanceSlow(布尔publicOnly,布尔 skipCheckThis,布尔值fillCache,StackCrawlMark& stackMark)\ r \ n位于 System.Activator.CreateInstance(类型,布尔值非公共)\ r \ n位于 System.Activator.CreateInstance(Type type)\ r \ n在 Hangfire.JobActivator.SimpleJobActivatorScope.Resolve(Type type)\ r \ n 在Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext 上下文)\ r \ n位于 Hangfire.Server.BackgroundJobPerformer.<> c__DisplayClass8_0.b__0()\ r \ n 在 Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter 过滤器,PerformingContext preContext,Func 1延续)\ r \ n位于 Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext 上下文,即IEnumerable`1个过滤器)\ r \ n位于 Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext 上下文)\ r \ n位于 Hangfire.Server.Worker.PerformJob(BackgroundProcessContext上下文, IStorageConnection连接,字符串jobId)"

由于这里没有任何IOC容器,因此我没有为此结构设置hanghang.

有人可以帮我吗?

演示项目: https://www.dropbox.com/s/bfjr58y6azgmm3w/HFDemo.zip?dl = 0

解决方案

答案就在其中

No parameterless constructor defined for this object.

您的OperationStrategy没有无参数的构造函数,因此,当Hangfire尝试创建此对象时,它不能-而是通过反射来实现的. Hangfire无法访问您在计划作业时使用的实例,因此会尝试重新创建它.它不能做到的.

您可以添加一个无参数的构造函数,并将Operations设置为公共集合.

这将使您可以完全按照当前的状态使用ctor,还可以将对象序列化,由Hangfire存储,然后在尝试运行时反序列化和创建.

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;}
}

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

解决方案

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;}
}

这篇关于在采用策略设计模式的情况下,Hangfire服务器无法接班的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆