我在 web-api 控制器操作中排队 Hangfire 作业,但我需要为任务注入服务。
这是代码:
[HttpPost]
public string DoSomething() => _backgroundJobClient.Enqueue<MyStorageService>(s => EnqueueJob(s));
[JobDisplayName("My job"), DisableConcurrentExecution(10 * 60)]
private static async void EnqueueJob(MyStorageService myservice)
{
await myservice.DoAsync();
}
但是,
MyStorageService
似乎捕获了一些缺失的上下文,因为我得到以下异常。
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: variable 's' of type 'MyStorageService' referenced from scope '', but it is not defined
at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda)
at System.Linq.Expressions.Expression`1.Compile()
at Hangfire.Common.ExpressionUtil.CachedExpressionCompiler.Compiler`2.<>c__DisplayClass7_0.<CompileFromFingerprint>b__0(ExpressionFingerprintChain _)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Hangfire.Common.ExpressionUtil.CachedExpressionCompiler.Compiler`2.CompileFromFingerprint(Expression`1 expr)
at Hangfire.Common.ExpressionUtil.CachedExpressionCompiler.Compiler`2.Compile(Expression`1 expr)
at Hangfire.Common.ExpressionUtil.CachedExpressionCompiler.Process[TModel,TValue](Expression`1 lambdaExpression)
at Hangfire.Common.CachedExpressionCompiler.Wrap(Expression arg)
at Hangfire.Common.CachedExpressionCompiler.Evaluate(Expression arg)
at Hangfire.Common.Job.GetExpressionValue(Expression expression)
at System.Linq.Enumerable.SelectIListIterator`2.ToArray()
MyStorageService
使用 Entity Framework / DbContext 来运行查询。
更新一:
MyStorageService
被添加为 program.cs
中的范围服务:
builder.Services.AddScoped<MyStorageService>();
更新二:
我尝试将 .net 7 文档应用于我的服务: https://learn.microsoft.com/en-us/dotnet/core/extensions/scoped-service
注入
IServiceProvider serviceProvider
以显式创建范围。这会将代码更改为:
[HttpPost]
public string ImportAsync()
{
using var scope = _serviceProvider.CreateScope();
var scopedProcessingService = scope.ServiceProvider.GetRequiredService<MyStorageService>();
return _backgroundJobClient.Enqueue(() => EnqueueJob(scopedProcessingService));
}
[JobDisplayName("My job"), DisableConcurrentExecution(10 * 60)]
public static async Task EnqueueJob(MyStorageService myservice)
{
await myservice.DoAsync();
}
这允许现在调用
DoAsync()
方法,但是一些依赖项似乎没有被注入到 MyStorageService 内部,因为一些对象现在(仅在这种情况下)not set to an instance of an object
.
错误:
Failed to process the job '3': an exception occurred. Retry attempt 1 of 10 will be performed in 00:00:44.
System.NullReferenceException: Object reference not set to an instance of an object.
at ..MyStorageService.DoAsync() in ..\MyStorageService.cs:line 52
at ..MyController.Enqueue(MyStorageService myservice) in ..MyController.cs:line 67
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
warn: Hangfire.AutomaticRetryAttribute[0]
...
Hangfire 中的依赖注入只对方法调用的目标有效。 你应该像这样重构你的代码:
[HttpPost]
public string DoSomething() => _backgroundJobClient.Enqueue<MyStorageService>(s => s.EnqueueJob());
其中
EnqueueJob
是类MyStorageService
的实例方法。 (避免使用async void
方法)
[JobDisplayName("My job"), DisableConcurrentExecution(10 * 60)]
public async Task EnqueueJob()
{
await this.DoAsync();
}