Coravel Schedule

https://docs.coravel.net/Installation/

// c# Add Coravel Parkage

In startup.cs add below code:

---- ConfigureServices -----
Services.AddScheduler();
Services.AddTransient<SampleInvocable>();

---- Configure ----
var provider = app.ApplicationServices;
provider.UseScheduler(scheduler =>
{
    scheduler.Schedule<SampleInvocable>();
    .EveryMinute()
    .Weekday();
});

// method 

public class SampleInvocable : IInvocable
{
    public Task Invoke()
    {
       Console.WriteLine("Every minute on weekday ..");
       return Task.CompletedTask;
     }
}

Cron expression generator: https://crontab.cronhub.io/

// cron expression

scheduler.Schedule(
.Cron("0 0 1 * *") // At midnight on the 1st day of each month.

0 1 * * 1 // At 01:00 AM, only on Monday
0 1 1 * * // At 01:00 AM, on day 1 of the month

scheduler.Schedule<SendDailyReportsEmailJob>()
.Cron("39 15 * * *") // run at 1:00 pm daily                 // .DailyAt(15, 29)
.Zoned(TimeZoneInfo.Local);


scheduler.Schedule<SendWeeklyReportsEmailJob>()
.Cron("0 1 * * 1")   // Cron expression - At 01:00 AM, only on Monday
.Zoned(TimeZoneInfo.Local);

scheduler.Schedule<SendMonthlyReportsEmailJob>()
.Cron("0 1 1 * *")   // Cron expression - At 01:00 AM, on day 1 of the month
.Zoned(TimeZoneInfo.Local);
               

reloadOnChange for appsettings.json

// Coravel Schudler, Change time - read value from appsettings.json

// startup.cs
public IConfiguration GetAppssetingsConfig()
{
    return new ConfigurationBuilder()
               .SetBasePath(Directory.GetCurrentDirectory())
               .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
               .AddEnvironmentVariables()
               .Build();
}
 
public Startup(IConfiguration configuration)
{
    Configuration = configuration;
    // GetAppssetingsConfig();    ///not be sured
}
        
public IServiceProvider ConfigureServices(IServiceCollection services)
{
   // GetAppssetingsConfig();  ///not be used, not sure

    //--- schedule service
    services.AddScheduler();
    services.AddTransient<SendDailyReportsEmailJob>();
    services.AddTransient<SendWeeklyReportsEmailJob>();
    services.AddTransient<SendMonthlyReportsEmailJob>();
    ...

}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

    // scheduler
    var provider = app.ApplicationServices;
    provider.UseScheduler(scheduler =>
    {
        // run at 1:00 AM daily     .DailyAt(1, 0)  or .Cron("0 1 * * *") 
        scheduler.Schedule<SendDailyReportsEmailJob>()
        .DailyAt(1, 0)
        .PreventOverlapping("SendDailyReportsEmailJob")
        .Zoned(TimeZoneInfo.Local);

        // Cron expression - At 01:15 AM, only on Monday
        scheduler.Schedule<SendWeeklyReportsEmailJob>()
        .Cron("15 1 * * 1")
        .Zoned(TimeZoneInfo.Local);

        // Cron expression - At 02:00 AM, on day 1 of the month
        scheduler.Schedule<SendMonthlyReportsEmailJob>()
        // .DailyAt(16, 0)
        .Cron("0 2 1 * *")
        .Zoned(TimeZoneInfo.Local);
    });

    // appsetings.json changed event
    ChangeToken.OnChange(() => this.Configuration.GetReloadToken(), () =>
    {
        using (var scope = app.ApplicationServices.CreateScope())
        {
            IScheduler scheduler = scope.ServiceProvider.GetRequiredService<IScheduler>();
            IConfiguration config = scope.ServiceProvider.GetRequiredService<IConfiguration>();

            Console.WriteLine("A new scheduled task! ...");
            // Remove your past scheduled event/task
            (scheduler as Scheduler).TryUnschedule("SendDailyReportsEmailJob");
            
            //add new task
            scheduler.Schedule<SendDailyReportsEmailJob>()
                   .Cron(config["DailyReportTime:daily-report-time"])
                   .Zoned(TimeZoneInfo.Local)
                   .PreventOverlapping("SendDailyReportsEmailJob");
        };
    });

}


//SendDailyReportsEmailJob.cs
public class SendDailyReportsEmailJob : IInvocable
{
    // run when schedule time is coming
    public async Task Invoke()
    {
         var dateString = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
         Console.WriteLine("A Email has sent!" + dateString);

         await GetDailyReport();      //  

    }

    private async Task GetDailyReport()
    {
        //sent email
    }
}

Change appsettings.json value when updating data in the database

// controller

[HttpPut]
[Route("api/setting/edit/{id}")]
public async Task<IActionResult> UpdateSetting(int id, [FromForm] SettingViewModel settingVM)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    try
    {
        var oldRecord = await _repository.GetById(id);

        if (oldRecord == null)
            return NotFound($"Couldn't find the EmailSetting id = {id}");

        var newRecord = _mapper.Map(settingVM, oldRecord);

        _repository.UpdateSetting(newRecord);

        if (await _unitOfWork.SaveAsyc() > 0)
        {
            _logger.LogInformation($"Setting id = {id} has updated !");
            string cronTime1 = getDailyCronTimeStr(newRecord.SendTime.Hour, newRecord.SendTime.Minute);      // daily-report-time1
            updateAppSetting<string>("DailyReportTime:daily-report-time1", cronTime1);

            string cronTime2 = getDailyCronTimeStr(newRecord.SendTime2?.Hour, newRecord.SendTime2?.Minute);  // daily-report-time2
            updateAppSetting<string>("DailyReportTime:daily-report-time2", cronTime2);

            return Ok(_mapper.Map<SettingViewModel>(oldRecord));
        }
        return new StatusCodeResult(StatusCodes.Status500InternalServerError);
    }
    catch (Exception ex)
    {
        _logger.LogInformation($"Exception: " + ex.Message);
    }
    return
        BadRequest($"Could not update EmailSetting id= {id}");
}
// appsettings.json
{

  "DailyReportTime": {
    "daily-report-time": "50 06 * * *",
    "daily-report-time2": "43 15 * * *"
  },
  ...
}



// EmailSetting Controller

private string getDailyCronTimeStr(int? hour, int? minute)
{
    var h = hour.ToString();
    if (h.Length == 1)
        h = "0" + h;

    var m = minute.ToString();
    if (m.Length == 1)
        m = "0" + m;

    string cronTimeStr = m + " " + h + " * * *";    // Cron("0 1 * * *") >> run at 1:00 AM daily 
    return cronTimeStr;
}

private  void updateAppSetting<T>(string key, T value)
{
   try
    {
        var path = Directory.GetCurrentDirectory();
        var filePath = Path.Combine(path, "appSettings.json");
        if (global::System.IO.File.Exists(filePath))
        {
            string json = global::System.IO.File.ReadAllText(filePath);
            dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
            var sectionPath = key.Split(":")[0];
            if (!string.IsNullOrEmpty(sectionPath))
            {
                var keyPath = key.Split(":")[1];
                var oldvalue = jsonObj[sectionPath][keyPath];

                jsonObj[sectionPath][keyPath] = value;
            }
            else
            {
                jsonObj[sectionPath] = value;  
            }

            string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
            global::System.IO.File.WriteAllText(filePath, output);
        }
    }
    catch (ConfigurationErrorsException)
    {
        Console.WriteLine("Error writing app settings");
    }            
} 

Last updated