EverTask 1.3.0
See the version list below for details.
dotnet add package EverTask --version 1.3.0
NuGet\Install-Package EverTask -Version 1.3.0
<PackageReference Include="EverTask" Version="1.3.0" />
paket add EverTask --version 1.3.0
#r "nuget: EverTask, 1.3.0"
// Install EverTask as a Cake Addin #addin nuget:?package=EverTask&version=1.3.0 // Install EverTask as a Cake Tool #tool nuget:?package=EverTask&version=1.3.0
Overview
EverTask is a .NET library for executing background tasks in .NET applications. It is designed to be simple and focuses on task persistence, ensuring that pending tasks resume upon application restart.
This project is in its initial stages, more detailed documentation will be provided in the future.
Features
Efficient Task Processing
EverTask employs a non-polling approach for task management, utilizing the .NET's System.Threading.Channels
to create a BoundedQueue
. This queue efficiently manages task execution without the need for constant database polling. Upon application restart after a stop, any unprocessed tasks are retrieved from the database in bulk and re-queued in the channel's queue for execution by the background service. This design ensures a seamless and efficient task processing cycle, even across application restarts.
Creating Requests and Handlers with Lifecycle Control
This example demonstrates how to create a request and its corresponding handler in EverTask. The SampleTaskRequest
and SampleTaskRequestHandler
illustrate the basic structure. Additionally, the handler includes optional overrides that allow you to control and monitor the lifecycle of a background task, providing hooks for when a task starts, completes, or encounters an error.
public record SampleTaskRequest(string TestProperty) : IEverTask;
public class SampleTaskRequestHanlder : EverTaskHandler<SampleTaskRequest>
{
private readonly ILogger<SampleTaskRequestHanlder> _logger;
public SampleTaskRequestHanlder(ILogger<SampleTaskRequestHanlder> logger)
{
_logger = logger;
}
public override Task Handle(SampleTaskRequest backgroundTask, CancellationToken cancellationToken)
{
_logger.LogInformation($"Property value: {backgroundTask.TestProperty}");
return Task.CompletedTask;
}
// Optional Overrides
public override ValueTask OnStarted(Guid persistenceId)
{
_logger.LogInformation($"====== TASK WITH ID {persistenceId} STARTED IN BACKGROUND ======");
return ValueTask.CompletedTask;
}
public override ValueTask OnCompleted(Guid persistenceId)
{
_logger.LogInformation($"====== TASK WITH ID {persistenceId} COMPLETED IN BACKGROUND ======");
return ValueTask.CompletedTask;
}
public override ValueTask OnError(Guid persistenceId, Exception? exception, string? message)
{
_logger.LogError(exception, $"Error in task with ID {persistenceId}: {message}");
return ValueTask.CompletedTask;
}
protected override ValueTask DisposeAsyncCore()
{
_logger.LogInformation("====== TASK DISPOSED IN BACKGROUND ======");
return base.DisposeAsyncCore();
}
}
Task Dispatch
To dispatch a task, obtain an instance of ITaskDispatcher
. This can be done using Dependency Injection:
// Retrieving ITaskDispatcher via method injection
var _dispatcher = serviceProvider.GetService<ITaskDispatcher>();
// Alternatively, ITaskDispatcher can be injected directly into the constructor of your class
_dispatcher.Dispatch(new SampleTaskRequest("Hello World"));
Dispatching Tasks with Delay
You can also schedule tasks to be executed after a certain delay. This can be achieved using either TimeSpan
or DateTimeOffset
.
Using TimeSpan for Relative Delay
To delay task execution by a relative time period, use TimeSpan
. This is useful when you want to postpone a task by a specific duration, such as 30 minutes or 2 hours from now.
// Delaying task execution by 30 minutes
var delay = TimeSpan.FromMinutes(30);
_dispatcher.Dispatch(new SampleTaskRequest("Delayed Task"), delay);
Using DateTimeOffset for Absolute Delay
Alternatively, use DateTimeOffset
for scheduling a task at a specific future point in time. This is particularly useful for tasks that need to be executed at a specific date and time, regardless of the current moment.
// Scheduling a task for a specific time in the future
var scheduledTime = DateTimeOffset.Now.AddHours(2); // 2 hours from now
_dispatcher.Dispatch(new SampleTaskRequest("Scheduled Task"), scheduledTime);
Â
💡 Remember: Delayed and scheduled tasks are also persistent. If your app restarts, you won't lose these tasks – they'll be executed at the right time!
Task Cancellation
When you dispatch, you can capture the returned GUID to keep track of the task. If you need to cancel this task before it starts, use the Cancel
method of ITaskDispatcher
with this ID.
// Dispatching a task and getting its unique ID
Guid taskId = _dispatcher.Dispatch(new SampleTaskRequest("Cancelable Task"));
// Cancelling the task (if not started yet)
_dispatcher.Cancel(taskId);
Â
💡 Note: Cancellation is effective only if the task has not yet begun. Once a task is in progress, the Cancel method will not affect its execution.
Basic Configuration
builder.Services.AddEverTask(opt =>
{
opt.SetChannelOptions(50)
.SetThrowIfUnableToPersist(true)
.RegisterTasksFromAssembly(typeof(Program).Assembly);
})
.AddMemoryStorage();
Advanced Configuration
builder.Services.AddEverTask(opt =>
{
opt.SetChannelOptions(settingsManager.Settings.Properties.BackgroundQueueCapacity)
.SetThrowIfUnableToPersist(true)
.RegisterTasksFromAssembly(typeof(AppSettings).Assembly);
})
.AddSqlServerStorage(configuration.GetConnectionString("QueueWorkerSqlStorage")!,
opt =>
{
opt.SchemaName = "EverTask";
opt.AutoApplyMigrations = true;
})
.AddSerilog(opt => opt.ReadFrom.Configuration(configuration, new ConfigurationReaderOptions { SectionName = "EverTaskSerilog" }));
Fluent Service Configuration
EverTaskService
can be configured using a series of fluent methods, allowing a clear and user-friendly way to set up the service. These methods enable precise control over task processing, persistence, and parallel execution. Below are the available configuration methods, along with their default values and types:
SetChannelOptions (Overloaded Methods)
- Type:
Action<BoundedChannelOptions>
orint
- Default: Capacity set to 100,
FullMode
set toBoundedChannelFullMode.Wait
- Functionality: Configures the behavior of the task queue. You can directly specify the queue capacity or provide a
BoundedChannelOptions
object. This defines the maximum number of tasks that can be queued and the behavior when the queue is full.
SetThrowIfUnableToPersist
- Type:
bool
- Default:
true
- Functionality: Determines whether the service should throw an exception if it is unable to persist a task. When enabled, it ensures that task persistence failures are explicitly managed, aiding in data integrity.
SetMaxDegreeOfParallelism
- Type:
int
- Default:
1
- Functionality: Sets the maximum number of tasks that can be executed concurrently. The default sequential execution can be adjusted to enable parallel processing, optimizing task throughput in multi-core systems.
RegisterTasksFromAssembly
- Functionality: Facilitates the registration of task handlers from a single assembly. This is particularly beneficial for applications structured in a modular fashion, enabling easy integration of task handlers.
RegisterTasksFromAssemblies
- Functionality: Allows for the registration of task handlers from multiple assemblies. This approach suits larger applications with distributed task handling logic spread across various modules or libraries.
SQL Server Persistence
- Default Schema: Creates a new schema named
EverTask
by default. This approach avoids adding clutter to the main data schema. - Migration Table: Places the Entity Framework Core migration table in the custom
EverTask
schema. - Schema Customization: Allows specifying a different schema or using
null
to default to the main schema. - Migration Handling: Option to apply database migrations automatically or handle them manually.
Serilog Integration
- Default Logging: Uses .NET configured
ILogger
by default. - Serilog Option: Enables adding Serilog as a separate logger for EverTask, with customizable options.
- Example Configuration in appsettings.json:
"EverTaskSerilog": { "MinimumLevel": { "Default": "Information", "Override": { "Default": "Information", "Microsoft": "Warning", "Microsoft.AspNetCore.SpaProxy": "Information", "Microsoft.Hosting.Lifetime": "Information", "Microsoft.EntityFrameworkCore.Database.Command": "Information" } }, "WriteTo": [ { "Name": "Console" }, { "Name": "File", "Args": { "path": "Logs/evertask-log-.txt", "rollingInterval": "Day", "fileSizeLimitBytes": 10485760, "retainedFileCountLimit": 10, "shared": true, "flushToDiskInterval": "00:00:01" } } ], "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ], "Properties": { "Application": "CliClub" } }
EverTask and EverTask.Abstractions
EverTask is complemented by the EverTask.Abstractions
package, designed for use in Application projects where additional implementations are not required. This allows separation of concerns, keeping your application layer free from infrastructural code.
In your Infrastructure project, where EverTask is added, specify the assembly (or assemblies) containing IEverTask
requests. This modular approach ensures that the application layer remains clean and focused, while the infrastructure layer handles task execution and management.
Serialization and deserialization of Requests for Persistence
EverTask uses Newtonsoft.Json for serializing and deserializing task requests, due to its robust support for polymorphism and inheritance, features that are limited in System.Text.Json. It is recommended to use simple objects for task requests, preferably primitives or uncomplicated complex objects, to ensure smooth serialization. In cases where EverTask is unable to serialize a request, it will throw an exception during the Dispatch
method. This design choice emphasizes reliability in task persistence, ensuring that only serializable tasks are queued for execution.
Future Developments
Feature | Description |
---|---|
Web Dashboard | Implement a simple web dashboard for monitoring tasks. |
Recurring Tasks | Recurring tasks using cron expressions or fluent builder. |
Support for New Storage Options | Considering the inclusion of additional storage options like MySql, Postgres, and various DocumentDBs initially supported by EfCore, with the possibility of expanding to other databases. |
Improving documentation | docs needs more love... |
Â
🌟 Acknowledgements
Special thanks to jbogard for the MediaTr project, providing significant inspiration in the development of key components of this library, especially in the creation of:
I have included comments within these files to acknowledge and reference the specific parts of the MediaTr project that inspired them.
Their approach and architecture have been instrumental in shaping the functionality and design of these elements.
This project includes code from MediatR, which is licensed under the Apache 2.0 License. The full text of the license can be found in the LICENSE file.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- EverTask.Abstractions (>= 1.3.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 7.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 7.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 7.0.0)
- Microsoft.Extensions.Logging (>= 7.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.0)
- Newtonsoft.Json (>= 13.0.3)
-
net7.0
- EverTask.Abstractions (>= 1.3.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 7.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 7.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 7.0.0)
- Microsoft.Extensions.Logging (>= 7.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.0)
- Newtonsoft.Json (>= 13.0.3)
-
net8.0
- EverTask.Abstractions (>= 1.3.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Newtonsoft.Json (>= 13.0.3)
NuGet packages (8)
Showing the top 5 NuGet packages that depend on EverTask:
Package | Downloads |
---|---|
EverTask.EfCore
Easy background task with persistence for ASP.NET Core |
|
EverTask.SqlServer
Easy background task with persistence for ASP.NET Core |
|
EverTask.Serilog
Easy background task with persistence for ASP.NET Core |
|
EverTask.Monitor.AspnetCore.SignalR
Easy background task with persistence for ASP.NET Core |
|
EverTask.Storage.EfCore
Easy background task with persistence for ASP.NET Core |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.5.4 | 213 | 5/31/2024 |
1.5.3 | 200 | 5/23/2024 |
1.5.2 | 216 | 4/9/2024 |
1.5.1 | 421 | 11/23/2023 |
1.5.0 | 219 | 11/21/2023 |
1.4.1 | 230 | 11/19/2023 |
1.4.0 | 194 | 11/19/2023 |
1.3.0 | 212 | 11/19/2023 |
1.2.0 | 197 | 11/17/2023 |
1.1.0 | 193 | 11/16/2023 |
1.0.4-beta | 71 | 11/13/2023 |
1.0.3-beta | 64 | 11/13/2023 |
1.0.0 | 187 | 11/15/2023 |