Разрешение зависимостей Hangfire /HttpContext в.NET Core Startup
Я установил и настроил Hangfire в классе запуска моего веб-приложения.NET Core следующим образом (с удалением большого количества кода, отличного от Hangfire):
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseHangfireServer();
//app.UseHangfireDashboard();
//RecurringJob.AddOrUpdate(() => DailyJob(), Cron.Daily);
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<AppSettings>(Configuration);
services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IPrincipal>((sp) => sp.GetService<IHttpContextAccessor>().HttpContext.User);
services.AddScoped<IScheduledTaskService, ScheduledTaskService>();
services.AddHangfire(x => x.UseSqlServerStorage(connectionString));
this.ApplicationContainer = getWebAppContainer(services);
return new AutofacServiceProvider(this.ApplicationContainer);
}
}
public interface IScheduledTaskService
{
void OverduePlasmidOrdersTask();
}
public class ScheduledTaskService : IScheduledTaskService
{
public void DailyJob()
{
var container = getJobContainer();
using (var scope = container.BeginLifetimeScope())
{
IScheduledTaskManager scheduledTaskManager = scope.Resolve<IScheduledTaskManager>();
scheduledTaskManager.ProcessDailyJob();
}
}
private IContainer getJobContainer()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new BusinessBindingsModule());
builder.RegisterModule(new DataAccessBindingsModule());
return builder.Build();
}
}
Как видите, я использую Autofac для DI. Я настроил все, чтобы вводить новый контейнер каждый раз при выполнении задания Hangfire.
В настоящее время у меня есть UseHangfireDashboard()
а также комментарий для добавления моей повторяющейся работы закомментирован, и я получаю следующую ошибку в строке ссылки IPrincipal
:
System.NullReferenceException: 'Ссылка на объект не установлена на экземпляр объекта.'
Я понимаю, что Hangfire не имеет HttpContext
, Я не совсем уверен, почему он даже запускает эту строку кода для потока Hangfire. В конечном итоге мне понадобится разрешить служебную учетную запись для моей IPrincipal зависимости.
Как я могу решить мою проблему с Hangfire и HttpContext?
3 ответа
Основная проблема, с которой я сталкиваюсь сейчас, заключается в том, что когда я добавляю UseHangfireServer, мне также необходимо разрешить HttpContext.
Найдено здесь Использование контейнеров IoC
HttpContext
не доступенИнформация запроса недоступна во время создания целевого типа. Если вы регистрируете свои зависимости в области запроса (
InstancePerHttpRequest
в автофаке,InRequestScope
в Ninject и т. д.) во время активации задания будет сгенерировано исключение.Таким образом, весь граф зависимостей должен быть доступен. Либо зарегистрируйте дополнительные службы без использования области запроса, либо используйте отдельный экземпляр контейнера, если ваш контейнер IoC не поддерживает регистрации зависимостей для нескольких областей.
Чтобы разрешить зависимые области в ядре.net, потребуется запрос, который недоступен при запуске при регистрации и активации заданий. Поэтому убедитесь, что ваша служба, необходимая для активации во время запуска, не зарегистрирована, используя время жизни в заданной области.
services.AddTransient<IScheduledTaskManager, ScheduledTaskManageImplementation>();
Теперь осталось только настроить приложение на использование этой службы с повторяющимся заданием,
public class Startup {
public IContainer ApplicationContainer { get; private set; }
public Startup(IHostingEnvironment env) {
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void Configuration(IApplicationBuilder app) {
// app.AddLogger...
//add hangfire features
app.UseHangfireServer();
app.UseHangfireDashboard();
//Add the recurring job
RecurringJob.AddOrUpdate<IScheduledTaskManager>(task => task.ProcessDailyJob(), Cron.Daily);
//app.UseMvc...
//...other code
}
public IServiceProvider ConfigureServices(IServiceCollection services) {
// Adding custom services
services.AddTransient<IScheduledTaskManager, ScheduledTaskManageImplementation>();
//add other dependencies...
// add hangfire services
services.AddHangfire(x => x.UseSqlServerStorage("<connection string>"));
//configure Autofac
this.ApplicationContainer = getWebAppContainer(services);
//get service provider
return new AutofacServiceProvider(this.ApplicationContainer);
}
IContainer getWebAppContainer(IServiceCollection service) {
var builder = new ContainerBuilder();
builder.RegisterModule(new BusinessBindingsModule());
builder.RegisterModule(new DataAccessBindingsModule());
builder.Populate(services);
return builder.Build();
}
//...other code
}
Рекомендации
Почему Hangfire пытается разрешить класс.NET Core Startup?
Hangfire не хранит лямбда-выражения в базе данных, он хранит тип и вызываемый метод. Затем, когда запланированное задание должно быть запущено, оно разрешает тип из контейнера и вызывает метод.
В вашем случае метод включен Startup
,
Вы можете зарегистрироваться Startup
с Autofac, если хотите, но, вероятно, проще всего иметь службу запланированных задач:
AddOrUpdate<IScheduledTaskService>(x => x.DailyTask(), Cron.Daily);
Я не уверен в типе для jobmanager, но вы можете разрешить зависимость от контейнера, используя область. Вы захотите разрешить из области в операторе using, чтобы предотвратить утечки памяти. Посмотреть документы Autofac
// not sure what type "jobManager" is
TYPE jobManager;
using(var scope = ApplicationContainer.BeginLifetimeScope())
{
jobManager = scope.Resolve<TYPE>();
}
RecurringJob.AddOrUpdate( ... );