DeeDee by joh-pot
Nuget / site data
Details
Info
Name: DeeDee
Mediator pattern using source generation for .NET
Author: joh-pot
NuGet: https://www.nuget.org/packages/DeeDee/
You can find more details at https://github.com/joh-pot/DeeDee/
Source : https://github.com/joh-pot/DeeDee/
Original Readme
DeeDee
Mediator using source generation for .NET
Send in-process commands/queries to handlers either sync or async. The mechanism for sending is generated during compile time as overloads based on your code.
Installation
Nuget Package manager>Install-Package DeeDee
See wiki for full details
About
Mediatr generated data
How to use
Example ( source csproj, source files )
- CSharp Project
- Program.cs
- PingPong.cs
- GenericLoggerHandler.cs
This is the CSharp Project that references DeeDee
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DeeDee" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
</ItemGroup>
</Project>
This is the use of DeeDee in Program.cs
Console.WriteLine("Hello, World!");
ServiceCollection services = new ();
DeeDeeDemo.DeeDee.Generated.IocExtensions.AddDispatcher(services);
services.AddSingleton(typeof(IPipelineAction<Ping, Pong>), typeof(GenericLoggerHandler)); // This will run 1st
var serviceProvider = services.BuildServiceProvider();
var mediator = serviceProvider.GetRequiredService<DeeDeeDemo.DeeDee.Generated.Models.IDispatcher>();
var id = Guid.NewGuid();
var request = new Ping(id);
var response = mediator.Send(request);
Console.WriteLine("-----------------------------------");
Console.WriteLine("ID: " + id);
Console.WriteLine(request);
Console.WriteLine(response);
This is the use of DeeDee in PingPong.cs
public sealed record Ping(Guid Id) : IRequest<Pong>;
public sealed record Pong(Guid Id);
public sealed class PingHandler : IPipelineAction<Ping, Pong>
{
public Pong Invoke(Ping request, ref PipelineContext<Pong> context, Next<Pong> next)
{
Console.WriteLine("4) Returning pong!");
return new Pong(request.Id);
}
}
This is the use of DeeDee in GenericLoggerHandler.cs
public sealed class GenericLoggerHandler : IPipelineAction<Ping, Pong>
{
public Pong Invoke(Ping request, ref PipelineContext<Pong> context, Next<Pong> next)
{
Console.WriteLine("1) Running logger handler");
try
{
var response = next(request , ref context);
Console.WriteLine("5) No error!");
return response;
}
catch (Exception ex)
{
Console.WriteLine("error:" + ex.Message);
throw;
}
}
}
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- Dispatcher.cs
- IDispatcher.cs
- IocExtensions.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using DeeDee.Models;
using ServiceProvider = DeeDee.Models.ServiceProvider;
namespace DeeDeeDemo.DeeDee.Generated.Models
{
public class Dispatcher : IDispatcher
{
private readonly ServiceProvider _serviceFactory;
private readonly Lazy<Next<Pong>> _Ping_Pong_lazy;
public Dispatcher(ServiceProvider service)
{
_serviceFactory = service;
_Ping_Pong_lazy = new Lazy<Next<Pong>>(Build<Ping, Pong>);
}
public Pong Send(Ping request)
{
var context = new PipelineContext<Pong>();
Next<Pong> builtPipeline = _Ping_Pong_lazy.Value;
return builtPipeline(request, ref context);
}
private NextAsync BuildAsync<TRequest>()
where TRequest : IRequest
{
{
var actions = _serviceFactory.GetServices<IPipelineActionAsync<TRequest>>();
var builtPipeline = actions.Aggregate((NextAsync)((req, ctx, tkn) => Task.CompletedTask), (next, pipeline) => (req, ctx, tkn) => pipeline.InvokeAsync((TRequest)req, ctx, next, tkn));
return builtPipeline;
}
}
private Next Build<TRequest>()
where TRequest : IRequest
{
{
var actions = _serviceFactory.GetServices<IPipelineAction<TRequest>>();
var builtPipeline = actions.Aggregate((Next)((IRequest req, ref PipelineContext ctx) =>
{
{
}
}), (next, pipeline) => (IRequest req, ref PipelineContext ctx) => pipeline.Invoke((TRequest)req, ref ctx, next));
return builtPipeline;
}
}
private NextAsync<TResponse> BuildAsync<TRequest, TResponse>()
where TRequest : IRequest<TResponse>
{
{
var actions = _serviceFactory.GetServices<IPipelineActionAsync<TRequest, TResponse>>();
var builtPipeline = actions.Aggregate((NextAsync<TResponse>)((req, ctx, tkn) => Task.FromResult(ctx.Result)), (next, pipeline) => (req, ctx, tkn) => pipeline.InvokeAsync((TRequest)req, ctx, next, tkn));
return builtPipeline;
}
}
private Next<TResponse> Build<TRequest, TResponse>()
where TRequest : IRequest<TResponse>
{
{
var actions = _serviceFactory.GetServices<IPipelineAction<TRequest, TResponse>>();
var builtPipeline = actions.Aggregate((Next<TResponse>)((IRequest<TResponse> req, ref PipelineContext<TResponse> ctx) => ctx.Result), (next, pipeline) => (IRequest<TResponse> req, ref PipelineContext<TResponse> ctx) => pipeline.Invoke((TRequest)req, ref ctx, next));
return builtPipeline;
}
}
}
}
using System;
using System.Threading;
using System.Threading.Tasks;
using DeeDee.Models;
namespace DeeDeeDemo.DeeDee.Generated.Models
{
public interface IDispatcher
{
public Pong Send(Ping request);
}
}
using System;
using System.Linq;
using System.Reflection;
using DeeDee.Models;
using DeeDeeDemo.DeeDee.Generated.Models;
using Microsoft.Extensions.DependencyInjection;
using ServiceProvider = DeeDee.Models.ServiceProvider;
namespace DeeDeeDemo.DeeDee.Generated
{
internal static class IocExtensions
{
public static IServiceCollection AddDispatcher(this IServiceCollection services, Lifetime lifetime = Lifetime.Singleton)
{
switch(lifetime)
{
case Lifetime.Singleton:
services.AddSingleton<IDispatcher, Dispatcher>();
services.AddSingleton<ServiceProvider>(ctx => ctx.GetRequiredService);
break;
case Lifetime.Scoped:
services.AddScoped<IDispatcher, Dispatcher>();
services.AddScoped<ServiceProvider>(ctx => ctx.GetRequiredService);
break;
case Lifetime.Transient:
services.AddTransient<IDispatcher, Dispatcher>();
services.AddTransient<ServiceProvider>(ctx => ctx.GetRequiredService);
break;
}
RegisterPipelineActions(services);
return services;
}
private static void RegisterPipelineActions(IServiceCollection services)
{
var pipelineTypes = AppDomain
.CurrentDomain
.GetAssemblies()
.SelectMany
(
a => a.GetTypes().Where
(
x => !x.IsInterface &&
!x.IsAbstract &&
x.GetInterfaces()
.Any
(
y => y.Name.Equals(typeof(IPipelineActionAsync<,>).Name,
StringComparison.InvariantCulture) ||
y.Name.Equals(typeof(IPipelineActionAsync<>).Name,
StringComparison.InvariantCulture) ||
y.Name.Equals(typeof(IPipelineAction<>).Name,
StringComparison.InvariantCulture) ||
y.Name.Equals(typeof(IPipelineAction<,>).Name, StringComparison.InvariantCulture)
)
).GroupBy(type => type.GetInterfaces()[0]).SelectMany(g => g.OrderByDescending(s => s.GetCustomAttribute<StepAttribute>()?.Order))
);
foreach (var type in pipelineTypes)
{
foreach (var implementedInterface in type.GetInterfaces())
{
var bindAs = type.GetCustomAttribute<BindAsAttribute>();
switch (bindAs?.Lifetime)
{
case Lifetime.Singleton:
services.AddSingleton(implementedInterface, type);
break;
case Lifetime.Scoped:
services.AddScoped(implementedInterface, type);
break;
case Lifetime.Transient:
services.AddTransient(implementedInterface, type);
break;
default:
services.AddSingleton(implementedInterface, type);
break;
}
}
}
}
}
}
Usefull
Download Example (.NET C# )
Share DeeDee
https://ignatandrei.github.io/RSCG_Examples/v2/docs/DeeDee