【问题标题】:Hangfire server unable to pick job in case of strategy design pattern在策略设计模式的情况下,Hangfire 服务器无法选择工作
【发布时间】:2018-03-27 13:24:07
【问题描述】:

我有以下申请:

1) Mvc 应用程序:Hangfire 客户端,我将从那里将作业排入队列并托管 仪表板。这个应用程序将包含我的类库的引用。

2) 控制台应用程序:Hangfire 服务器将存在于此控制台应用程序中。

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

我在 strategy design pattern. 之后的类库中具有如下结构

代码取自以下:Reference:

接口:

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.0172303Z”、“ExceptionType”: "System.MissingMethodException", "ExceptionMessage": "没有无参数 为此对象定义的构造函数。", "ExceptionDetails": “System.MissingMethodException:未定义无参数构造函数 对于此对象。\r\n 在 System.RuntimeTypeHandle.CreateInstance(RuntimeType 类型,布尔值 publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)\r\n at System.RuntimeType.CreateInstanceSlow(布尔 publicOnly,布尔 skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)\r\n at System.Activator.CreateInstance(类型类型,布尔非公共)\r\n at System.Activator.CreateInstance(类型类型)\r\n at Hangfire.JobActivator.SimpleJobActivatorScope.Resolve(类型类型)\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 at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext 上下文,IEnumerable`1 过滤器)\r\n at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext 上下文)\r\n 在 Hangfire.Server.Worker.PerformJob(BackgroundProcessContext 上下文, IStorageConnection 连接,字符串 jobId)"

我没有得到应该为这个结构设置的hangfire,因为我这里没有涉及任何 IOC 容器。

有人可以帮我解决这个问题吗?

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

【问题讨论】:

    标签: c# asp.net-mvc strategy-pattern hangfire


    【解决方案1】:

    答案就在异常中

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

    【讨论】:

    • 在这一行抛出错误:var op = operations.FirstOrDefault(o => o.AppliesTo(type));值不能为空。操作为空
    • @User 你试过public List&lt;IOperation&gt; Operations {get; set;} = new List&lt;IOperation&gt;(); 吗?
    • @jbl 我没有按照你的建议尝试这个选项。让我试试,我会更新你。但是为什么列出而不是数组?
    • @User 请查看我的编辑 - 在 ctor 中初始化 Operations,但重要的是在设置属性值时使用正确的大小写(之前是 operations
    • @Alex 现在我的操作计数为 0,因此我得到 Exception:Type not registered
    猜你喜欢
    • 2016-11-11
    • 2011-09-30
    • 2018-10-11
    • 1970-01-01
    • 2018-08-16
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    • 2019-01-25
    相关资源
    最近更新 更多