1
0
mirror of synced 2024-11-24 19:53:10 +01:00
Tapeti/Examples/ExampleLib/ExampleConsoleApp.cs
Mark van Renswoude bcdb376256 Fixed queue arguments error due to wrong value types
Added test for publish overflows
Removed support for Unity Container
Changed third party package references to ranges
Fixed XML documentation
2022-11-22 13:20:47 +01:00

117 lines
3.8 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
using Tapeti;
namespace ExampleLib
{
/// <summary>
/// Callback method for ExampleConsoleApp.Run
/// </summary>
/// <param name="dependencyResolver">A reference to the dependency resolver passed to the ExampleConsoleApp</param>
/// <param name="waitForDone">Await this function to wait for the Done signal</param>
public delegate Task AsyncFunc(IDependencyResolver dependencyResolver, Func<Task> waitForDone);
/// <summary>
/// Since the examples do not run as a service, we need to know when the example has run
/// to completion. This helper injects IExampleState into the container which
/// can be used to signal that it has finished. It also provides the Wait
/// method to wait for this signal.
/// </summary>
public class ExampleConsoleApp
{
private readonly IDependencyContainer dependencyResolver;
private readonly int expectedDoneCount;
private int doneCount;
private readonly TaskCompletionSource<bool> doneSignal = new();
/// <param name="dependencyResolver">Uses Tapeti's IDependencyContainer interface so you can easily switch an example to your favourite IoC container</param>
/// <param name="expectedDoneCount"></param>
public ExampleConsoleApp(IDependencyContainer dependencyResolver, int expectedDoneCount = 1)
{
this.dependencyResolver = dependencyResolver;
this.expectedDoneCount = expectedDoneCount;
dependencyResolver.RegisterDefault<IExampleState>(() => new ExampleState(this));
}
/// <summary>
/// Runs the specified async method and waits for completion. Handles exceptions and waiting
/// for user input when the example application finishes.
/// </summary>
/// <param name="asyncFunc"></param>
public void Run(AsyncFunc asyncFunc)
{
try
{
asyncFunc(dependencyResolver, WaitAsync).Wait();
}
catch (Exception e)
{
Console.WriteLine(UnwrapException(e));
}
finally
{
Console.WriteLine("Press any Enter key to continue...");
Console.ReadLine();
}
}
/// <summary>
/// Returns a Task which completed when IExampleState.Done is called
/// </summary>
public async Task WaitAsync()
{
await doneSignal.Task;
// This is a hack, because the signal is often given in a message handler before the message can be
// acknowledged, causing it to be put back on the queue because the connection is closed.
// This short delay allows consumers to finish. This is not an issue in a proper service application.
await Task.Delay(500);
}
internal Exception UnwrapException(Exception e)
{
while (true)
{
if (e is not AggregateException aggregateException)
return e;
if (aggregateException.InnerExceptions.Count != 1)
return e;
e = aggregateException.InnerExceptions[0];
}
}
internal void Done()
{
if (Interlocked.Increment(ref doneCount) == expectedDoneCount)
doneSignal.TrySetResult(true);
}
private class ExampleState : IExampleState
{
private readonly ExampleConsoleApp owner;
public ExampleState(ExampleConsoleApp owner)
{
this.owner = owner;
}
public void Done()
{
owner.Done();
}
}
}
}