Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bsdayo committed Nov 27, 2023
0 parents commit ec81c29
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/nuget.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Publish NuGet Package

on:
push:
branches:
- main

jobs:
nuget-publish:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.x.x

- name: Publish NekoSpace.Toolkit.HostedServices
uses: alirezanet/[email protected]
with:
PROJECT_FILE_PATH: src/NekoSpace.Toolkit.HostedServices/NekoSpace.Toolkit.HostedServices.csproj
TAG_COMMIT: true
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
.idea/
102 changes: 102 additions & 0 deletions .idea/.idea.Toolkit.BackgroundServices/.idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions NekoSpace.Toolkit.HostedServices.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NekoSpace.Toolkit.HostedServices", "src\NekoSpace.Toolkit.HostedServices\NekoSpace.Toolkit.HostedServices.csproj", "{3AF184ED-D1AF-4BA5-89CC-C1F9BFE0469C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CronExample", "examples\CronExample\CronExample.csproj", "{50FCDAF4-246A-4DFC-9452-CAC5FB6C3792}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3AF184ED-D1AF-4BA5-89CC-C1F9BFE0469C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3AF184ED-D1AF-4BA5-89CC-C1F9BFE0469C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3AF184ED-D1AF-4BA5-89CC-C1F9BFE0469C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3AF184ED-D1AF-4BA5-89CC-C1F9BFE0469C}.Release|Any CPU.Build.0 = Release|Any CPU
{50FCDAF4-246A-4DFC-9452-CAC5FB6C3792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50FCDAF4-246A-4DFC-9452-CAC5FB6C3792}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50FCDAF4-246A-4DFC-9452-CAC5FB6C3792}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50FCDAF4-246A-4DFC-9452-CAC5FB6C3792}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
18 changes: 18 additions & 0 deletions examples/CronExample/CronExample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\NekoSpace.Toolkit.HostedServices\NekoSpace.Toolkit.HostedServices.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

</Project>
29 changes: 29 additions & 0 deletions examples/CronExample/ExampleScheduledService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.Extensions.Logging;
using NekoSpace.Toolkit.HostedServices;

namespace CronExample;

class ExampleScheduledService : CronScheduledService
{
private readonly ILogger<ExampleScheduledService> _logger;

/// <summary>
/// Executes every 5 minutes.
/// </summary>
protected override string CronExpression => "*/5 * * * *";

public ExampleScheduledService(ILogger<ExampleScheduledService> logger)
{
_logger = logger;
}

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Doing works...");

// Simulate some works
await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken);

_logger.LogInformation("Work done!");
}
}
14 changes: 14 additions & 0 deletions examples/CronExample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using CronExample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

// Same syntax as ASP.NET Core

var builder = Host.CreateApplicationBuilder(args);

// Take a look at ExampleScheduledService.cs
builder.Services.AddHostedService<ExampleScheduledService>();

var app = builder.Build();

app.Run();
66 changes: 66 additions & 0 deletions src/NekoSpace.Toolkit.HostedServices/CronScheduledService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Diagnostics;
using Cronos;
using Microsoft.Extensions.Hosting;
using Timer = System.Timers.Timer;

namespace NekoSpace.Toolkit.HostedServices;

public abstract class CronScheduledService : IHostedService, IDisposable
{
private readonly Timer _timer = new();
private CancellationTokenSource? _stoppingCts;
private CronExpression? _cronExpr;

protected abstract string CronExpression { get; }

protected abstract Task ExecuteAsync(CancellationToken cancellationToken);

protected CronScheduledService()
{
_timer.Elapsed += async (_, _) =>
{
RefreshTimer();
await ExecuteAsync(_stoppingCts?.Token ?? default);
};
}

private void RefreshTimer()
{
_cronExpr ??= Cronos.CronExpression.Parse(CronExpression);

_timer.Stop();

var nextOccurrence = _cronExpr.GetNextOccurrence(DateTime.UtcNow);
if (nextOccurrence is not { } next)
{
Debug.WriteLine("No next occurrence.");
return;
}

var delay = next - DateTime.UtcNow;

Debug.WriteLine($"Delay {delay}, next occurrence: {next.ToLocalTime()}");

_timer.Interval = delay.TotalMilliseconds;
_timer.Start();
}

public virtual Task StartAsync(CancellationToken cancellationToken)
{
_stoppingCts = new CancellationTokenSource();
RefreshTimer();
return Task.CompletedTask;
}

public virtual Task StopAsync(CancellationToken cancellationToken)
{
_stoppingCts?.Cancel();
return Task.CompletedTask;
}

public virtual void Dispose()
{
_timer.Dispose();
GC.SuppressFinalize(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Cronos" Version="0.7.1"/>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0"/>
</ItemGroup>

</Project>

0 comments on commit ec81c29

Please sign in to comment.