asp.net Hangfire获取上次执行时间

mxg2im7a  于 2023-01-03  发布在  .NET
关注(0)|答案(5)|浏览(173)

我使用的是hangfire 1.5.3,在我的循环作业中,我想调用一个使用自上次运行以来的时间的服务,不幸的是LastExecution被设置为当前时间,因为作业数据在执行作业之前被更新了。

工作

public abstract class RecurringJobBase
{
    protected RecurringJobDto GetJob(string jobId)
    {
        using (var connection = JobStorage.Current.GetConnection())
        {
            return connection.GetRecurringJobs().FirstOrDefault(p => p.Id == jobId);
        }
    }

    protected DateTime GetLastRun(string jobId)
    {
        var job = GetJob(jobId);

        if (job != null && job.LastExecution.HasValue)
        {

            return job.LastExecution.Value.ToLocalTime();
        }

        return DateTime.Today;
    }
}

public class NotifyQueryFilterSubscribersJob : RecurringJobBase
{
    public const string JobId = "NotifyQueryFilterSubscribersJob";
    private readonly IEntityFilterChangeNotificationService _notificationService;

    public NotifyQueryFilterSubscribersJob(IEntityFilterChangeNotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    public void Run()
    {
        var lastRun = GetLastRun(JobId);
        _notificationService.CheckChangesAndSendNotifications(DateTime.Now - lastRun);
    }
}

注册

RecurringJob.AddOrUpdate<NotifyQueryFilterSubscribersJob>(NotifyQueryFilterSubscribersJob.JobId, job => job.Run(), Cron.Minutely, TimeZoneInfo.Local);

我知道,它被配置为精确,所以我可以粗略地计算时间。但我希望有一个配置独立的实现。所以我的问题是:如何实现RecurringJobBase.GetLastRun以返回上一次运行的时间?

8wtpewkr

8wtpewkr1#

为了解决我上面的评论,您可能有不止一种类型的重复运行的作业,但希望检查以前的状态,您可以检查以前的作业信息实际上与这种类型的作业相关,如下所示(虽然这感觉有点黑客/错综复杂)。
如果你要把PerformContext传入job方法,那么你可以使用以下代码:

var jobName = performContext.BackgroundJob.Job.ToString();
var currentJobId = int.Parse(performContext.BackgroundJob.Id);
JobData jobFoundInfo = null;

using (var connection = JobStorage.Current.GetConnection()) {
    var decrementId = currentJobId;
    while (decrementId > currentJobId - 50 && decrementId > 1) { // try up to 50 jobs previously
        decrementId--;
        var jobInfo = connection.GetJobData(decrementId.ToString());
        if (jobInfo.Job.ToString().Equals(jobName)) { // **THIS IS THE CHECK**
            jobFoundInfo = jobInfo;
            break;
        }
    }
    if (jobFoundInfo == null) {
       throw new Exception($"Could not find the previous run for job with name {jobName}");
    }
    return jobFoundInfo;
}
ukqbszuj

ukqbszuj2#

您可以利用您已经声明的事实-“* 不幸的是,LastExecution被设置为当前时间,因为作业数据在执行作业之前已经更新 *"。
作业包含“LastJobId”属性,该属性似乎是一个递增的ID。因此,您应该能够通过递减LastJobId并查询该ID的作业数据来获取“真实的的”上一个作业。

var currentJob = connection.GetRecurringJobs().FirstOrDefault(p => p.Id == CheckForExpiredPasswordsId);

if (currentJob == null)
{
    return null; // Or whatever suits you
}

var previousJob = connection.GetJobData((Convert.ToInt32(currentJob.LastJobId) - 1).ToString());

return previousJob.CreatedAt;

请注意,这是创建的时间,而不是执行的时间。但它可能对您来说足够准确。请记住,当它是您的第一次运行时,边缘情况,因此将没有以前的作业。

nnt7mjpx

nnt7mjpx3#

经过多方研究,我想出了以下解决方案。

var lastSucceded = JobStorage.Current.GetMonitoringApi().SucceededJobs(0, 1000).OrderByDescending(j => j.Value.SucceededAt).FirstOrDefault(j => j.Value.Job.Method.Name == "MethodName" && j.Value.Job.Type.FullName == "NameSpace.To.Class.Containing.The.Method").Value;
var lastExec = lastSucceded.SucceededAt?.AddMilliseconds(Convert.ToDouble(-lastSucceded.TotalDuration));

它并不完美,但我认为比其他的解决方案干净一点。希望他们能尽快实施一个官方的方式。

dldeef67

dldeef674#

@Marius施泰因巴赫给出的答案通常足够好,但如果你有数千个作业执行(我的例子),从DB加载所有的作业似乎并不那么好。所以最后我决定写一个简单的SQL查询并直接使用它(这是针对PostgreSQL存储的,但将其改为SqlServer应该很简单):

private async Task<DateTime?> GetLastSuccessfulExecutionTime(string jobType)
{
    await using var conn = new NpgsqlConnection(_connectionString);
    if (conn.State == ConnectionState.Closed)
        conn.Open();

    await using var cmd = new NpgsqlCommand(@"
        SELECT s.data FROM hangfire.job j
        LEFT JOIN hangfire.state s ON j.stateid = s.id 
        WHERE j.invocationdata LIKE $1 AND j.statename = $2
        ORDER BY s.createdat DESC
        LIMIT 1", conn)
    {
        Parameters =
        {
            new() { Value = $"%{jobType}%" } ,
            new() { Value = SucceededState.StateName }
        }
    };

    var result = await cmd.ExecuteScalarAsync();
    if (result is not string data)
        return null;

    var stateData = JsonSerializer.Deserialize<Dictionary<string, string>>(data);

    return JobHelper.DeserializeNullableDateTime(stateData?.GetValueOrDefault("SucceededAt"));
}
uplii1fm

uplii1fm5#

使用此方法返回一个作业的上次执行时间和下次执行时间。此方法返回一个作业的上次执行时间和下次执行时间。

public static (DateTime?, DateTime?) GetExecutionDateTimes(string jobName)
{
    DateTime? lastExecutionDateTime = null;
    DateTime? nextExecutionDateTime = null;
    using (var connection = JobStorage.Current.GetConnection())
    {
        var job = connection.GetRecurringJobs().FirstOrDefault(p => p.Id == jobName);
        if (job != null && job.LastExecution.HasValue)
            lastExecutionDateTime = job.LastExecution;
        if (job != null && job.NextExecution.HasValue)
            nextExecutionDateTime = job.NextExecution;
    }
    return (lastExecutionDateTime, nextExecutionDateTime);
}

相关问题