initial commit

This commit is contained in:
2026-01-28 23:28:31 +01:00
Unverified
parent c5b48a1975
commit 7a9c9260af
13 changed files with 567 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
using System.Text.Json;
namespace TimetableDesigner.Backend.Events.OutboxPattern;
public class Event
{
public Guid Id { get; set; }
public required string Payload { get; set; }
public required string PayloadType { get; set; }
public DateTimeOffset OccuredOn { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset? LastRetryOn { get; set; }
public uint RetryCount { get; set; }
public Event(object payload)
{
Payload = JsonSerializer.Serialize(payload);
PayloadType = payload.GetType().FullName!;
}
}

View File

@@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace TimetableDesigner.Backend.Events.OutboxPattern;
public class EventConfiguration : IEntityTypeConfiguration<Event>
{
public void Configure(EntityTypeBuilder<Event> builder)
{
builder.HasKey(x => x.Id);
builder.HasIndex(x => x.Id)
.IsUnique();
builder.Property(x => x.Id)
.IsRequired();
builder.Property(x => x.Payload)
.IsRequired();
builder.Property(x => x.PayloadType)
.IsRequired();
builder.Property(x => x.OccuredOn)
.IsRequired();
builder.Property(x => x.LastRetryOn);
builder.Property(x => x.RetryCount)
.IsRequired();
}
}

View File

@@ -0,0 +1,50 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
namespace TimetableDesigner.Backend.Events.OutboxPattern;
public class EventOutboxSender<TDbContext> : BackgroundService where TDbContext : DbContext, IEventOutboxDbContext
{
private readonly TDbContext _databaseContext;
private readonly IEventQueuePublisher _eventQueuePublisher;
public EventOutboxSender(TDbContext databaseContext, IEventQueuePublisher eventQueuePublisher)
{
_databaseContext = databaseContext;
_eventQueuePublisher = eventQueuePublisher;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Event? eventData = await _databaseContext.Events.FirstOrDefaultAsync(x => x.LastRetryOn == null, stoppingToken)
?? await _databaseContext.Events.OrderBy(x => x.LastRetryOn).FirstOrDefaultAsync(stoppingToken);
if (eventData is null)
{
continue;
}
Type payloadType = Type.GetType(eventData.PayloadType)!;
JsonSerializer.Deserialize(eventData.Payload, payloadType);
try
{
await _eventQueuePublisher.PublishAsync(eventData.Payload, payloadType);
_databaseContext.Events.Remove(eventData);
}
catch
{
eventData.LastRetryOn = DateTimeOffset.UtcNow;
eventData.RetryCount++;
}
finally
{
await _databaseContext.SaveChangesAsync(stoppingToken);
}
}
}
}

View File

@@ -0,0 +1,8 @@
using Microsoft.EntityFrameworkCore;
namespace TimetableDesigner.Backend.Events.OutboxPattern;
public interface IEventOutboxDbContext
{
DbSet<Event> Events { get; }
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version />
<Title>TimetableDesigner.Backend.Events.OutboxPattern</Title>
<Authors>Mateusz Skoczek</Authors>
<PackageProjectUrl>https://github.com/TimetableDesigner</PackageProjectUrl>
<PackageIcon>icon.png</PackageIcon>
<RepositoryUrl>https://github.com/TimetableDesigner/TimetableDesigner.Backend.Events.OutboxPattern</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageReference Include="TimetableDesigner.Backend.Events" Version="1.1.4" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB