Активатор заданий Autofac с исключением исключения Hangfire DependencyResolutionException
Моя структура проекта такая же как:
https://github.com/MarlabsInc/webapi-angularjs-spa
Я следовал инструкциям в:
http://docs.hangfire.io/en/latest/background-methods/using-ioc-containers.html
Итак, я создал контейнерный активатор заданий.
В моем Bootstrapper.cs
containerBuilder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().AsImplementedInterfaces().InstancePerApiRequest();
containerBuilder.RegisterType<UnitOfWork>().As<IUnitOfWork>().AsImplementedInterfaces().InstancePerApiRequest();
containerBuilder.RegisterApiControllers(System.Reflection.Assembly.GetExecutingAssembly());
IContainer container = containerBuilder.Build();
Hangfire.GlobalConfiguration.Configuration
.UseAutofacActivator(container);
JobActivator.Current = new AutofacJobActivator(container);
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
Мой класс запуска имеет метод:
GlobalConfiguration.Configuration
.UseSqlServerStorage("entitiesDB",
new SqlServerStorageOptions
{
PrepareSchemaIfNecessary = false,
InvisibilityTimeout = TimeSpan.FromMinutes(30)
});
app.UseHangfireDashboard();
app.UseHangfireServer();
В контроллере: я пытаюсь обновить статус 2000 счетов-фактур как "Одобрено". Таким образом, метод прост:
foreach(int id in invoiceIds)
{
BackgroundJob.Enqueue<IInvoiceService>(a => a.UpdateInvoice(id));
}
Теперь, когда я запрашиваю в SQL:
select * from HangFire.[State]
Я получаю следующее исключение в столбце данных:
{"FailedAt": "2015-07-07T10: 00: 40.9454943Z", "ExceptionType": "Autofac.Core.DependencyResolutionException", "ExceptionMessage": "Никакая область с тегом, совпадающим с" AutofacWebRequest ", не видна из области в для которого был запрошен экземпляр. Как правило, это указывает на то, что компонент, зарегистрированный как запрос на HTTP, запрашивается компонентом SingleInstance() (или аналогичным сценарием). При веб-интеграции всегда запрашивают зависимости из DependencyResolver.Current или ILifetimeScopeProvider.RequestLifetime., никогда из самого контейнера.","ExceptionDetails":"Autofac.Core.DependencyResolutionException: ни одна область с тегом, совпадающим с 'AutofacWebRequest', не видна из области, в которой был запрошен экземпляр. Обычно это указывает на то, что компонент зарегистрирован согласно -HTTP-запрос запрашивается компонентом SingleInstance() (или аналогичным сценарием). При веб-интеграции всегда запрашивают зависимости из DependencyResolver.Current или ILifetimeScopeProvider.Request. Время жизни, никогда не из самого контейнера.\ R \n в Autofac.Core.Lifetime.MatchingScopeLifetime.FindScope(ISharingLifetimeScope mostNestedVisibleScope)\r\n в Autofac.Core.Resolving.InstanceLookup..ctor(регистрация IComponent IScopeLoveScopeLoveScopes, IRSifeSolveOsVision, Т.е.
1 parameters)\r\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1 параметры) \ r \n в Autofac.Core.Resolving.ResolveOperation.ResolveComponent(регистрация IComponentRegistration, IEnumerable1 parameters)\r\n at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable
1 параметры) \ r \n в Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(регистрация IComponentRegistration, IEnumerable1 parameters)\r\n at Autofac.Core.Container.ResolveComponent(IComponentRegistration registration, IEnumerable
1 параметр) \ r \n в Autofac.ResolutionExtensions.TryResolveService(контекст IComponentContext, сервис службы, IEnumerable1 parameters, Object& instance)\r\n at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable
1 параметры) \ r \n в Autofac.ResolutionExtensions.Resolve(контекст IComponentContext, тип serviceType, параметры IEnumerable`1) \ r \n в Autofac.ResolutionExtensions.Resolve(контекст IComponentContext, тип serviceType) \ r \n в Hangfire.AcofJ.ActivateJob(Тип jobType)\r\n at Hangfire.Common.Job.Activate(активатор JobActivator)"}
Может кто-нибудь, пожалуйста, помогите мне понять, что я делаю неправильно?
1 ответ
Ты используешь InstancePerApiRequest
поэтому ваш контейнер IoC знает, как создать новый экземпляр для каждого запроса API.
Но ваше фоновое задание не выполняется внутри запроса API, поэтому ваш контейнер IoC не знает, как разрешить ваши зависимости.
Из документации по Hangfire:
HttpContext не доступен
Информация запроса недоступна во время создания целевого типа. Если вы зарегистрируете свои зависимости в области запроса (InstancePerHttpRequest в Autofac, InRequestScope в Ninject и т. Д.), В процессе активации задания будет сгенерировано исключение.
Таким образом, весь граф зависимостей должен быть доступен. Либо зарегистрируйте дополнительные службы без использования области запроса, либо используйте отдельный экземпляр контейнера, если ваш контейнер IoC не поддерживает регистрации зависимостей для нескольких областей.
Согласно этому связанному вопросу SO, Autofac не поддерживает регистрацию нескольких областей действия из коробки, поэтому вам необходимо:
- Используйте "нижнюю" область видимости, например "InstancePerDependency", или
- Используйте отдельный контейнер IoC для фоновых заданий.
ОБНОВЛЕНИЕ:
Интеграционный пакет Hangfire Autofac представляет InstancePerBackgroundJob()
метод расширения, и предполагает, что Autofac поддерживает регистрацию для нескольких областей:
builder.RegisterType<Database>()
.InstancePerBackgroundJob()
.InstancePerHttpRequest();
Тем не менее, он доступен только в версии 1.2.0-бета1 Hangfire.Autofac, который требует Hangfire 1.5.0-бета1.