Initial Commit;

This commit is contained in:
2024-03-10 00:43:57 +07:00
commit 5d80c6351c
29 changed files with 797 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

13
.idea/.idea.InServiceQue/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.InServiceQue.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

6
.idea/.idea.InServiceQue/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,7 @@
namespace InServiceQue.Core.Models;
public interface IQueueTask
{
string GetTypeString();
string GetPayloadString();
}

View File

@@ -0,0 +1,45 @@
namespace InServiceQue.Core.Models;
public class QueueTask: IQueueTask
{
private string _type;
public Guid Id { get; init; }
public string TaskType { get; init; } = default!;
public DateTime DateCreated { get; init; }
public DateTime? DateProcessed { get; private set; }
public DateTime? DateClosed { get; private set; }
public int Attempts { get; private set; }
public string? Payload { get; init; }
public QueueTask(string taskType, string? payload)
{
Id = Guid.NewGuid();
TaskType = taskType;
DateCreated = DateTime.UtcNow;
Payload = payload;
}
public QueueTask(IQueueTask task): this(task.GetTypeString(), task.GetPayloadString()) { }
public void MarkAttempt()
{
DateProcessed = DateTime.UtcNow;
Attempts++;
}
public void SolveTask()
{
DateClosed = DateTime.UtcNow;
}
string IQueueTask.GetTypeString()
{
return TaskType;
}
string IQueueTask.GetPayloadString()
{
return Payload ?? string.Empty;
}
}

View File

@@ -0,0 +1,20 @@
using System.Data;
using InServiceQue.Core.Models;
namespace InServiceQue.Core.Repositories;
public interface ITaskRepository: IDisposable
{
void Insert(QueueTask task);
Task InsertAsync(QueueTask task);
IDbTransaction StartTransaction();
Task<IDbTransaction> StartTransactionAsync();
void CommitTransaction(IDbTransaction tx);
Task CommitTransactionAsync(IDbTransaction tx);
void RollbackTransaction(IDbTransaction tx);
Task RollbackTransactionAsync(IDbTransaction tx);
QueueTask? GetNextTask(IDbTransaction tx);
Task<QueueTask?> GetNextTaskAsync(IDbTransaction tx);
void SaveTask(QueueTask task, IDbTransaction tx);
Task SaveTaskAsync(QueueTask task, IDbTransaction tx);
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\InServiceQue.Core\InServiceQue.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,82 @@
using System.Collections.Concurrent;
using System.Data;
using InServiceQue.Core.Models;
using InServiceQue.Core.Repositories;
namespace InServiceQue.InMemory;
public class TaskRepositoryInMemory: ITaskRepository
{
private static ConcurrentQueue<QueueTask> _que = new();
public void Dispose()
{
// TODO release managed resources here
}
public void Insert(QueueTask task)
{
_que.Enqueue(task);
}
public async Task InsertAsync(QueueTask task)
{
Insert(task);
await Task.CompletedTask;
}
public IDbTransaction StartTransaction()
{
return null;
}
public async Task<IDbTransaction> StartTransactionAsync()
{
return await Task.FromResult<IDbTransaction>(null);
}
public void CommitTransaction(IDbTransaction tx)
{
}
public async Task CommitTransactionAsync(IDbTransaction tx)
{
await Task.CompletedTask;
}
public void RollbackTransaction(IDbTransaction tx)
{
}
public async Task RollbackTransactionAsync(IDbTransaction tx)
{
await Task.CompletedTask;
}
public QueueTask? GetNextTask(IDbTransaction tx)
{
QueueTask? task;
_que.TryDequeue(out task);
return task;
}
public async Task<QueueTask?> GetNextTaskAsync(IDbTransaction tx)
{
QueueTask? task = null;
_que.TryDequeue(out task);
return await Task.FromResult(task);
}
public void SaveTask(QueueTask task, IDbTransaction tx)
{
}
public async Task SaveTaskAsync(QueueTask task, IDbTransaction tx)
{
await Task.CompletedTask;
}
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\InServiceQue.Core\InServiceQue.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Npgsql" Version="7.0.6" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,122 @@
using System.Data;
using Dapper;
using InServiceQue.Core.Models;
using InServiceQue.Core.Repositories;
using Npgsql;
namespace InServiceQue.Postgres;
public class TaskRepository: ITaskRepository
{
private readonly NpgsqlConnection _connection;
public TaskRepository(string connectionString)
{
_connection = new NpgsqlConnection(connectionString);
}
public void Insert(QueueTask task)
{
var sql =
$"INSERT INTO QUEUE({nameof(QueueTask.Id)}, {nameof(QueueTask.TaskType)}, {nameof(QueueTask.DateCreated)}, {nameof(QueueTask.Payload)}) VALUES (@{nameof(QueueTask.Id)}, @{nameof(QueueTask.TaskType)}, @{nameof(QueueTask.DateCreated)}, @{nameof(QueueTask.Payload)})";
_connection.Execute(sql, task);
}
public async Task InsertAsync(QueueTask task)
{
var sql =
$"INSERT INTO QUEUE({nameof(QueueTask.Id)}, {nameof(QueueTask.TaskType)}, {nameof(QueueTask.DateCreated)}, {nameof(QueueTask.Payload)}) VALUES (@{nameof(QueueTask.Id)}, @{nameof(QueueTask.TaskType)}, @{nameof(QueueTask.DateCreated)}, @{nameof(QueueTask.Payload)})";
await _connection.ExecuteAsync(sql, task);
}
public IDbTransaction StartTransaction()
{
return _connection.BeginTransaction();
}
public async Task<IDbTransaction> StartTransactionAsync()
{
return await _connection.BeginTransactionAsync();
}
public void CommitTransaction(IDbTransaction tx)
{
tx.Commit();
}
public async Task CommitTransactionAsync(IDbTransaction tx)
{
await ((NpgsqlTransaction)tx).CommitAsync();
}
public void RollbackTransaction(IDbTransaction tx)
{
tx.Rollback();
}
public async Task RollbackTransactionAsync(IDbTransaction tx)
{
await ((NpgsqlTransaction)tx).RollbackAsync();
}
public QueueTask? GetNextTask(IDbTransaction tx)
{
var sql = $@"select * from QUEUE where
order by {nameof(QueueTask.DateCreated)}
limit 1
for update skip locked";
return _connection.QuerySingleOrDefault<QueueTask>(sql, tx);
}
public async Task<QueueTask?> GetNextTaskAsync(IDbTransaction tx)
{
var sql = $@"select * from QUEUE
order by {nameof(QueueTask.DateCreated)}
limit 1
for update skip locked";
return await _connection.QuerySingleOrDefaultAsync<QueueTask>(sql, tx);
}
public void SaveTask(QueueTask task, IDbTransaction tx)
{
var sql = $@"UPDATE QUEUE
SET
{nameof(QueueTask.DateProcessed)} = @{nameof(QueueTask.DateProcessed)},
{nameof(QueueTask.DateClosed)} = @{nameof(QueueTask.DateClosed)},
{nameof(QueueTask.Attempts)} = @{nameof(QueueTask.Attempts)},
{nameof(QueueTask.Payload)} = @{nameof(QueueTask.Payload)}";
_connection.Execute(sql, task, tx);
}
public async Task SaveTaskAsync(QueueTask task, IDbTransaction tx)
{
var sql = $@"UPDATE QUEUE
SET {nameof(QueueTask.DateProcessed)} = @{nameof(QueueTask.DateProcessed)},
{nameof(QueueTask.DateClosed)} = @{nameof(QueueTask.DateClosed)},
{nameof(QueueTask.Attempts)} = @{nameof(QueueTask.Attempts)},
{nameof(QueueTask.Payload)} = @{nameof(QueueTask.Payload)}";
await _connection.ExecuteAsync(sql, task, tx);
}
private void ReleaseUnmanagedResources()
{
_connection.Dispose();
}
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~TaskRepository()
{
ReleaseUnmanagedResources();
}
}

View File

@@ -0,0 +1,56 @@
using System.Reflection;
using InServiceQue.Core.Models;
using InServiceQue.Core.Repositories;
using InServiceQue.InMemory;
using InServiceQue.Services;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace InServiceQue.Sample;
public static class DIExtensions
{
public static IServiceCollection RegisterInternals(this IServiceCollection services)
{
services.AddSingleton<ITypeRegistry, QueueTypeRegistry>();
services.AddTransient<ITaskRepository, TaskRepositoryInMemory>();
return services;
}
public static IServiceCollection RegisterQueues(this IServiceCollection services)
{
using var sp = services.BuildServiceProvider();
// find all types in the assembly that implement IQueueHandler<T>
var queueTypes = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces()
.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQueueHandler<>)));
var hostedServiceRegistrator = new HostedServiceRegistrator();
// register each query type with its corresponding interface
foreach (var queueType in queueTypes)
{
// get the T from IQueueHandler<T>
var type = queueType.GetInterfaces()
.Single(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQueueHandler<>));
// register the query type as a scoped service with its corresponding interface
services.AddScoped(queueType);
var typeRegistry = sp.GetRequiredService<ITypeRegistry>();
typeRegistry.RegisterTaskType(type.GenericTypeArguments.First().Name, type);
services.AddScoped(typeof(IQueueHandler<>)
.MakeGenericType(type.GenericTypeArguments), queueType);
//todo: bug here
var hostedServiceType = typeof(QueueService<>).MakeGenericType(type.GenericTypeArguments);
hostedServiceRegistrator.RegisterHostedService(services, hostedServiceType);
// services.AddSingleton(typeof(IQueueService<>)
// .MakeGenericType(type.GenericTypeArguments), );
}
return services;
}
}

View File

@@ -0,0 +1,19 @@
using System.Reflection;
namespace InServiceQue.Sample;
public class HostedServiceRegistrator
{
public void RegisterHostedService(IServiceCollection services, Type hostedServiceType)
{
Type servicesType = typeof(HostedServiceRegistrator);
MethodInfo methodInfo = servicesType.GetMethod("AddHostedService");
MethodInfo genericMethod = methodInfo.MakeGenericMethod(hostedServiceType);
genericMethod.Invoke(this, new object[] { services });
}
// Needed as a work-arround because we can't call the extension method with reflection.
public IServiceCollection AddHostedService<THostedService>(IServiceCollection services)
where THostedService : class, IHostedService =>
services.AddHostedService<THostedService>();
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\InServiceQue.Core\InServiceQue.Core.csproj" />
<ProjectReference Include="..\InServiceQue.InMemory\InServiceQue.InMemory.csproj" />
<ProjectReference Include="..\InServiceQue.Postgres\InServiceQue.Postgres.csproj" />
<ProjectReference Include="..\InServiceQue\InServiceQue.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
using InServiceQue.Core.Models;
using InServiceQue.Core.Repositories;
using InServiceQue.Sample;
var builder = WebApplication.CreateBuilder(args);
builder.Services.RegisterInternals();
builder.Services.RegisterQueues();
var app = builder.Build();
app.MapGet("/", (string msg) => app.Services.GetService<ITaskRepository>().Insert(new QueueTask(new SendMessageTask(new SendMessagePayload(){To = "John", From = "Garry", Message = msg}))));
app.Run();

View File

@@ -0,0 +1,37 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:61951",
"sslPort": 44361
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5121",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7149;http://localhost:5121",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,17 @@
using InServiceQue.Services;
namespace InServiceQue.Sample;
public class SendMessageHandler: IQueueHandler<SendMessageTask>
{
public bool Handle(string payload)
{
throw new NotImplementedException();
}
public Task<bool> HandleAsync(string payload)
{
Console.WriteLine(payload);
return Task.FromResult<bool>(true);
}
}

View File

@@ -0,0 +1,33 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using InServiceQue.Core.Models;
namespace InServiceQue.Sample;
public class SendMessagePayload
{
public string From { get; set; }
public string To { get; set; }
public string Message { get; set; }
}
public class SendMessageTask: IQueueTask
{
private SendMessagePayload _payload;
public SendMessageTask(SendMessagePayload payload)
{
_payload = payload;
}
public string GetTypeString()
{
return nameof(SendMessageTask);
}
public string GetPayloadString()
{
return JsonSerializer.Serialize<SendMessagePayload>(_payload);
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

40
InServiceQue.sln Normal file
View File

@@ -0,0 +1,40 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InServiceQue.Core", "InServiceQue.Core\InServiceQue.Core.csproj", "{28B85073-3A07-41E0-9B94-9738280C63A6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InServiceQue.Postgres", "InServiceQue.Postgres\InServiceQue.Postgres.csproj", "{CF132CFC-3FEA-470E-B404-5FB9C4F3100C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InServiceQue", "InServiceQue\InServiceQue.csproj", "{42B7AE80-3C8C-478A-BC7F-0E86CCBC8AD9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InServiceQue.Sample", "InServiceQue.Sample\InServiceQue.Sample.csproj", "{8B68480F-D518-4DF8-BCF1-E77596B47B09}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InServiceQue.InMemory", "InServiceQue.InMemory\InServiceQue.InMemory.csproj", "{97879D5B-FE10-4153-81DD-6B922929C993}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{28B85073-3A07-41E0-9B94-9738280C63A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28B85073-3A07-41E0-9B94-9738280C63A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28B85073-3A07-41E0-9B94-9738280C63A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28B85073-3A07-41E0-9B94-9738280C63A6}.Release|Any CPU.Build.0 = Release|Any CPU
{CF132CFC-3FEA-470E-B404-5FB9C4F3100C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF132CFC-3FEA-470E-B404-5FB9C4F3100C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF132CFC-3FEA-470E-B404-5FB9C4F3100C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF132CFC-3FEA-470E-B404-5FB9C4F3100C}.Release|Any CPU.Build.0 = Release|Any CPU
{42B7AE80-3C8C-478A-BC7F-0E86CCBC8AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42B7AE80-3C8C-478A-BC7F-0E86CCBC8AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42B7AE80-3C8C-478A-BC7F-0E86CCBC8AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42B7AE80-3C8C-478A-BC7F-0E86CCBC8AD9}.Release|Any CPU.Build.0 = Release|Any CPU
{8B68480F-D518-4DF8-BCF1-E77596B47B09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B68480F-D518-4DF8-BCF1-E77596B47B09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B68480F-D518-4DF8-BCF1-E77596B47B09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B68480F-D518-4DF8-BCF1-E77596B47B09}.Release|Any CPU.Build.0 = Release|Any CPU
{97879D5B-FE10-4153-81DD-6B922929C993}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97879D5B-FE10-4153-81DD-6B922929C993}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97879D5B-FE10-4153-81DD-6B922929C993}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97879D5B-FE10-4153-81DD-6B922929C993}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\InServiceQue.Core\InServiceQue.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
using InServiceQue.Core.Models;
namespace InServiceQue.Services;
public interface IQueueHandler
{
bool Handle(string payload);
Task<bool> HandleAsync(string payload);
}
public interface IQueueHandler<T>: IQueueHandler
where T: IQueueTask
{
}

View File

@@ -0,0 +1,15 @@
using InServiceQue.Core.Models;
using Microsoft.Extensions.Hosting;
namespace InServiceQue.Services;
public interface IQueueService: IHostedService
{
void AddTask(IQueueTask task);
void TryProcessTask();
}
public interface IQueueService<T> : IQueueService where T : IQueueTask
{
}

View File

@@ -0,0 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
namespace InServiceQue.Services;
public interface ITypeRegistry
{
void RegisterTaskType(string taskType, Type type);
IQueueHandler GetService(IServiceScope scope, string taskType);
}

View File

@@ -0,0 +1,118 @@
using InServiceQue.Core.Models;
using InServiceQue.Core.Repositories;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace InServiceQue.Services;
public class QueueService<T> : BackgroundService, IQueueService<T>
where T: IQueueTask
{
private readonly ITypeRegistry _typeRegistry;
private readonly ITaskRepository _repository;
private readonly IServiceProvider _serviceProvider;
public QueueService(ITypeRegistry typeRegistry, ITaskRepository repository, IServiceProvider serviceProvider)
{
_typeRegistry = typeRegistry;
_repository = repository;
_serviceProvider = serviceProvider;
}
public void AddTask(IQueueTask task)
{
var queueTask = new QueueTask(task);
_repository.Insert(queueTask);
}
public async Task AddTaskAsync(IQueueTask task)
{
var queueTask = new QueueTask(task);
await _repository.InsertAsync(queueTask);
}
public void TryProcessTask()
{
using (var scope = _serviceProvider.CreateScope())
{
using var tx = _repository.StartTransaction();
var queueTask = _repository.GetNextTask(tx);
if (queueTask == null)
{
_repository.RollbackTransaction(tx);
return;
}
var taskType = queueTask.TaskType;
var handler = _typeRegistry.GetService(scope, taskType);
try
{
Handle(queueTask, handler);
_repository.SaveTask(queueTask, tx);
_repository.CommitTransaction(tx);
}
catch (Exception e)
{
// ignored
}
}
}
public async Task TryProcessTaskAsync()
{
using (var scope = _serviceProvider.CreateScope())
{
using var tx = await _repository.StartTransactionAsync();
var queueTask = await _repository.GetNextTaskAsync(tx);
if (queueTask == null)
{
await _repository.RollbackTransactionAsync(tx);
return;
}
var taskType = queueTask.TaskType;
var handler = _typeRegistry.GetService(scope, taskType);
try
{
await HandleAsync(queueTask, handler);
await _repository.SaveTaskAsync(queueTask, tx);
await _repository.CommitTransactionAsync(tx);
}
catch (Exception e)
{
// ignored
}
}
}
internal void Handle(QueueTask queueTask, IQueueHandler handler)
{
queueTask.MarkAttempt();
var success = handler.Handle(queueTask.Payload ?? string.Empty);
if (success)
{
queueTask.SolveTask();
}
}
internal async Task HandleAsync(QueueTask queueTask, IQueueHandler handler)
{
queueTask.MarkAttempt();
var success = await handler.HandleAsync(queueTask.Payload ?? string.Empty);
if (success)
{
queueTask.SolveTask();
}
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
await TryProcessTaskAsync();
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
namespace InServiceQue.Services;
public class QueueTypeRegistry: ITypeRegistry
{
private static Dictionary<string, Type> _typeRegistry = new Dictionary<string, Type>();
public void RegisterTaskType(string taskType, Type type)
{
_typeRegistry.Add(taskType, type);
}
public IQueueHandler GetService(IServiceScope scope, string taskType)
{
var classType = _typeRegistry[taskType];
return (IQueueHandler)scope.ServiceProvider.GetService(classType);
}
}