Hangfire 不识别数据库上下文

问题描述 投票:0回答:1

我正在尝试添加 Hangfire 来为管理员发送包含每周摘要的电子邮件。

我有后台调度程序

{
    public class BackgroundScheduler : IBackgroundScheduler
    {
        private readonly IMediator _mediator;
        private readonly IServiceScopeFactory _serviceScopeFactory;

        public BackgroundScheduler(IRecurringJobManager recurringJobManager,IMediator mediator, IServiceScopeFactory serviceScopeFactory)
        {
            _mediator = mediator;
            _serviceScopeFactory = serviceScopeFactory;
        }

        public void SendWeeklyDigestEmail()
        {

                _mediator.Send(new WeeklyDigestEmailCommand());
            
        }

        public void ScheduleRecurringTasks()
        {
            RecurringJob.AddOrUpdate(() => SendWeeklyDigestEmail(), "0 7 * * MON");
        }
    }
}

将命令发送到命令处理程序。该命令是空的,所以我将直接向您展示命令处理程序:

  public class WeeklyDigestEmailCommandHandler : IRequestHandler<WeeklyDigestEmailCommand>
    {
        private readonly IBackofficeDbContext _context;
        private readonly IEmailNotification _emailNotification;
        private readonly ITemplateProvider _templateProvider;
        private readonly FrontendConfiguration _frontendConfiguration;
        public WeeklyDigestEmailCommandHandler(IBackofficeDbContext context, IEmailNotification emailNotification, ITemplateProvider templateProvider,
            IOptions<FrontendConfiguration> frontendConfiguration)
        {
            this._context = context;
            _emailNotification = emailNotification;
            _templateProvider = templateProvider;
            _frontendConfiguration = frontendConfiguration.Value;
        }

        public async Task<Unit> Handle(WeeklyDigestEmailCommand command,
            CancellationToken cancellationToken)
        {

            var usersByTenant = await _context.Users
            .Include(x => x.Role)
            .Where(x => x.Role.Name == Constants.Roles.TenantAdministrator)
            .ToListAsync(cancellationToken);

            Console.WriteLine(usersByTenant.Count);

            var usersByTenantGrouped = usersByTenant
                .GroupBy(x => x.TenantId);


            var subject = "Weekly digest email";
            var template = _templateProvider.GetTemplate(EmailTemplateType.WeeklyDigestEmailTemplate);


            foreach (var tenantGroup in usersByTenantGrouped)
            {
                var tenantAdmins = tenantGroup.ToList();
                var tenantAdminsIds = tenantAdmins.Select(ta => ta.Id).ToList();


                var newCustomersCount = await _context.Customers
                    .Where(c => c.Created >= DateTime.UtcNow.AddDays(-7) && tenantAdminsIds.Contains(c.CreatedBy))
                    .CountAsync(cancellationToken);

                var newProjectsCount = await _context.Projects
                    .Where(p => p.Created >= DateTime.UtcNow.AddDays(-7) && tenantAdminsIds.Contains(p.CreatedBy))
                    .CountAsync(cancellationToken);

                var completedProjectsCount = await _context.Projects
                    .Where(p => p.LastModified >= DateTime.UtcNow.AddDays(-7) || p.Created >= DateTime.UtcNow.AddDays(-7) && p.Status == ProjectStatus.Completed && tenantAdminsIds.Contains(p.CreatedBy))
                    .CountAsync(cancellationToken);

                var completedProjectTasksCount = await _context.ProjectTasks
                    .Where(t => t.LastModified >= DateTime.UtcNow.AddDays(-7) || t.Created >= DateTime.UtcNow.AddDays(-7) && t.Completed && tenantAdminsIds.Contains(t.CreatedBy))
                    .CountAsync(cancellationToken);

                var overdueTasksCount = await _context.ProjectTasks
                    .Where(t => DateTime.UtcNow >= t.DueDate && tenantAdminsIds.Contains(t.CreatedBy))
                    .CountAsync(cancellationToken);
                Console.WriteLine("Weekly digest email.");
                foreach (var tenantAdmin in tenantAdmins)
                {
                    var body = template.FillTemplate(tenantAdmin.Name, newCustomersCount, newProjectsCount, completedProjectsCount, completedProjectTasksCount, overdueTasksCount, _frontendConfiguration.Url);

                    await _emailNotification.SendEmailAsync(new EmailMessage
                    {
                        To = tenantAdmin.Email,
                        Subject = subject,
                        Model = body,
                    }, EmailTemplateType.WeeklyDigestEmailTemplate);

                }
            }


            return Unit.Value;
        }

我出于测试目的使用此命令制作了 httppost,当我通过控制器发送它时,它正在发送电子邮件并且一切正常。但是当我尝试通过 hangfire 运行它时,它在 usersByTenant 变量之后停止运行,因为它似乎没有找到任何租户管理员,这很奇怪,因为当我运行它时,我得到了他们并且程序继续运行。之后我尝试初始化 dbcontext 和 hangfire,但它仍然无法正常工作。

程序.cs:

builder.Services.AddDbContext<BackofficeDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DbConnectionString")));

// Add Hangfire
builder.Services.AddHangfire(config =>
{
    config.UseSqlServerStorage(builder.Configuration.GetConnectionString("DbConnectionString"));
});

//Register background scheduler
builder.Services.AddScoped<IBackgroundScheduler, BackgroundScheduler>();

var serviceProvider = builder.Services.BuildServiceProvider();
var backgroundScheduler = serviceProvider.GetService<IBackgroundScheduler>();
backgroundScheduler.ScheduleRecurringTasks();

我也试过在服务范围内制作它,但我收到一个错误消息:

System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.

我也不能使用 ContextFactory,因为我只能访问数据库上下文的接口,因为实现是在解决方案的另一个项目中。我该如何解决这个问题?

c# .net hangfire
1个回答
0
投票

我建议您将

ScheduleRecurringTasks
的实现更改为:

public static void ScheduleRecurringTasks()
{
   RecurringJob.AddOrUpdate<IBackgroundScheduler>(x => x.SendWeeklyDigestEmail(), "0 7 * * MON");
}

这将为您解决两件事:

  1. 您将不再需要自己构建服务提供者,因此您可以删除以下内容并通过调用静态方法进行更改:

var serviceProvider = builder.Services.BuildServiceProvider();变量 后台调度程序 = serviceProvider.GetService()

  1. 因为你已经在 DI 上注册了
    BackgroundScheduler
    接口,作业的激活器应该尝试使用接口找到它,你所做的第一个实现将告诉激活器使用类类型找到它。

builder.Services.AddScoped();

© www.soinside.com 2019 - 2024. All rights reserved.