#c# #hangfire
#c# #hangfire
Вопрос:
У меня возникли проблемы с настройкой HangFire. Я пытаюсь запустить и забыть тревожный вызов в ядре ASP Net.
Служба оповещения:
public void FireAndForgetAlarm(int statusCode, string message, string subject = null, IList<string> attachmentFilePaths = null)
{
BackgroundJob.Enqueue(() => Alarm(statusCode, message, subject,
attachmentFilePaths));
}
* Метод аварийной сигнализации является общедоступным.
Описание ошибки:
предупреждение: Hangfire.AutomaticRetryAttribute[0] Не удалось обработать задание ‘eb7f76ea-7522-49e2-b7b9-3ed3ed091a0c’: произошло исключение. Повторная попытка 2 из 10 будет выполнена в 00:01:02. System.Исключение InvalidOperationException: не удалось разрешить службу для типа ‘Wms.Тревога.AlarmingConfig’ при попытке активировать ‘Wms.Тревога.AlarmingService’. в Microsoft.Расширения.DependencyInjection.ActivatorUtilities.Сопоставитель конструкторов.CreateInstance (поставщик услуг IServiceProvider) в Microsoft.Расширения.DependencyInjection.ActivatorUtilities.CreateInstance(поставщик IServiceProvider, введите instanceType, параметры Object[]) в Microsoft.Расширения.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance(поставщик IServiceProvider, тип type) в Hangfire.AspNetCore.AspNetCoreJobActivatorScope.Разрешить (ввести тип) в Hangfire.Server.CoreBackgroundJobPerformer.Выполнить (контекст PerformContext) в Hangfire.Сервер.BackgroundJobPerformer.<>c__DisplayClass9_0.b__0() в Hangfire.Сервер.BackgroundJobPerformer.InvokePerformFilter(фильтр IServerFilter, фильтры PerformingContext preContext, Func
1 continuation) at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_1.<PerformJobWithFilters>b__2() at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable
1) в Hangfire.Сервер.BackgroundJobPerformer.Выполнить (контекст PerformContext) в Hangfire.Сервер.Рабочий.Выполнить задание (контекст BackgroundProcessContext, подключение IStorageConnection, идентификатор строки JobID)
Мой класс запуска содержит:
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureAlarmingServices(new AlarmingConfig()
{
Port = int.Parse(ConfigurationManager.AppSettings["mailPort"]), // for SSL 465
Sender = ConfigurationManager.AppSettings["mailSender"],
SmtpServer = ConfigurationManager.AppSettings["smtpServer"],
Recipients = ConfigurationManager.AppSettings["mailRecipients"],
SenderPassword = ConfigurationManager.AppSettings["mailPassword"]
});
services.AddHangfire(c => {
c.UseMemoryStorage();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHangfireServer(new BackgroundJobServerOptions(){Activator = new AspNetCoreJobActivator(app.ApplicationServices.GetRequiredService<IServiceScopeFactory>())});
}
Примечания:
- Я пробовал несколько версий для класса startup, в том числе помещал активатор в метод AddHangire. На самом деле ни одна попытка не сработала, и я также правильно следовал документации.
- Служба оповещения настроена правильно, она работает без HangFire.
- Передача литералов в тревожной конфигурации не работает
- Единственное решение — сделать метод статическим, но это не позволяет создавать хорошие DI
- Я видел решение в их документации, где у них всегда был бы конструктор по умолчанию и вызывал конструктор с параметрами. Однако для меня это невозможно, поскольку я также внедряю регистратор.
Содержимое метода расширения, запрошенного в комментариях:
public static void ConfigureAlarmingServices(this IServiceCollection collection, AlarmingConfig config)
{
collection.AddSingleton<IAlarmingService>(x=> new
AlarmingService(x.GetService<ILogger<IAlarmingService>>(), config));
}
Комментарии:
1. Где код, который на самом деле выдает ошибку? Похоже, вам не хватает конфигурации для
FileStorageService
.2. Вы правы @Xerillio Я показал службу оповещения, поскольку у меня также была проблема с службой оповещения (та же самая). Позвольте мне обновить.
3. @Xerillio Я обновил сообщение об ошибке, сообщение об ошибке filestorageservice было моим первым, которое я смог решить, сделав его статичным. Однако этот тревожный сервис не может быть решен таким образом.
4. Это
ConfigureAlarmingServices
пользовательский метод? Что происходит в этом методе? Похоже, вам не хватает aservices.AddScoped<AlarmingService>(...)
или чего-то подобного.5. да, это метод расширения, в котором я регистрирую службу оповещения как одноэлементный @Xerillio
Ответ №1:
Прежде всего, важной частью исключения, которое вы видите, является:
Не удалось разрешить службу для типа ‘Wms.Тревога.AlarmingConfig’ при попытке активировать ‘Wms.Тревога.AlarmingService ‘
Это указывает на то, что где-то в вашем коде (часть, которую вы еще не показали) вы пытаетесь внедрить экземпляр AlarmingService
.
Вы настроили, как разрешить одноэлементную службу для интерфейса IAlarmingService
, что означает, что вы должны иметь возможность зависеть от класса IAlarmingService
. Однако, если ваш класс зависит от AlarmingService
(а не от интерфейса), вы получите эту ошибку, потому что вы не определили, как разрешить зависимость типа AlarmingService
.
Либо…
- код, который вы не показали, должен зависеть только от
IAlarmingService
, а не от самого конкретного класса (AlarmingService
) или.. - вам также необходимо настроить контейнер DI для обработки зависимостей определенного типа
AlarmingService
, например:
public static void ConfigureAlarmingServices(this IServiceCollection collection, AlarmingConfig config)
{
collection.AddSingleton<AlarmingService>(
x => new AlarmingService(x.GetService<ILogger<AlarmingService>>(), config));
collection.AddSingleton<IAlarmingService>(x => x.GetService<AlarmingService>());
}
Я думаю, что первый момент — это то, что вам нужно исправить.
Комментарии:
1. От конкретного класса ничего не зависит, я использую правильный DI. Второй оператор ничем не отличается от первого, плюс недействителен, поскольку вы получаете IAlarmingService и пытаетесь зарегистрировать его как AlarmingService. Однако вы дали мне новую идею, зарегистрировав конфигурацию отдельно. Это сработало!
2. @wutBruh Вы правы, регистрацию службы следовало поменять местами. Теперь это исправлено. Мне все еще интересно, зачем вам нужно регистрировать конфигурацию отдельно. Это не должно быть необходимым при определении фабрики для
IAlarmingService
. Если вы не зарегистрируете конфигурацию и не используете мой фиксированный код, это все равно не сработает?3. Ах, это действительно работает, и я только сейчас понимаю, что вы имели в виду. Библиотеке нужна фактическая реализация, потому что именно там она выполняется, несмотря на передачу интерфейса. Имеет смысл.
4. Еще один вопрос. Имеет ли смысл ставить в очередь задание, которое запускает один единственный асинхронный метод? Должен ли я ждать этого или нет?
5. @wutBruh конечно, почему бы и нет? Количество вызываемых вами методов не имеет значения. Я не думаю, что имеет значение, ждать или нет. Насколько я вижу, Hangfire пока не поддерживает истинную «асинхронность».