Skip to content

Instantly share code, notes, and snippets.

@AlexZeitler
Forked from flew2bits/FanOutSample.cs
Created April 12, 2024 14:41
Show Gist options
  • Select an option

  • Save AlexZeitler/e2c7a710c57f10e20c3433e43af638d7 to your computer and use it in GitHub Desktop.

Select an option

Save AlexZeitler/e2c7a710c57f10e20c3433e43af638d7 to your computer and use it in GitHub Desktop.

Revisions

  1. @flew2bits flew2bits created this gist Apr 12, 2024.
    82 changes: 82 additions & 0 deletions FanOutSample.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,82 @@
    using Marten;
    using Marten.Events.Projections;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;

    var builder = Host.CreateApplicationBuilder();
    builder.Services.AddMarten(opt => {
    opt.Connection(builder.Configuration.GetConnectionString("Marten") ?? "Host=localhost; Port=5433; User Id=postgres; Password=pgsql");
    opt.Projections.Add<WorkByDayProjection>(ProjectionLifecycle.Inline);
    }).UseLightweightSessions();

    builder.Services.AddHostedService<Runner>();

    var app = builder.Build();

    await app.RunAsync();

    public class Runner : BackgroundService
    {
    private readonly IDocumentSession _session;
    private readonly IHostApplicationLifetime _lifetime;

    public Runner(IDocumentSession session, IHostApplicationLifetime lifetime)
    {
    _session = session;
    _lifetime = lifetime;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    => Task.Run(async () =>
    {

    var assignmentId = Guid.NewGuid();
    var workerId = Guid.NewGuid();

    var today = DateOnly.FromDateTime(DateTime.Today);
    var tomorrow = today.AddDays(1);
    var oneWeekFromToday = today.AddDays(7);

    Console.Write($"{today} {oneWeekFromToday}");

    _session.Events.Append(assignmentId, new WorkerAssigned(assignmentId, workerId, today, oneWeekFromToday));
    _session.Events.Append(assignmentId, new WorkCompleted(assignmentId, today, "Work completed today"));
    _session.Events.Append(assignmentId, new WorkCompleted(assignmentId, today, "More work completed today"));
    _session.Events.Append(assignmentId, new WorkCompleted(assignmentId, tomorrow, "Work completed tomorrow"));

    await _session.SaveChangesAsync(stoppingToken);

    _lifetime.StopApplication();
    }, stoppingToken);
    }


    public record WorkerAssigned(Guid AssignmentId, Guid WorkerId, DateOnly Start, DateOnly End);

    public record WorkCompleted(Guid AssignmentId, DateOnly Date, string DescriptionOfWork);


    public record WorkByDay(string Id, Guid AssignmentId, Guid WorkerId, DateOnly Date, string[] WorkCompleted);

    public record WorkerAssignedForDay(Guid AssignmentId, Guid WorkerId, DateOnly Date);

    public class WorkByDayProjection : MultiStreamProjection<WorkByDay, string>
    {
    public WorkByDayProjection()
    {
    FanOut<WorkerAssigned, WorkerAssignedForDay>(e => Enumerable.Range(0, e.End.DayNumber - e.Start.DayNumber + 1)
    .Select(d => new WorkerAssignedForDay(e.AssignmentId, e.WorkerId, e.Start.AddDays(d))), FanoutMode.BeforeGrouping);
    Identity<WorkerAssignedForDay>(e => IdForEvent(e.AssignmentId, e.Date));
    Identity<WorkCompleted>(e => IdForEvent(e.AssignmentId, e.Date));
    IncludeType<WorkerAssigned>();
    }

    private static string IdForEvent(Guid assignmentId, DateOnly date) => $"{assignmentId:N}:{date:yyyyMMdd}";

    public static WorkByDay Create(WorkerAssignedForDay evt) => new(IdForEvent(evt.AssignmentId, evt.Date),
    evt.AssignmentId, evt.WorkerId, evt.Date, Array.Empty<string>());

    public static WorkByDay Apply(WorkCompleted evt, WorkByDay view) =>
    view with { WorkCompleted = view.WorkCompleted.Append(evt.DescriptionOfWork).ToArray() };
    }