Aiursoft.Canon
9.0.11
dotnet add package Aiursoft.Canon --version 9.0.11
NuGet\Install-Package Aiursoft.Canon -Version 9.0.11
<PackageReference Include="Aiursoft.Canon" Version="9.0.11" />
<PackageVersion Include="Aiursoft.Canon" Version="9.0.11" />
<PackageReference Include="Aiursoft.Canon" />
paket add Aiursoft.Canon --version 9.0.11
#r "nuget: Aiursoft.Canon, 9.0.11"
#:package Aiursoft.Canon@9.0.11
#addin nuget:?package=Aiursoft.Canon&version=9.0.11
#tool nuget:?package=Aiursoft.Canon&version=9.0.11
Aiursoft Canon
Aiursoft Canon is used to implement dependency-based Fire and Forget for .NET projects, which means starting a heavy task without waiting for it to complete or caring about its success, and continuing with subsequent logic.
This is very useful in many scenarios to avoid blocking, such as when sending emails.
Why this project
The traditional way to fire and forget in C# is:
_ = Task.Run(() =>
{
// Do something heavy
});
However, if your task depends on something like Entity Framework, it's hard to control it's life cycle.
How to install
First, install Aiursoft.Canon
to your ASP.NET Core project from nuget.org:
dotnet add package Aiursoft.Canon
Add the service to your IServiceCollection
in StartUp.cs
:
using Aiursoft.Canon;
services.AddTaskCanon();
Your project will get:
// A retry engine.
services.AddTransient<RetryEngine>();
// An easier to use Cache service.
services.AddTransient<CacheService>();
// A transient service to throw an exception if the task takes too long.
services.AddTransient<TimeoutStopper>();
// A transient service to replace 'Task.WhenAll()'.
services.AddTransient<CanonPool>();
// Simple Fire and forget service that runs immediately.
services.AddSingleton<CanonService>();
// Application singleton background job queue.
services.AddSingleton<CanonQueue>();
// A watch service to measure how much time a task used.
services.AddTransient<WatchService>();
How to use Aiursoft.CanonQueue
Then, you can inject CanonQueue
to your controller. And now, you can fire and forget your task like this:
public class YourController : Controller
{
private readonly CanonQueue _canonQueue;
public YourController(CanonQueue canonQueue)
{
_canonQueue = canonQueue;
}
public IActionResult Send()
{
// Send an confirmation email here:
_canonQueue.QueueWithDependency<EmailSender>(async (sender) =>
{
await sender.SendAsync(); // Which may be slow. The service 'EmailSender' will be kept alive!
});
return Ok();
}
}
That's it.
How to use Aiursoft.CanonPool
You can also put all your tasks to a task queue, and run those tasks with a limit of concurrency:
Inject CanonPool
first:
private readonly EmailSender _sender;
private readonly CanonPool _canonPool;
public DemoController(
EmailSender sender,
CanonPool canonPool)
{
_sender = sender;
_canonPool = canonPool;
}
Now you can register tasks to the pool and run them concurrently with a controlled limit.
foreach (var user in users)
{
_canonPool.RegisterNewTaskToPool(async () =>
{
await _sender.SendAsync(user); // Which may be slow.
});
}
// Execute tasks in pool, with a maximum of 8 running at the same time.
await _canonPool.RunAllTasksInPoolAsync();
This is far better than this:
var tasks = new List<Task>();
foreach (var user in users)
{
tasks.Add(Task.Run(() => sender.SendAsync(user)));
}
// This may start too many tasks at once and overload your remote service.
await Task.WhenAll(tasks);
You can control the concurrency of your tasks. For example, you can start 16 tasks at the same time:
await _canonPool.RunAllTasksInPoolAsync(16); // Start the engine with 16 concurrency.
That helps you to avoid blocking your Email sender or database with too many tasks.
How to use Aiursoft.RetryEngine
The RetryEngine
is useful for automatically retrying an operation that might fail, such as a network request. It uses an exponential backoff strategy to wait between retries.
First, inject RetryEngine
into your service or controller:
private readonly RetryEngine _retry;
public MyService(RetryEngine retry)
{
_retry = retry;
}
Now, you can wrap a potentially failing task with RunWithRetry
.
public async Task<string> CallUnreliableApiService()
{
var result = await _retry.RunWithRetry(async (attempt) =>
{
// This code will be executed up to 5 times.
Console.WriteLine($"Trying to call the API, attempt {attempt}...");
var client = new HttpClient();
var response = await client.GetStringAsync("https://example.com/api/data");
return response;
},
attempts: 5,
when: e => e is HttpRequestException); // Only retry on HttpRequestException.
return result;
}
If the API call fails with an HttpRequestException
, the RetryEngine
will wait for a short, random duration (which increases after each failure) and then try again. If it fails 5 times, the exception will be re-thrown.
How to use Aiursoft.CacheService
CacheService
provides a simple way to cache results from expensive operations in memory, reducing redundant calls.
Inject CacheService
where you need it:
private readonly CacheService _cache;
private readonly MyDbContext _dbContext;
public MyController(
CacheService cache,
MyDbContext dbContext)
{
_cache = cache;
_dbContext = dbContext;
}
Use RunWithCache
to get data. It will first try to find the data in the cache. If it's not there, it will execute your fallback function, cache the result, and then return it.
public async Task<IActionResult> GetDashboard()
{
// Define a unique key for this cache entry.
var cacheKey = "dashboard-stats";
// This data will be cached for 10 minutes.
var stats = await _cache.RunWithCache(cacheKey, async () =>
{
// This logic only runs if the cache is empty or expired.
// It's an expensive database query.
return await _dbContext.Statistics.SumAsync(t => t.Value);
},
cachedMinutes: _ => TimeSpan.FromMinutes(10));
return View(stats);
}
The next time GetDashboard
is called within 10 minutes, the expensive database query will be skipped, and the result will be served directly from the in-memory cache.
How to use Aiursoft.TimeoutStopper
TimeoutStopper
allows you to run a task with a specified time limit. If the task doesn't complete within the timeout, a TimeoutException
is thrown. This is useful for preventing long-running operations from blocking your application indefinitely.
Inject TimeoutStopper
into your class:
private readonly TimeoutStopper _timeoutStopper;
public MyProcessor(TimeoutStopper timeoutStopper)
{
_timeoutStopper = timeoutStopper;
}
Wrap your long-running task with RunWithTimeout
.
public async Task ProcessDataWithDeadline()
{
try
{
// We give this operation a 5-second deadline.
await _timeoutStopper.RunWithTimeout(async (cancellationToken) =>
{
// Simulate a very long-running process.
Console.WriteLine("Starting a heavy computation...");
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
Console.WriteLine("Computation finished."); // This line will not be reached.
}, timeoutInSeconds: 5);
}
catch (TimeoutException ex)
{
Console.WriteLine(ex.Message); // "The operation timed out after 5 seconds."
// Handle the timeout case, e.g., log an error or notify the user.
}
}
In this example, Task.Delay
simulates a 10-second task. Because the timeout is set to 5 seconds, the TimeoutStopper
will throw a TimeoutException
before the task can complete.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Aiursoft.Scanner.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Caching.Memory (>= 9.0.9)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on Aiursoft.Canon:
Package | Downloads |
---|---|
Aiursoft.AiurProtocol
API Communication practice |
|
Aiursoft.GitRunner
Nuget package of 'GitRunner' provided by Aiursoft |
|
Aiursoft.NiBot.Core
Nuget package of 'Core' provided by Aiursoft |
|
Aiursoft.Voyager.Core
Nuget package of 'Core' provided by Aiursoft |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
9.0.11 | 162 | 9/10/2025 |
9.0.10 | 128 | 9/6/2025 |
9.0.9 | 382 | 8/5/2025 |
9.0.8 | 238 | 7/9/2025 |
9.0.7 | 555 | 6/11/2025 |
9.0.6 | 184 | 6/1/2025 |
9.0.5 | 567 | 4/9/2025 |
9.0.4 | 226 | 4/8/2025 |
9.0.3 | 334 | 3/12/2025 |
9.0.2 | 359 | 2/12/2025 |
9.0.1 | 291 | 1/15/2025 |
9.0.0 | 285 | 12/30/2024 |
8.0.6 | 610 | 10/27/2024 |
8.0.5 | 284 | 10/9/2024 |
8.0.4 | 707 | 6/24/2024 |
8.0.3 | 227 | 6/12/2024 |
8.0.2 | 194 | 6/6/2024 |
8.0.1 | 2,028 | 2/19/2024 |
8.0.0 | 615 | 2/19/2024 |
7.0.8 | 446 | 2/2/2024 |
7.0.7 | 217 | 1/30/2024 |
7.0.6 | 2,385 | 11/22/2023 |
7.0.5 | 744 | 11/2/2023 |
7.0.4 | 1,094 | 9/21/2023 |
7.0.3 | 167 | 9/21/2023 |
7.0.2 | 376 | 9/13/2023 |
7.0.1 | 185 | 9/13/2023 |
7.0.0 | 574 | 9/5/2023 |
6.0.13 | 339 | 8/20/2023 |
6.0.12 | 1,189 | 6/24/2023 |
6.0.11 | 417 | 6/23/2023 |
6.0.10 | 227 | 6/19/2023 |
6.0.9 | 628 | 6/18/2023 |
6.0.8 | 227 | 6/18/2023 |
6.0.7 | 207 | 6/18/2023 |
6.0.6 | 235 | 6/15/2023 |
6.0.5 | 222 | 6/15/2023 |
6.0.4 | 230 | 6/10/2023 |
6.0.1 | 256 | 6/7/2023 |
6.0.0 | 242 | 6/7/2023 |