Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Milestone 1 - Implement Recurring Tasks of Background Processing #169

Open
wants to merge 1 commit into
base: dev/milestone-1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using MongoDB.Bson;
using System.Text.RegularExpressions;
using System.Linq.Expressions;

namespace MongoDB.Driver;

internal static class FilterBuilderExtensions
{
public static FilterDefinition<TDocument> Contains<TDocument>(this FilterDefinitionBuilder<TDocument> builder, Expression<Func<TDocument, object>> expression, string value)
=> builder.Regex(expression, new BsonRegularExpression(new Regex($".*{value}.*", RegexOptions.IgnoreCase)));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using MongoDB.Driver;
using uBeac.Repositories.History;
using uBeac.Repositories.MongoDB;

namespace uBeac.BackgroundTasks.Logging.MongoDB;

public class MongoBackgroundTaskLogRepository<TEntity, TContext> : MongoEntityRepository<TEntity, TContext>, IBackgroundTaskLogRepository<TEntity>
where TEntity : BackgroundTaskLog
where TContext : IMongoDBContext
{
public MongoBackgroundTaskLogRepository(TContext mongoDbContext, IApplicationContext applicationContext, IHistoryManager history) : base(mongoDbContext, applicationContext, history)
{
}

public async Task<IEnumerable<TEntity>> Search(BackgroundTaskSearchRequest request, CancellationToken cancellationToken = default)
{
var builder = Builders<TEntity>.Filter;

var filter = builder.Empty;

if (request.FromDate.HasValue) filter &= builder.Gte(x => x.StartDate, request.FromDate);
if (request.ToDate.HasValue) filter &= builder.Lte(x => x.StartDate, request.ToDate);

if (!string.IsNullOrWhiteSpace(request.Term)) filter &= builder.Contains(x => x.Option.Name, request.Term) |
builder.Contains(x => x.Option.Type, request.Term);

return (await Collection.FindAsync<TEntity>(filter, null, cancellationToken)).ToList(cancellationToken);
}
}

public class MongoBackgroundTaskLogRepository<TContext> : MongoBackgroundTaskLogRepository<BackgroundTaskLog, TContext>, IBackgroundTaskLogRepository
where TContext : IMongoDBContext
{
public MongoBackgroundTaskLogRepository(TContext mongoDbContext, IApplicationContext applicationContext, IHistoryManager history) : base(mongoDbContext, applicationContext, history)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using uBeac.BackgroundTasks.Logging.MongoDB;
using uBeac.Repositories.MongoDB;

namespace Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMongoBackgroundTaskLogging<TContext>(this IServiceCollection services)
where TContext : IMongoDBContext
{
services.AddBackgroundTaskLogging<MongoBackgroundTaskLogRepository<TContext>>();

return services;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageId>uBeac.BackgroundTasks.Logging.MongoDB</PackageId>
<Authors>uBeac</Authors>
<PackageIcon>ubeac-logo.jpg</PackageIcon>
<PackageProjectUrl>https://github.com/ubeac/ubeac-api</PackageProjectUrl>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Repository\uBeac.Core.Repositories.MongoDB\uBeac.Core.Repositories.MongoDB.csproj" />
<ProjectReference Include="..\uBeac.Core.BackgroundTasks.Logging\uBeac.Core.BackgroundTasks.Logging.csproj" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\..\ubeac-logo.jpg">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions src/BackgroundTasks/uBeac.Core.BackgroundTasks.Logging/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace uBeac.BackgroundTasks.Logging;

public class BackgroundTaskLog : Entity
{
public RecurringBackgroundTaskOption Option { get; set; }

public List<string> Descriptions { get; set; }

public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }

public BackgroundTaskStatus Status { get; set; }

public int Succeed { get; set; }
public int Failure { get; set; }
}

public enum BackgroundTaskStatus
{
Idle = 1,
Running = 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace uBeac.BackgroundTasks.Logging;

public class BackgroundTaskSearchRequest
{
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }

public string Term { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using uBeac.Repositories;

namespace uBeac.BackgroundTasks.Logging;

public interface IBackgroundTaskLogRepository<TEntity> : IEntityRepository<TEntity>
where TEntity : BackgroundTaskLog
{
Task<IEnumerable<TEntity>> Search(BackgroundTaskSearchRequest request, CancellationToken cancellationToken = default);
}

public interface IBackgroundTaskLogRepository : IBackgroundTaskLogRepository<BackgroundTaskLog>
{
}
72 changes: 72 additions & 0 deletions src/BackgroundTasks/uBeac.Core.BackgroundTasks.Logging/Service.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using uBeac.Services;

namespace uBeac.BackgroundTasks.Logging;

public interface IBackgroundTaskLogService<TEntity> : IEntityService<TEntity>
where TEntity : BackgroundTaskLog
{
Task<IListResult<TEntity>> Search(BackgroundTaskSearchRequest request, CancellationToken cancellationToken = default);

Task Update(TEntity log, string description, bool? success = null, CancellationToken cancellationToken = default);
}

public interface IBackgroundTaskLogService : IBackgroundTaskLogService<BackgroundTaskLog>
{
Task<BackgroundTaskLog> Create(RecurringBackgroundTaskOption option, CancellationToken cancellationToken = default);
}

public class BackgroundTaskLogService<TEntity> : EntityService<TEntity>, IBackgroundTaskLogService<TEntity>
where TEntity : BackgroundTaskLog
{
private readonly IBackgroundTaskLogRepository<TEntity> _repository;

public BackgroundTaskLogService(IBackgroundTaskLogRepository<TEntity> repository) : base(repository)
{
_repository = repository;
}

public async Task<IListResult<TEntity>> Search(BackgroundTaskSearchRequest request, CancellationToken cancellationToken = default)
{
var result = await _repository.Search(request, cancellationToken);

return result.ToListResult();
}

public async Task Update(TEntity log, string description, bool? success = null, CancellationToken cancellationToken = default)
{
if (success.HasValue && success.Value)
{
log.Succeed += 1;
}
else if (success.HasValue)
{
log.Failure += 1;
}

log.Descriptions.Add($"{description} at {DateTime.Now}.\n");

await Task.Factory.StartNew(async () => await Update(log, cancellationToken), cancellationToken);
}
}

public class BackgroundTaskLogService : BackgroundTaskLogService<BackgroundTaskLog>, IBackgroundTaskLogService
{
public BackgroundTaskLogService(IBackgroundTaskLogRepository repository) : base(repository)
{
}

public async Task<BackgroundTaskLog> Create(RecurringBackgroundTaskOption option, CancellationToken cancellationToken = default)
{
var log = new BackgroundTaskLog
{
Descriptions = new List<string> { "Background Task is started!" },
StartDate = DateTime.Now,
Status = BackgroundTaskStatus.Running,
Option = option
};

await Task.Factory.StartNew(async () => await Create(log, cancellationToken), cancellationToken);

return log;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using uBeac.BackgroundTasks.Logging;

namespace Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddBackgroundTaskLogging<TEntity, TRepository, TService>(this IServiceCollection services)
where TEntity : BackgroundTaskLog
where TRepository : class, IBackgroundTaskLogRepository<TEntity>
where TService : class, IBackgroundTaskLogService<TEntity>
{
services.AddScoped<IBackgroundTaskLogRepository<TEntity>, TRepository>();
services.AddScoped<IBackgroundTaskLogService<TEntity>, TService>();

return services;
}

public static IServiceCollection AddBackgroundTaskLogging<TRepository, TService>(this IServiceCollection services)
where TRepository : class, IBackgroundTaskLogRepository
where TService : class, IBackgroundTaskLogService
{
services.AddScoped<IBackgroundTaskLogRepository, TRepository>();
services.AddScoped<IBackgroundTaskLogService, TService>();

return services;
}

public static IServiceCollection AddBackgroundTaskLogging<TRepository>(this IServiceCollection services)
where TRepository : class, IBackgroundTaskLogRepository
{
services.AddScoped<IBackgroundTaskLogRepository, TRepository>();
services.AddScoped<IBackgroundTaskLogService, BackgroundTaskLogService>();

return services;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageId>uBeac.BackgroundTasks.Logging</PackageId>
<Authors>uBeac</Authors>
<PackageIcon>ubeac-logo.jpg</PackageIcon>
<PackageProjectUrl>https://github.com/ubeac/ubeac-api</PackageProjectUrl>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Service\uBeac.Core.Services\uBeac.Core.Services.csproj" />
<ProjectReference Include="..\uBeac.Core.BackgroundTasks\uBeac.Core.BackgroundTasks.csproj" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\..\ubeac-logo.jpg">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Microsoft.Extensions.Hosting;

namespace uBeac.BackgroundTasks;

// The default behavior of the BackgroundService is that StartAsync calls ExecuteAsync.
// It's a default, the StartAsync is virtual so you could override it.
// If you create a subclass of BackgroundService, you must implement ExecuteAsync
// (because it's abstract). That should do your work.
// https://stackoverflow.com/questions/60356396/difference-between-executeasync-and-startasync-methods-in-backgroundservice-net#:~:text=The%20default%20behavior%20of%20the,so%20you%20could%20override%20it.&text=If%20you%20create%20a%20subclass,That%20should%20do%20your%20work.
public class BackgroundTaskManager : BackgroundService
{
private readonly IEnumerable<IRecurringBackgroundTask> _backgroundTasks;

public BackgroundTaskManager(IEnumerable<IRecurringBackgroundTask> backgroundTasks)
{
_backgroundTasks = backgroundTasks;
}

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
foreach (var backgroundTask in _backgroundTasks)
{
await backgroundTask.Start(cancellationToken);
}
}

public override async Task StopAsync(CancellationToken cancellationToken)
{
foreach (var backgroundTask in _backgroundTasks)
{
await backgroundTask.Stop(cancellationToken);
}
await base.StopAsync(cancellationToken);
}

public override void Dispose()
{
foreach (var backgroundTask in _backgroundTasks)
{
backgroundTask.Dispose();
}
base.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace uBeac.BackgroundTasks;

public interface IRecurringBackgroundTask : IDisposable
{
public Task Process(CancellationToken cancellationToken = default);
public Task Start(CancellationToken cancellationToken = default);
public Task Stop(CancellationToken cancellationToken = default);
}

public abstract class RecurringBackgroundTask : IRecurringBackgroundTask
{
private Timer _timer = null;

public RecurringBackgroundTaskOption Option { get; }

protected RecurringBackgroundTask(RecurringBackgroundTaskOption option)
{
Option = option;
}

public virtual Task Start(CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
DisableTimer();
else
EnableTimer();

return Task.CompletedTask;
}

public virtual Task Stop(CancellationToken cancellationToken = default)
{
DisableTimer();
return Task.CompletedTask;
}

public abstract Task Process(CancellationToken cancellationToken = default);

public virtual void Dispose()
{
_timer?.Dispose();
}

private void TimerElapsed(object state)
{
Process().Wait();
}

private void EnableTimer()
{
var durationToStart = TimeSpan.Zero;
var recurringDuration = Timeout.InfiniteTimeSpan;

if (Option.Start.HasValue)
{
var startTime = DateTime.Today + Option.Start.Value;

if (startTime < DateTime.Now && Option.Recurring.HasValue)
{
while (startTime < DateTime.Now)
startTime += Option.Recurring.Value;
}

durationToStart = (DateTime.Now - startTime).Duration();

}

if (Option.Recurring.HasValue)
recurringDuration = Option.Recurring.Value;

_timer = new Timer(TimerElapsed, null, durationToStart, recurringDuration);
}

private void DisableTimer()
{
_timer?.Change(Timeout.Infinite, 0);
}
}
Loading