Консоль ядра Hangfire .net Внедрение зависимостей - PullRequest
0 голосов
/ 22 октября 2019

У меня есть проект с такой структурой

  • AdventureWorks.Web <- веб-проект .NET Core (службы, Viewmodels, DataContext, представления контроллера и т. Д.) </li>
  • AdventureWorks.Console<- консольное приложение .NET Core (сервер конфигурации Hangfire) </li>
  • AdventureWorks.Model <- только классы чистой модели POCO </li>

Я пытаюсь запустить сервер Hangfire в отдельном процессе (AdventureWorks.Console) [https://docs.hangfire.io/en/latest/background-processing/processing-jobs-in-console-app.html]

Обработка заданий в консольном приложении.

Я добавил ссылку на AdventureWorks.Web на AdventureWorks.Console - таким образом сервер Hangfire будетвыполнить код

static void Main(string[] args)
{
    GlobalConfiguration.Configuration.UseSqlServerStorage("Connection");

    using (var server = new BackgroundJobServer())
    {
        Console.WriteLine("Hangfire Server started. Press any key to exit...");
        Console.ReadKey();
    }
}

В веб-проекте я регистрирую свои службы в Startup классе

services.AddTransient<IDepartmentServices, DepartmentServices>();

В конструкторе есть параметр, который автоматически вводится

private readonly AdventureWorksDataContext db;

    public DepartmentServices(AdventureWorksDataContext _db)
    {
        db = _db;
    }

В контроллер вводятся сервисы

private readonly IDepartmentServices Service;

    public DepartmentsController(IDepartmentServices service)
    {
        Service = service;
    }

В одном методе в контроллере, когда пользователь нажимает кнопку «Создать отчет», метод ставит в очередь задание на создание отчета

BackgroundJob.Enqueue<**IDepartmentService**>((serv) => serv.CrearReport(data));

Пока все хорошо.

Я вижу на приборной панели созданное заданиено он не может выполнить

System.MissingMethodException Невозможно создать экземпляр интерфейса.

, что очевидно, поэтому мой первый вопрос

  • Как создать работу с учетом внедрения зависимости? Я пытаюсь следовать инструкции здесь [https://docs.hangfire.io/en/latest/background-methods/passing-dependencies.html], но я не вижу ничего о зависимости, которая вводится, мой лучший гостевой зависший огонь не работает с инъекцией зависимости

Такчтобы продолжить, я изменяю интерфейс с классом

BackgroundJob.Enqueue<DepartmentService>((serv) => serv.CrearReport(data));

пока все хорошо, задание создано. Но происходит сбой с ошибкой

System.MissingMethodException
Для этого объекта не определен конструктор без параметров

, что очевидно, поэтому мой второй вопрос

  • как передать параметр datacontext на сервер зависания? Мое лучшее предположение, что я не могу, потому что сервер зависания использует Активатор, который хочет пустой конструктор

Поэтому, чтобы продолжить, я следовал инструкциям здесь [https://docs.hangfire.io/en/latest/background-methods/passing-dependencies.html]

Я создалпустой конструктор

public EmployeeService()
        :this(new AdventureWorksDataContext())
    {

    }

, и мне нужно добавить пустой конструктор в текстовый текст

public AdventureWorksDataContext()
    {

    }

, но теперь мне нужно переопределить метод "OnConfigure"

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.UseSqlServer("????");<--connectionstring???
    }

поэтому мой вопрос: как мне получить строку подключения из appsettings.json, мой лучший гость, я не могу игнорировать эту проблему, я добавляю прямое соединение

optionsBuilder.UseSqlServer("Server=.;Database=AdventureWorks2017;Trusted_Connection=True;");

таким образом, работа завершена правильно

но это решение, я думаю, плохое, может быть, структура проекта плохая или как внедрение зависимостей работает с Hangfire, если он работает в отдельном процессе, как работает IoC и консоль сервера hangfire

некоторые, назовите мне лучшее решение, какструктурировать проект

извините за мой английский я не говорю

1 Ответ

0 голосов
/ 23 октября 2019

Попробуйте это: https://docs.hangfire.io/en/latest/getting-started/aspnet-core-applications.html. В прошлом я использовал Nuget Hangfire.AutoFac, используя специальный JobActivator, подходящий для данной структуры DI.

Многое изменилось, проверьте: https://docs.hangfire.io/en/latest/background-methods/using-ioc-containers.html и (это должно работать) https://docs.hangfire.io/en/latest/getting-started/aspnet-core-applications.html

--- что у меня было:

Так что на самом деле вам нужно добавить некоторый объект, связанный с зависанием от пожара, в контейнер. Я использовал AutoFac (с .NET MVC) ...

Надеюсь, это поможет вам двигаться вперед.

, поэтому установка внедрения службы (через сервис) через AutoFac (вы можете использовать чистый MVC DI):

                //
                // Set up HANGFIRE
                var connectionStringName = config["Hangfire:ConnectionStringName"];
                var connectionSection = config.GetSection($"Data:{connectionStringName}");
                var connectionString = config[$"Data:{connectionStringName}:ConnectionString"];

                var queuePollIntervanInMs = int.Parse(config["Hangfire:QueuePollIntervalInMilliseconds"]);

                GlobalConfiguration.Configuration.UseLogProvider(new HangfireLogProvider());
                GlobalConfiguration.Configuration.UseSqlServerStorage(
                    connectionString,
                    new SqlServerStorageOptions() { QueuePollInterval = TimeSpan.FromMilliseconds(queuePollIntervanInMs) }
                );

                var serverEnabled = bool.Parse(config["Hangfire:ServerEnabled"]);
                if (serverEnabled)
                {
                    GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute());
                    GlobalJobFilters.Filters.Add(new HangfirePermanentFailureNofitifactionAttribute());

                    // Setting up Hangfire background job processing (use SIGLETON!, keep reference for graceful shutdown - btw. app.UseHangfireServer() works in web-context and does register to graceful-shutdown)
                    GlobalConfiguration.Configuration.UseAutofacActivator(_container);

                    _hangfireServer = new BackgroundJobServer(
                        new BackgroundJobServerOptions()
                        {
                            Activator = JobActivator.Current
                        },
                        JobStorage.Current
                    );
                }

startup.cs (настроить службы - перед добавлением указанных выше зависимостей) - игнорировать некоторые комментарии, я не считаю их полезными:


                /*
                // Hangfire - since we plan to use it both in web and threaded (unit test) env., initialization is done with hangfire globals on a single place...
                services.AddHangfire(h =>
                {
                    // h.UseSqlServerStorage("<connection string or its name>");
                    // h.UseActivator(...)
                    // h.UseFilter(...)
                });
                */

                // NOTE: the following 2 objects needs to be registered for dashboard to work...https://github.com/HangfireIO/Hangfire/blob/129707d66fde24dc6379fb9d6b15fa0b8ca48605/src/Hangfire.AspNetCore/HangfireServiceCollectionExtensions.cs
                services.TryAddSingleton(_ => GlobalConfiguration.Configuration);
                services.TryAddSingleton(_ => DashboardRoutes.Routes);

и startup.cs(настроить iapp builder:

                var dashboardEnabled = bool.Parse(Configuration["Hangfire:DashboardEnabled"]);
                if (dashboardEnabled)
                {
                    // NOTE: Place a call to the UseHangfireDashboard method after other authentication methods in your OWIN Startup class. Otherwise authentication may not work for you.
                    var hangfireDashboarOptions = new DashboardOptions
                    {
                        Authorization = new IDashboardAuthorizationFilter[]
                        {
                            new LocalRequestsOnlyAuthorizationFilter()
                        },
                        StatsPollingInterval = int.Parse(Configuration["Hangfire:DashboardRefreshIntervalInMilliseconds"])
                    };

                    // https://github.com/HangfireIO/Hangfire/blob/129707d66fde24dc6379fb9d6b15fa0b8ca48605/src/Hangfire.AspNetCore/HangfireApplicationBuilderExtensions.cs
                    app.UseHangfireDashboard("/dashboard", hangfireDashboarOptions, JobStorage.Current);
                }

и пример при вызове:

    /// <summary>
    /// Main event handler, can inherit from multiple IDomainEventHandler<TEvent>.
    /// NOTE: depending how event registration and dispatching is done, for each handler a separate instance might be served from the DI framework!
    /// </summary>
    public class UserEventHandler :
        IDomainEventHandler<UserRegisteredEvent>
    {
        private ILog _logger;
        private MyDbContext _myDbContext;

        public UserEventHandler(
            ILog logger,
            MyDbContext myDbContext,
            )
        {
            _logger = logger;
            _myDbContext = myDbContext;
        }

        public void Handle(UserRegisteredEvent e)
        {
            // Send Welcome Email
            if (!string.IsNullOrEmpty(e.User.Email))
                BackgroundJob.Enqueue<UserMessageHandler>(i => i.SendWelcomeEmail(e.User.Email, e.User.UserName, e.UserPassword, e.PRCo));
            else
                _logger.Warn(new { Message = $"Can't send notification email, user: '{e.User.GetFullName()}' with SSN: {e.User.Ssn}' has no email assigned!" });

            // Send Welcome SMS
            if (!string.IsNullOrEmpty(e.CellPhone))
                BackgroundJob.Enqueue<UserMessageHandler>(i => i.SendWelcomeSMS(e.CellPhone, e.User.UserName, e.UserPassword));
            else
                _logger.Warn(new { Message = $"Can't send notification message, user: '{e.User.GetFullName()}' with SSN: {e.User.Ssn}' has no cell phone assigned!" });
        }

Хитрость заключается в том, как вы видите, UserMessageHandler не внедряется в сам класс обработчика, он вводится в контекст(как синглтон - но не обязательно как синглтон), и Hangfire выбирает объект из контекста!

...