added domain events, fix IQueryalbeProvider abstraction, added support for sagas and RabbitMQ

This commit is contained in:
Mathias Beaulieu-Duncan
2025-12-20 15:13:05 -05:00
parent 4051800934
commit 9b9e2cbdbe
57 changed files with 4951 additions and 91 deletions
@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
namespace Svrnty.CQRS.DynamicQuery;
/// <summary>
/// Builder for configuring DynamicQuery services.
/// </summary>
public class DynamicQueryServicesBuilder
{
/// <summary>
/// The service collection being configured.
/// </summary>
public IServiceCollection Services { get; }
internal DynamicQueryServicesBuilder(IServiceCollection services)
{
Services = services;
}
}
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using PoweredSoft.Data.Core;
namespace Svrnty.CQRS.DynamicQuery;
/// <summary>
/// In-memory implementation of IAsyncQueryableService.
/// For EF Core projects, use AddDynamicQueryServices().UseEntityFramework() instead.
/// </summary>
public class InMemoryAsyncQueryableService : IAsyncQueryableService
{
public IEnumerable<IAsyncQueryableHandlerService> Handlers { get; } = Array.Empty<IAsyncQueryableHandlerService>();
public Task<List<TSource>> ToListAsync<TSource>(IQueryable<TSource> queryable, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.ToList());
}
public Task<TSource?> FirstOrDefaultAsync<TSource>(IQueryable<TSource> queryable, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.FirstOrDefault());
}
public Task<TSource?> FirstOrDefaultAsync<TSource>(IQueryable<TSource> queryable, Expression<Func<TSource, bool>> predicate, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.FirstOrDefault(predicate));
}
public Task<TSource?> LastOrDefaultAsync<TSource>(IQueryable<TSource> queryable, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.LastOrDefault());
}
public Task<TSource?> LastOrDefaultAsync<TSource>(IQueryable<TSource> queryable, Expression<Func<TSource, bool>> predicate, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.LastOrDefault(predicate));
}
public Task<bool> AnyAsync<TSource>(IQueryable<TSource> queryable, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.Any());
}
public Task<bool> AnyAsync<TSource>(IQueryable<TSource> queryable, Expression<Func<TSource, bool>> predicate, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.Any(predicate));
}
public Task<bool> AllAsync<TSource>(IQueryable<TSource> queryable, Expression<Func<TSource, bool>> predicate, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.All(predicate));
}
public Task<int> CountAsync<TSource>(IQueryable<TSource> queryable, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.Count());
}
public Task<long> LongCountAsync<TSource>(IQueryable<TSource> queryable, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.LongCount());
}
public Task<TSource?> SingleOrDefaultAsync<TSource>(IQueryable<TSource> queryable, Expression<Func<TSource, bool>> predicate, CancellationToken cancellationToken = default)
{
return Task.FromResult(queryable.SingleOrDefault(predicate));
}
public IAsyncQueryableHandlerService? GetAsyncQueryableHandler<TSource>(IQueryable<TSource> queryable)
{
return null;
}
}
@@ -1,16 +1,31 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using PoweredSoft.Data.Core;
using PoweredSoft.DynamicQuery;
using PoweredSoft.DynamicQuery.Core;
using Svrnty.CQRS.Abstractions;
using Svrnty.CQRS.Abstractions.Discovery;
using Svrnty.CQRS.DynamicQuery.Abstractions;
using Svrnty.CQRS.DynamicQuery.Discover;
using PoweredSoft.DynamicQuery.Core;
namespace Svrnty.CQRS.DynamicQuery;
public static class ServiceCollectionExtensions
{
/// <summary>
/// Registers core DynamicQuery services with in-memory async queryable.
/// For EF Core projects, chain with .UseEntityFramework().
/// </summary>
/// <param name="services">The service collection.</param>
/// <returns>A builder for further configuration.</returns>
public static DynamicQueryServicesBuilder AddDynamicQueryServices(this IServiceCollection services)
{
services.TryAddTransient<IAsyncQueryableService, InMemoryAsyncQueryableService>();
services.TryAddTransient<IQueryHandlerAsync, QueryHandlerAsync>();
return new DynamicQueryServicesBuilder(services);
}
public static IServiceCollection AddDynamicQuery<TSourceAndDestination>(this IServiceCollection services, string name = null)
where TSourceAndDestination : class
=> AddDynamicQuery<TSourceAndDestination, TSourceAndDestination>(services, name: name);
@@ -55,6 +70,22 @@ public static class ServiceCollectionExtensions
return services;
}
/// <summary>
/// Registers a custom queryable provider override for the specified source type.
/// Use this for DTOs that require custom projection from entities.
/// </summary>
/// <typeparam name="TSource">The DTO/Item type returned by the queryable.</typeparam>
/// <typeparam name="TProvider">The provider implementation type.</typeparam>
/// <param name="services">The service collection.</param>
/// <returns>The service collection for chaining.</returns>
public static IServiceCollection AddQueryableProviderOverride<TSource, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TProvider>(this IServiceCollection services)
where TSource : class
where TProvider : class, IQueryableProviderOverride<TSource>
{
services.AddTransient<IQueryableProvider<TSource>, TProvider>();
return services;
}
public static IServiceCollection AddDynamicQueryWithParams<TSourceAndDestination, TParams>(this IServiceCollection services, string name = null)
where TSourceAndDestination : class
where TParams : class